Debugging Tools for Windows

源码模式调试

如果可以分析源代码而不是反汇编二进制代码,调试程序会更加容易一些。

当源代码是C、C++或汇编语言时,WinDbg、CDB和KD可以在调试中使用它们。

编译的要求

要进行源码调试,必须让编译器或链接器在构建二进制文件时生成符号文件(.pdb文件)。这些符号文件保存了二进制指令和源码行之间的对应关系。

另外,调试器必须能够访问源码文件,因为符号文件中并不包含实际的源代码文本。

如果这些都满足,编译器和链接器还不能对代码进行优化。如果代码经过优化,在源码调试时访问局部变量会变得很困难,有时候几乎是不可能的。如果使用Build 实用程序作为编译器和链接器,可以将MSC_OPTIMIZATION 宏设置为/Od /Oi 来避免优化。

定位符号文件和源码文件

在源码模式下调试,调试器必须能够找到源码文件和符号文件。更多信息,查看设置路径和加载文件

开始源码调试

只要调试器拥有当前被调试线程的正确的符号和源码文件,就可以显示源码信息。

如果使用调试器启动一个新的用户模式程序,在Ntdll.dll加载程序时初始断点就会触发。由于调试器不能访问Ntdll.dll的源码文件,所以这时不能访问应用程序的源码信息。

要将程序计数器移动到程序的开始位置,可以在二进制代码入口点设置断点。在调试器命令窗口输入下面的命令。

bp main
g

之后,程序会被加载起来并在进入main函数时停止。(当然,可以使用任何入口点,而不仅仅是main。)

如果程序抛出一个异常,它会中断到调试器中。这时源码信息是可用的。但是,如果通过CTRL+CCTRL+BREAKDebug | Break命令来中断,调试器创建了一个新线程,所以不能看到源代码。

当到达具有源码文件的线程时,在调试器命令窗口中就可以执行源码调试命令了。如果使用WinDbg,Source窗口会出现。如果已经通过点击File菜单的 Open Source File 打开了源码窗口,

在WinDbg GUI中进行源码调试

如果使用WinDbg,当程序计数器运行到调试器拥有源码信息的代码时,一个源码窗口会出现。

WinDbg为用户或它自己打开的每个源文件显示一个源码窗口。关于该窗口的文本属性的更多信息,查看Source窗口

之后可以单步执行程序、执行到断点或执行到光标。关于单步和跟踪命令的更多信息,查看控制目标

源码模式调试时,如果单步执行程序,合适的源码窗口会移动到前台。因为应用程序执行中也会调用到一些Microsoft Windows函数,这时调试器可能会将反汇编窗口移到前台(因为调试器不能访问这些函数的源码)。当程序计数器又返回到已知的源码文件,相关的源码窗口又会被激活。控制应用程序执行时,WinDbg将源码窗口和汇编窗口中所在的位置用绿色高亮。设置了断点的行为红色(启用的断点)、黄色(禁用的断点)或紫色(如果当前程序计数器是断点位置)。源代码会根据对语言的分析进行着色。如果已经选中了源码窗口,则可以将鼠标移动到符号上来查看它的值。关于这些特性的信息以及如何控制它们,查看Source窗口

在WinDbg中激活源码调试,可以使用L+t命令、点击Debug 菜单的Source Mode 或在工具栏点击Source mode on 按钮()。

源码模式激活时,状态栏的ASM指示器会变为灰色。

源码模式下单步执行某个函数时,可以查看或修改它的任何局部变量的值。更多信息,查看读写内存

调试器命令窗口中的源码调试

如果使用CDB,则没有单独的源码窗口。但是,在单步执行源码时还是可以查看运行的情况。

使用CDB源码调试之前,必须通过.lines (Toggle Source Line Support) 命令加载源码行符号,或者使用-lines 命令行选项启动调试器。

如果使用了ll+t 命令,则每次单步执行一行源码。使用L-t 来一次执行一条汇编指令。如果使用WinDbg,该命令和选中或清除Debug 菜单上的Source Mode 或使用工具栏菜单的效果一样。

 l+s 命令在提示符显示当前的代码行和行号。如果只想显示行号,使用l+l

如果使用l+ol+s,在单步执行时只会显示源码行。程序计数器、汇编码和寄存器信息都不会显示。这种显示类型使得可以快速通过源码来单步调试而不会看到除了源码之外的东西。

lsp (Set Number of Source Lines)命令来指定单步或者执行程序时显示的源码行数。

下面的命令序列是一种单步执行源码文件的有效方式。

.lines        启用源码行信息
bp main       设置初始断点
l+t           按源码行进行单步
l+s           命令窗口中显示源码行
g             运行程序,直到"main"函数
pr            执行一行源码,并将寄存器切换为不显示
p             执行一行源码 

因为ENTER会重复最后一条命令,所以现在可以通过ENTER键来单步调试程序了。每一步都会有源码行、内存偏移和汇编代码显示出来。

关于反汇编显示的更多信息,查看汇编模式调试

当显示汇编代码时,在每行右边末尾会显示出任何访问到的内存位置。用d* (Display Memory)e* (Enter Values)命令来查看或修改这些位置的值。

如果需要查看每条汇编代码来确认偏移或内存信息,使用l-t来以汇编指令单步而不是用源码。源码行信息仍然可以显示出来。每行源码和一条或多条汇编指令对应。

所有这些命令在WinDbg和CDB中都可用。可以使用这些命令来在WinDbg的调试器命令窗口查看源码,而不是在源码窗口中。

源码行和偏移

使用表达式求值器来确定和指定源码行关联的偏移位置也可以进行源码调试。

下面的命令显示一个内存偏移。

? `[[module!]filename][:linenumber]` 

如果省略filename,调试器会搜索和当前程序计数器位置符合的代码。

不管当前使用的基数是什么,如果没有添加0x前缀,调试器认为linenumber 是10进制数。如果省略了linenumber ,表达式的值为和源码文件关联的可执行文件初始地址。

CDB中只有在使用了.lines 命令或-lines 命令行选项来加载源码行符号时才能识别该语法。

该技术非常有用,因为不管当前的程序计数器指向什么地方都可以使用。例如,可以使用下面这样的命令来预先设置断点。

bp `source.c:31` 

更多信息,查看源码行语法使用断点

源码模式下的单步和跟踪

以源码模式调试时,单行代码种可能有多个函数调用。不能使用pt来分开这些调用。

例如,在下面的命令中,t命令会单步进入GetTickCountprintf ,而p命令会单步步过两个调用。

printf( "%x\n", GetTickCount() );

如果在跟踪进入其他调用时想步过某个调用,用.step_filter (Set Step Filter)来指定步过哪些调用。

可以使用_step_filter 来跳过框架函数(例如,对微软基本类库(Microsoft Foundation Classes,MFC)或活动模板库(ATL)的调用)。

Build machine: CAPEBUILD