8.1 初级调试技巧

8.1.1 基础操作

首先介绍的是最基础的调试操作,已经具备了调试基础的读者可以直接跳过本节。在debug模式下,可以单击工具栏中的“调试”按钮启动调试,设置断点,并使用相应的调试快捷键进行调试。如表8-1介绍了Visual Studio和Xcode这两个IDE最基础的调试快捷键(Xcode一般在笔记本键盘下才需要按Fn),通过断点以及逐语句和逐方法的调试,可以观察到程序运行的流程,结合对变量的监视,可以分析出程序执行错误流程的原因。

表8-1 调试快捷键

设置断点的方式是在代码界面左侧的断点栏单击或按断点快捷键,Visual Studio为F9,在断点栏上会出现红色的圆形断点,如图8-1所示。Xcode为cmd+\,在断点栏上会出现蓝色的书签形断点,如图8-2所示。

图8-1 Visual Studio断点调试

图8-2 Xcode断点调试

调试时程序运行到断点处会停下,程序当前执行到的代码行对应的断点栏会有一个小箭头标识,可以通过拖曳这个小箭头强制改变程序的运行流程,例如,希望再次执行一遍,可以将小箭头往回拖,如果希望跳过某些代码,也可以直接拖曳小箭头跳过那些代码。这个操作最好不要跳过当前正在执行的函数,如拖曳到另外一个函数中。

除了单步调试之外,还可以将鼠标指针悬停在当前堆栈中的变量上查看、修改当前堆栈中的变量,也可以是一些全局变量,修改后按Enter键即可生效,如图8-3和图8-4所示。

图8-3 Visual Studio修改变量

图8-4 Xcode修改变量

查看当前堆栈也是最常用的基础操作,因为虽然问题出现在这里,但是问题的根源可能是由于调用者传入了错误的参数,那么就需要通过堆栈来分析上层调用者的问题。Visual Studio可以通过调用堆栈窗口来查看当前的堆栈,双击堆栈上的函数可以跳转至对应的函数,并查看对应函数的执行情况以及相关变量等,如图8-5所示。

图8-5 调用堆栈

Xcode则需要通过左侧的调试导航栏来观察当前堆栈,Xcode的调试导航栏还可以观察到当前的内存、CPU、网络I/O和磁盘等资源的占用情况,如图8-6所示。

图8-6 Xcode的调试导航栏

8.1.2 启动调试

除了从调试器中启动调试之外,还有其他的一些开始调试的技巧。可以动态附加到进程中,这意味着当程序在不处于调试状态下发生错误或崩溃时,可以动态附加到进程中进行调试。Visual Studio可以通过选择“调试”→“附加到进程”命令,然后在弹出的对话框中选择要附加的进程。Xcode则是通过选择Debug→Attach to Process命令,再选择要附加的进程。

在Visual Studio下,还可以利用其多启动项目特性来同时调试多个项目,在开发多个协同工作的程序时可以用到,如同时调试客户端和服务端程序。在解决方案的属性页面中,可以选择要启动的项目,并指定哪些项目是调试启动,哪些是不调试启动,如图8-7所示。

图8-7 多启动项目

Xcode并不支持此特性,但可以通过打开多个IDE来同时调试多个项目。

此外Visual Studio还支持强大的远程调试功能,该功能在后面的高级技巧中会介绍。

8.1.3 条件断点

当断点被执行多次时,使用条件断点可以大大提高调试效率,例如,当在一个for循环中设置了一个断点,希望在for循环执行到100次的时候观察循环内部的变量,如果没有条件断点,那么就需要在这里中断100次。

Visual Studio可以在断点上右击,在弹出的快捷菜单中选择条件,然后在弹出的对话框中设置条件,可以设置表达式为条件,也可以设置当表达式的值被修改才命中该断点,如图8-8所示。

图8-8 设置断点条件

右键快捷菜单中的命中次数允许设置指定的次数,当断点的命中次数达到设定的条件时才命中该断点,对话框中的“重置”按钮可以重置当前的命中次数为0,如图8-9所示。

图8-9 命中次数

Xcode可以在断点上右击,在弹出的快捷菜单中选择Edit Breakpoint,在弹出的界面中,可以在Condition中设置条件表达式,在Ignore中设置命中次数,如图8-10所示。

图8-10 Xcode的断点编辑菜单

8.1.4 监视技巧

通过监视窗口可以很方便地观察当前堆栈中变量的情况,在Visual Studio中,有4个监视窗口,通过选择“调试”→“窗口”→“监视”命令,或按Ctrl+Alt+W快捷键,再输入监视窗口的编号(1~4)可以切换它们。Visual Studio还提供了自动窗口来自动显示当前有效的变量信息,以及局部变量窗口来自动显示当前函数中的局部变量信息。在Xcode中则是整合为一个监视窗口,位于IDE的下方。

1.添加监视

在Visual Studio中可以在变量或变量的悬浮信息框上右击,在弹出的快捷菜单中选择“添加监视”命令,如图8-11所示,也可以直接在监视窗口中输入表达式。而在Xcode中只能在监视窗口输入表达式。

图8-11 添加监视

2.特殊监视

图8-12演示了如何输入表达式来添加监视,除了监视变量,还可以监视地址,例如,我们new了一个对象,即使当前函数中无法访问该对象,也可以监视这个对象,这对于观察指定对象的变化非常有用,通过监视,可以观察对象是否被修改了,如图8-12所示。

图8-12 监视地址

通过输入Director::getInstance()方法、director变量,以及地址强制转换为指针,都可以观察到指定的变量,如图8-13所示。只不过调用方法会有一些副作用,因为方法会被执行,这可能导致程序出现意料之外的问题,直接监视局部变量director则只会在该函数内生效,只有监视地址的方式才会一直生效。

图8-13 监视函数返回值

Xcode可以在监视窗口上右键选择Add Expression来添加要监视的表达式,如图8-14所示。

图8-14 Xcode添加监视

3.Visual Studio内置变量

除了可以输入普通的表达式以外,Visual Studio还提供了很多内置变量以供监视,如下所示。

❑ $tid:当前线程的线程ID。

❑ $pid:进程ID。

❑ $cmdline:启动程序的命令行字符串。

❑ $user:正在运行程序的用户。

❑ $err:显示最后一个错误的错误码。

❑ $err, hr:显示最后一个错误的错误信息。

更多的内置变量可以参考https://msdn.microsoft.com/en-us/library/ms164891.aspx