导言
你钟情什么样的调试器?如果你问我这个问题,我会回答是“Visual Studio + WinDbg”。我比较喜欢Visual Studio那朴实无华且易操作的接口,更喜欢它能迅速把我需要的信息以可视的形式展示出来。但遗憾的是,Visual Studio调试器无法获取某些信息。例如,假设我想知道哪个线程正在占用特殊的临界区?或者是哪个函数占用了大部分的栈空间?不用担心,有WinDbg呢。它的命令能回答这些问题,以及调试过程中出现的其它有趣的问题。甚至不退出Visual Studio,WinDbg就可以附上目标应用程序??谢谢WinDbg支持入侵模式的调试(本文后面会详细讨论),我们可以把Visual Studio GUI和WinDbg的命令行结合起来使用。
唯一的问题是WinDbg不太好用。需要花些时间适应它的用户界面,而掌握它的命令则要花更多的时间。但是假设你现在就需要它,马上用它调试紧急的问题?有什么快速简便的方法吗?当然。WinDbg的小弟CDB,功能和WinDbg差不多;因为它是基于命令行的,所以用起来更简单一些。在这篇文章里,我将把CDB作为Visual Studio调试器的补充,介绍怎样使用CDB。在这篇文章里,你将会看到怎样配置CDB,怎样用它解决实际的问题。另外,我还会提供一些批处理文件,它们可以隐藏CDB命令行接口的大部分复杂性,也让你少打几个字。
安装与配置
安装
当然,在使用CDB前,必须先安装并配置它。WinDbg和CDB是Debugging Tools for Windows 的一部分,可以从这里下载。安装很简单,你可以用默认设置安装,除非你准备用WinDbg SDK开发应用程序。(如果你准备用SDK,需要选择定制安装,并启用SDK安装;推荐你把它安装在不包含空格的目录名的目录中)。安装完成后,安装目录里将包含所有必需的文件,包括WinDbg(windbg.exe)和CDB(cdb.exe)。
调试工具也支持“xcopy”类型的安装。也就是说,在一台机器上安装后,如果你想在其它的机器上使用,不用再安装,直接把已经安装的目录直接拷过去就行了。
符号文件服务器路径
如果不能访问操作系统DLL的最新的符号文件,有些重要的WinDbg命令将不能正常工作。在以往,我们可以从微软的FTP服务器上下载巨大的符号文件包,然后从中找出需要的符号文件。这非常浪费时间,而且在操作系统更新或升级后,符号文件就过时了(因此也就变得毫无用处)。幸运的是,现在有更简便的方法来获得符号文件??符号文件服务器。WinDbg和Visual Studio都支持这个方法,在需要时直接从微软维护的服务上下载最新的符号文件。有了符号文件服务器,我们再也不用下载整个符号文件包了(那实在是太大了),因为调试器知道需要用到哪个DLLs,所以直接下载单个符号文件就行了。如果符号文件在操作系统更新或升级以后过时了,调试器会注意到这种情形,并再次下载必需的符号文件。
为了使符号文件服务器起作用,我们应该让调试器知道符号文件服务器的路径。最简单的方法是在_NT_SYMBOL_PATH环境变量里指定符号文件服务器的路径。可以用如下的路径:
"srv*c:\symbolcache*http://msdl.microsoft.com/download/symbols"
(c:\symbolcache目录将被用来保存从符号文件服务器下载下来的符号文件;当然,你可以用任何有效的本地或网络路径)。例如:
set _NT_SYMBOL_PATH=srv*c:\symbols*http://msdl.microsoft.com/download/symbols
在你设置_NT_SYMBOL_PATH环境变量之后,就可以使用符号文件服务器了。关于符号文件服务器的更多信息,相关设置,以及可能会用到的排除故障的小技巧,可以从WinDbg的文档中找到(Debuggers | Symbols section)。
如果你需要从一台需登录的代理服务器后访问符号文件服务器。参见本篇文章中CDB and proxy servers部分,以了解更多信息。
Microsoft (R) Windows Debugger Version 6.5.0003.7
Copyright (c) Microsoft Corporation. All rights reserved.
CommandLine: myapp.exe
Symbol search path is: *** Invalid ***
****************************************************************************
* Symbol loading may be unreliable without a symbol search path. *
* Use .symfix to have the debugger choose a symbol path. *
* After setting your symbol path, use .reload to refresh symbol locations. *
****************************************************************************
调试CPU高消耗的问题
对大多数软件来说,太高的CPU消耗率(根据任务管理器的显示,在单CPU上接近100%)明显指出软件中有bug。通常意味着应用程序的某个线程陷入了死循环。当然,调试这个问题的、最普通的方法是用Visual Studio调试器附上这个进程,查找哪个线程在捣乱。但是我们应该检查哪个线程呢?CDB为我们提供了简便的方法??!runaway命令。当不带参数使用时,这条命令显示应用程序每个线程执行用户模式代码时所花的时间(使用另外的参数,可以显示在内核模式下所花的时间,自线程启动后占用的时间等)。
如下是在CDB下使用这条命令的示例:
cdb -pv -pn myapp.exe -logo out.txt -c "!runaway;q"
下面是!runaway命令的输出示例:
0:000> !runaway
User Mode Time
Thread Time
1:358 0 days 0:00:47.408 2:150 0 days 0:00:03.495
0:d8 0 days 0:00:00.000
看起来好像是ID为0x358的线程占用了大部分的CPU时间。但这个消息还不足以证明线程0x358就是罪魁祸首,因为这条命令显示的CPU时间是线程在它整个生命期中所花的。我们还需要进一步查看线程所用CPU时间的变化情况。让我们再次运行这条命令。这次,我们可以看到类似于下列的内容:
0:000> !runaway
User Mode Time
Thread Time
1:358 0 days 0:00:47.408
2:150 0 days 0:00:06.859
0:d8 0 days 0:00:00.000
现在,我们可以把这个输出内容与上次的输出内容做个比较,找出CPU时间增长最快的线程。在这个例子里,很明显就是线程0x150。现在,我们可以用Visual Studio调试器附上这个应用程序,切换到这个线程下,检查它为什么转个不停。