Bug 排查策略及优先级

  1. 收集触发 Bug 之前都做了什么操作,Bug 的表现,其他有助于 Bug 分析的内容,如日志、Dmp文件等。
  2. 在大脑里快速过一遍与 Bug 有关的流程,猜测可能出问题的地方。不要深入的分析流程,这不是这个优先级下该做的,因为程序是我们用心设计的,Bug 是我们用心后还是没能预料到的事,再去深入分析极大的概率是无法找出问题的。
  3. 重现 Bug。对于不好重现又不出异常的 Bug,注意收集规律,再去创造满足规律的条件。
  4. 有 Dmp 文件的,用 Visual Studio 直接打开 Dmp 看当时出异常时的情况。
  5. 没有 Dmp 文件的,Visual Studio 中 Debug 运行程序,触发 Bug。如果没有直接触发自动断点,则看 输出 窗口中抛异常、触发 Windows 异常或触发断言;这些都可以在 Visual Studio 中通过设置,在触发时自动断点。
  6. 如果能知道触发 Bug 的问题代码,常规调,不多说。
  7. 如果无法确定问题代码,只能深入分析代码流程了。
  8. 自己闷头分析不出,找个人讲一遍,一起分析分析。
  9. 如果还是不行,那就只能大段大段删代码来试了。

Visual Studio 的一些好用的功能

Attach & Detach

Debug → Attach to Process… 可以对已经运行了,但没挂调试的程序进行调试。

Debug → Detach All 可以对正在调试的程序进行脱离,这并不会终止该程序。

调用堆栈(Call Stack)

调用堆栈可以查看当前所在代码是一层层从哪里调用过来的。

双击某一行,可以切换到当时调用该函数时的情况,可以看当时有关变量的值。

如果调用了第三方库,又没有对应的 pdb 文件,会导致调用堆栈中无法看到调用哪些函数;可以通过 Tools → Option… → Debugging 勾选“Load dll exports (Native only)”来显示那些导出过的函数,它内部的函数还是没法看的。

断点(Breakpoint)

我们常用的是在具体的代码行上按 F9 设置断点。

另外,在“New”那里两个好用的断点类型:

  1. 函数断点;可以直接通过函数名设置断点,不管有没有对应的代码。当然没有代码的 DLL 内的函数,必须是导出的函数,内部函数时不行的。举个例子,如果程序不知道在哪里打开不该打开的文件,那可以设置 CreateFileW 函数断点,当有文件打开就会触发断点。
  2. 数据断点;我们有时候会遇到一个成员变量不知道在哪里被修改了,将该成员变量设置成数据断点,当被修改时就会触发断点,就可以知道在哪里被修改。

对于断点,我们还可以附加判断条件,只有条件成立时才触发。在“Breakpoints”窗口中,断点上 右键 → Settings… 勾选“Conditions”;在代码中,在断点上 右键 → Conditions…。

异常设置(Exception Settings)

有时会遇到程序抛了不该抛的 C++ 异常,而抛的点太多,或根本不在自己代码内,难以设置断点;就可以通过异常设置添加对应的 C++ 异常,当抛该异常时,会自动触发断点。

有时会遇到内存访问错误、浮点数运算溢出等 Windows 异常,也可以通过勾选相应的设置来自动触发断点。

线程(Threads)

Debug → Windows → Threads 可以打开线程查看窗口。

当我们处理多线程代码时,会遇到线程死锁的问题,难以知道哪个线程在哪里锁了。可以用 Break All 功能将整个程序暂停下来,再在线程窗口中看有哪些线程,通过双击,可以切换到该线程,看正在处理什么代码。通过调用堆栈就可以看出哪个线程处于等待状态。

监视(Watch)

在常看变量的时候,经常会遇到 Visual Studio 无法自动确定大小的数组,导致只显示数组内的第一个,而无法看到后续。可以通过“变量名,数组大小”的形式让 Visual Studio 显示成有大小的数组,例如,“name,20”。

其他一些用法看 C++ 中的格式说明符

多进程调试

使用Microsoft Child Process Debugging Power Tool 插件,简单设置后可以非常方便地调试多进程工程

安装和配置插件

请先安装 Microsoft Child Process Debugging Power Tool 插件。

安装插件后启动 Visual Studio,可以在 Debug -> Other Debugging Targets 中找到 Child Process Debugging Settings。

然后你可以按照下图的设置开启此项目的子进程调试:

现在,你只需要开始调试你的程序,那么你程序中启动的新的子进程都将可以自动加入调试。

发表评论

电子邮件地址不会被公开。 必填项已用*标注