《使用windbg调试程序》由会员分享,可在线阅读,更多相关《使用windbg调试程序(4页珍藏版)》请在金锄头文库上搜索。
1、什么是 WinDBG?WinDbg 是微软开发的免费源码级调试工具。Windbg 可以用于 Kernel 模式调试和用 户模式调试,还可以调试 Dump 文件。由于大部分程序员不需要做 Kernel 模式调试, 我在这篇文章中不会介绍 Kernel 模式调试。Kernel 模式调试对学习 Windows 核心极 有帮助。如果你对此感兴趣,可以阅读 Inside Windows 2000 和 Windbg 所带的帮助文件。这篇文章得主要目的是介绍 WINDBG 的主要功能以及相关的命令。关于这些命令 的详细语法,请参阅帮助文件。对文章中提到的许多命令,WINDBG 有相应的菜单选项。如何得到帮助
2、在命令(Command)窗口中输入.hh 命会调出帮助文件令。.hh keyword会显示关于 keyword 的详细命令。启动 DebuggerWindbg 可以用于如下三种调试:远程调试:你可以从机器 A 上调试在机器 B 上执行的程序。具体步骤如下: ? 在机器 B 上启动一个调试窗口(Debug Session) 。你可以直接在 Windbg 下运行一个程序或者将 Windbg 附加(Attach)到 一个进程。? 在机器 B 的 Windbg 命令窗口上启动一个远程调试接口(remote):.server npipe:pipe=PIPE_NAMEPIPE_NAME 是该接口的名字。?
3、 在机器 A 上运行:windbg remote npipe:server=SERVER_NAME,pipe=PIPE_NAMESERVER_NAME 是机器 B 的名字。Dump 文件调试:如果在你的客户的机器上出现问题,你可能不能使用远程调试 来解决问题。你可以要求你的用户将 Windbg 附加到出现问题的进程上,然后在命令窗口 中输入: .dump /ma File Name创建一个 Dump 文件。在得到 Dump 文件后,使用如下的命令来打开它:windbg z DUMP_FILE_NAME本地进程调试:你可以在 Windbg 下直接运行一个程序: Windbg “path to e
4、xecutable” arguments 也可以将 Windbg 附加到一个正在运行的程序:Windbg p “process id” Windbg pn “process name” 注意有一种非侵入(Noninvasive)模式可以用来检查一个进程的状态并不进程的执 行。当然在这种模式下无法控制被调试程序的执行。这种模式也可以用于查看一个已经在 Debugger 控制下运行的进程。具体命令如下:Windbg pv p “process id” Windbg pv pn “process name” 调试多个进程和线程如果你想控制一个进程以及它的子进程的执行,在 Windbg 的命令行上加上
5、-o 选项。 Windbg 中还有一个新的命令.childdbg 可以用来控制子进程的调试。如果你同时调试几个进程,可以使用 | 命令来显示并 切换到不同的进程。在同一个进程中可能有多个线程。 命令可以用来显示和切换线程。调试前的必备工作在开始调试前首先要做的工作是设置好符号(Symbols)路径。没有符号,你看到的 调用堆栈基本上毫无意义。Microsoft 的操作系统符号文件(PDB)是对外公开的。另外请 注意在编译你自己的程序选择生成 PDB 文件的选项。如果设置好符号路径后,调用堆栈看 起来还是不对。可以使用 lm, !sym noisy, !reload 等命令来验证符号路径是否正确
6、。Windbg 也支持源码级的调试。在开始源码调试前,你需要用.srcpath 设置源代码路 径。如果你是在生成所执行代码的机器上进行调试,符号文件中的源码路径会指向正确的 位置,所以不需要设置源代码路径。如果所执行代码是在另一台机器上生成的,你可以将 所用的源码拷贝(保持原有的目录结构)的一个可以访问的文件夹(可以是网络路径)并 将源代码路径设为该文件夹的路径。注意如果是远程调试,你需要使用.lsrcpath 来设置源 码路径。静态命令:显示调用堆栈:在连接到一个调试窗口后,首先要知道的就是程序当前的执行情况 k* 命令显示当前线程的堆栈。*kb 会显示所有线程的调用堆栈。如果堆栈太长, W
7、indbg 只会显示堆栈的一部分。.kframes 可以用来设置缺省显示框架数。显示局部变量:接下来要做通常是用 dv 显示局部变量的信息。CTRL ALT V 可 以切换到更详细的显示模式。关于 dv 要注意的是在优化过的代码中 dv 的输出极有可能是 不准确的。这时后你能做的就是阅读汇编代码来发现你感兴趣的值是否存储在寄存器中或 堆栈上。有时后当前的框架(Frame)上可能找不到你想知道的数据。如果该数据是作为 参数传到当前的方法中的,可以读一读上一个或几个框架的汇编代码,有可能该数据还在 堆栈的某个地址上。静态变量是储存在固定地址中的,所以找出静态变量的值较为容易。 .Frame(或者在
8、调用堆栈窗口中双击)可以用来切换当前的框架。注意 dv 命令显示的是当 前框架的内容。你也可在 watch 窗口中观察局部变量的值。显示类和链表: dt 可以显示数据结构。比如 dt PEB 会显示操作系统进程结构。在 后面跟上一个进程结构的地址会显示该结构的详细信息:dt PEB 7ffdf000 。Dl 命令可以显示一些特定的链表结构。显示当前线程的错误值:!gle 会显示当前线程的上一个错误值和状态值。!error 命令 可以解码 HRESULT。搜索或修改内存:使用 s 命令来搜索字节,字或双字,QWORD 或字符串。使用 e 命令来修改内存。计算表达式:?命令可以用来进行计算。关于表
9、达式的格式请参照帮助文档。使用 n 命令来切换输入数字的进制。显示当前线程,进程和模块信息:!teb 显示当前线程的环境信息。最常见的用途是 查看当前线程堆栈的起始地址,然后在堆栈中搜索值。!peb 显示当前进程的环境信息,比 如执行文件的路径等等。lm 显示进程中加载的模块信息。显示寄存器的值:r 命令可以显示和修改寄存器的值。如果要在表达式中使用寄存 器的值,在寄存器名前加符号(比如eax ) 。显示最相近的符号:ln Address。如果你有一个 C 对象的指针,可以用来 ln 来查 看该对象类型。 查找符号:x 命令可以用来查找全局变量的地址或过程的地址。x 命令支持匹配符号。 x k
10、ernel32!*显示 Kernel32.dll 中的所有可见变量,数据结构和过程。查看 lock:!locks 显示各线程的锁资源使用情况。对调试死锁很有用。查看 handle:!handle 显示句柄信息。如果一段代码导致句柄泄漏,你只需要在代码 执行前后使用!handle 命令并比较两次输出的区别。有一个命令!htrace 对调试与句柄有关的 Bug 非常有用。在开始调试前输入:!htrace enable 然后在调试过程中使用!htrace handle_value 来显示所有与该句柄有关的调用堆栈。显示汇编代码:u。程序执行控制命令:设置代码断点:bp/bu/bm 可以用来设置代码断
11、点。你可以指定断点被跳过的次数。假设一段代码KERNEL32!SetLastError 在运行很多次后会出错,你可以设置如下断点:bp KERNEL32!SetLastError 0x100.在出错后使用 bl 来显示断点信息(注意粗体显示的值):0 e 77e7a3b0 004f (0100) 0:* KERNEL32!SetLastError重新启动调试(.restart 命令)并设置如下的断点:bp Kernel32!SetLastError 0x100-0x4fDebugger 会停在出错前最后一次调用该过程的地方。你可以指定断点被激活时 Debugger 应当执行的命令串。在该命令串
12、中使用 J 命令可 以用来设置条件断点:bp mysource.cpp:143 “j (poi(MyVar)”0n20) ; g “上面的断点只在 MyVar 的值大于 32 时被激活(g 命令条件断点的用途极为广泛。你可以指定一个断点只在特殊的情况下被激活,比如传 入的参数满足一定的条件,调用者是某个特殊的过程,某个全局变量被设为特殊的值等等。设置内存断点:ba 可以用来设置内存断点。调试过程中一个常见的问题是跟踪某些 数据的变化。如下的断点:ba w4 0x40000000 “kb; g“可以打印出所有修改 0x40000000 的调用堆栈。控制程序执行:p, pa,t, ta 等命令可以用来控制程序的执行。控制异常和事件处理:Debugger 的缺省设置是跳过首次异常(first chance expcetion), 在二次异常(second chance exception)时中断程序的执行。sx 命令显示 Debugger 的设置。sxe 和 sxd 可以改变 Debugger 的设置。sxe clr可以控制 Debugger 在托管异常发生时中断程序的执行。常用的 Debugger 事件有:av 访问异常 eh C+ 异常clr 托管异常ld 模块加载-c 选项可以用来指定在事件发生时执行的调试命令。