Visual Studio C++调试技巧和窍门

[原文发表地址] Debugging Tips and Tricks for C++ in Visual Studio

[原文发表时间]2016/7/11

当我们第一次编写软件时,我们的目标是能够创建能正常运行的应用程序。不幸的是,它很少是这样的,我们要花大量的时间和精力去调试代码中的问题,而不是添加新的用途。幸运的是,当问题出现需要调查的时候,Visual Studio这个具有一流调试经验的工具能够帮助我们快速找到问题。然而,由于我们不知道或者只是忘了它提供快速解决问题的功能,致使我们很多人不能充分利用调试器。

在本贴中我将通过一个我最近展示的C++调试技巧和窍门的视频来简要介绍调试功能,这当然不包括一切,如果你想要更深入了解的话,每个功能都有相关的更加详细的信息。此外,每个在视频中展示的窍门都会包含一个精确的直接链接。

完成下列22条技巧后,让我们知道哪些是你新知道的,有没有人都知道这些?哪一个是你最喜欢的?

最后,请你在继续之前请花一分钟注册来帮助我们继续提升debugger以便更好的满足你的需要

提示和技巧 从项目属性配置启动选项(1:25):开发软件是添加配置选项去改变行为或基于其他命令或环境变量去测试可选的设计是一种常见的模式,你能配置相同的命令参数并且为目标应用程序的”调试”选项卡上的项目属性页设置环境变量(在解决方案管理器中的项目上单击鼠标右键并且选择从菜单中选择”属性”)。

函数返回值(2:34): 当代码逐句通过调试器中,在自动窗口上显示你在上一行代码调用的所有参数的返回值。

设置下一个运行位置(3:16): 让你能够更改应用程序将要执行的下一个指令。这对返回到和重新调试已经执行过的语句是很好的。或者对强制应用程序下的不同代码的路径的测试功能很有用(例如,你甚至可以在条件计算结果为false条件块内移动执行)。你能通过单击和拖动左边边框黄色指令指针到所期待的行来改变下一个运行位置,或右键单击所期待的下一个运行行,然后从快捷菜单中选择“设置下一条语句”,又或者通过键盘快捷方式Ctrl + Shift + F10 来设置下一个运行位置的光标。需要指出的是这只是改变下一个指令运行的位置,如果你使它向前移动,它不会撤销任何以前已经运行的结果,如果你设置的是移动运行后面的代码,例如,如果设置了下一条运行语句而跳过一个变量的初始化,那么由于变量的继续初始化将导致应用程序的崩溃。

 单步运行(4:07):当同一行有多个函数同时被调用时,不需要操作定义或者设置断点就允许你能够直接进入一个函数,然后继续执行命中的断点。单步运行可以通过右键单击当前行从编辑器的快捷菜单中访问,或者使用快捷键Shift + Alt + F11。

 运行到光标处(5:05): 一次性使用断点功能是调试运行到该行并在停在该行。运行到光标处可以从编辑器上的上下文菜单中设置,也可以使用快捷键Ctrl +F10。另一个关于运行到光标处的有用的技巧是即使在设计时(当你在不调试),键盘快捷方式Ctrl + F10 将开始调试并运行该应用程序的那一行代码,就像你设置断点并且按了F5。

编辑并继续(6:57): 这是一个在调试代码时能够节省大量时间的方法,如果你注意到一个简单的错误并且你可以纠正它,你不需要停止调试和编译去运行应用程序返回到该位置。在Visual Studio 2015中我们添加了支持编辑和能够继续到包括基于支持x64的默认C++调试器。

异常设置(8:31):当指定的异常被引发时,我们可以设置调试器去停止,即使这个异常随后在应用程序中可能被捕获。这对于应用程序在处理异常而你需要调试为什么异常会发生时是非常有用的。

条件表达式,命中次数,断点过滤 (13:44):只有当应用程序中的某些条件得到满足(比如,当一个变量与一个固定的字符串匹配) 时,条件表达式会告诉调试器停止调试。中断的次数取决于断点击中的次数,当程序在断点处满足等于某个值,等于或者大于某个值,又或者是某个值的倍数时都会发生中断。如果你需要采用某种抽样的方法去处理问题(比如:只显示每100次的值),那么这种调试将会非常有用。过滤条件是为并行线程的情况所设计的,你可以在一个特定的线程、进程或者机器上终止执行。

固定数据提示(19:17):当在一个反复的代码路径里工作时(比如:循环),你总是集中在一个单一的值上。通过在编辑器上将鼠标悬停来查看变量是一种很好的方法,但是当你移开鼠标时数据提示就会消失。通过点击数据提示右侧的“图钉图标”可以使数据固定在编辑器上变量所在那行源代码处,直到你关闭它为止。

并行堆栈窗口 (19:42):用一个简明的窗口显示在这个进程中所有线程的调用堆栈,这个并行堆栈窗口通过调试->窗口打开。

显示外部代码(20:30):Visual Studio默认启动的功能我们称之为仅我的代码,它可以帮助你专注于你的代码, 你可以通过控制在调用堆栈窗口有折叠操作系统和运行帧成为一行单独的标签为[External Code]的帧,如果你想或者需要看到完整的堆栈,你可以右键单击调用堆栈或并行堆栈窗口以显示完整的调用堆栈。如果你希望这种方式的调试总是关闭的,你可以将调试->选项下的“仅我的代码”永久禁用。

并行监视窗口(22:00): 显示某一个变量在当前函数中所有所执行的线程的值。它也会显示在循环方法中相同变量在所有堆栈帧的值。

冻结和解冻线程(23:13):能够让你更加精细的控制单独的线程。如果一个特定的线程是有问题的,但是你又不希望当你调试单个线程时其他的线程还正在运行你可以冻结你不希望运行的线程。

标记线程将标记线程运行到光标处(24:18):标记线程能够让你为线程创建群组,就像运行到光标处一样让你可以将应用程序运行到代码的指定行,运行标记线程到光标处能够让你将整个线程组移动到指定行。当你需要控制特定线程的位置时,这个方法是非常有用的。(比如:确定它们没有一个被锁住然后冻结它们)

在源中显示线程(26:13):是一个能够让你看到在断点空白处随着一个小符号执行的线程的设置。要打开它,点击调试工具栏上的“在源中显示线程”按钮(如下显示)。

在以并行方式运行代码时这个功能是特别强大的,但是也伴随着小的性能损耗,所以当你不用它的时候不要让它无限期的停留。

调试工具栏(28:09): 是一个你可以进入当前调用堆栈并且能够在其他功能中的线程之间快速转换的工具栏。如果你使用的是“常规设置”配置文件,那么在默认情况下它是可见的,否则的话你就需要通过“视图”à“工具栏”手动来启用它。

在页堆中调试堆损坏(29:43):堆损坏应该是bug中最难解决的类型之一,因为这种逻辑错误引起的破坏经常在应用程序崩溃或者有奇怪表现之前就出现了,这使得它很难诊断问题出现的位置。页堆是Windows中的一项功能,如果应用程序往不属于它的内存中读或者写数据时导致堆损坏,调试器会停在引起堆损坏的地方,页堆让操作系统抛出异常。。启用页堆目前需要将gflags作为Windows SDK的一部分内置到Windows的调试工具中。如果你已经安装了Windows调试工具,打开管理员命令提示符执行gflags /p /enable [executable name] 语句去启用页堆,然后像平时一样在在Visual Studio中调试程序。请注意,页堆导致Windows做更多的内存跟踪,所以当它启动的时候会降低应用程序的运行速度,所以当不调试堆损坏时时,不要一直留着它。

PerfTips(33:32):显示代码在编译器中从执行到停止的时间(包括单步运行和在断点之间运行的时间)。

集成CPU性能分析器(35:53):当我们写代码时,我们总是尽所能的让它能够正确运行,但是很难或者不太可能花时间去对代码进行单独的性能分析。为了提高在性能中快速查找主要问题的便利性,我们在调试器里融入了CPU性能分析器,它也可以通过诊断工具窗口访问。

集成储存器分析(38:01): 往底层追踪内存漏洞很难,为了让它尽可能的简单,我们设计融入了内存分析工具去帮助我们通过诊断工具窗口找到并且直接在调试器中修复漏洞。把它集成到调试器中,当你扫视的时候你可以更加精确的控制,意味着你可以看到通过单个函数调用或者单行代码时的内存变化。

Natvis(41:03): 让你可以自定义Visual Studio在调试器中显示本地文件的类型。在Visual Studio中我们增加了支持增加本地文件的项目,只要它们被更新了只要它们被更新呢么就会在调试器中生效,无需再重新调试。

显示那些源代码,导致访问冲突(42:43): 一行代码经常依附多个指针,它的结果是搜寻触发访问冲突的指针。在Visual Studio2015Update1中我们在调试器中增加了逻辑分析能够告诉你触发访问冲突的准确的源表达式。

结论

我希望这次概述的对你Visual Studio 的调试是有帮助的,如果你有任何的意见或者问题,你可以写在下面的评论部分里,或者通过Twitter让我们知道。最后,我们一直在寻找能够帮助我们改进Visual Studio debugger的人。