调试winddows程序(windbg 和 Debug Diagnostic Tool)
2012-08-25 23:34:05
一、功能:
调试不在编译器中运行的程序。可以解决一下两种问题。
1、 程序崩溃,能找到程序报错是运行的代码行。
2、 程序涨内存,直接定位到导致内存泄漏的语句。(这个在Debug Diagnostic Tool中使用很简单)
二、用法:
我知道两个工具,Windbg和Debug Diagnostic Tool,前者比较牛叉,能调试系统驱动级的程序,普通程序当然也能调试,后者方便易用,功能也很厉害。
1、如果要调试运行在没有开发环境的机器上的程序,可以此机器上开一个远程调试工具(Windbg或者Remote Debug),然后在自己工作机上运行调试工具(windbg或者vs2008),设置好Symbol(符号 *.pdb文件)和Source(源码)文件路径,然后Attach to Process到需要调试的进程,就能像在平时用vs2008 上调试一样方便了。
2、如果要调试崩溃的程序,可以抓一个dump(*.dmp文件,也叫内存转储文件),然后用Windbg或者DebugDiagnosticTool工具进行分析,也能找到出错的代码行,进而分析出结果。
总之,调试前一定要设置Symbol文件路径,没有Symbol,你看到的调用堆栈基本上毫无意义。
三、详细用法:
(32位机器安装x86版本 64位机器安装x64版本, 以下链接为32位版本)
下载安装windbg
http://msdl.microsoft.com/download/symbols/debuggers/dbg_x86_6.11.1.404.msi
下载安装Debug Diagnostic Tool
http://download.microsoft.com/download/B/8/0/B80535DB-DF4D-4020-B41F-ECE9005D9A65/DebugDiagx86.msi
Windbg中:
1、 设置Symbol和Source。
File->Symbol File Path File->Source File Path
Symbol中添加“srv*c:\symbols* http://msdl.microsoft.com/download/symbols”,可以自动从Microsoft中下载windows系统的pdb,自动按需下载。如果添加多个目录,可以用 ; 分隔。
2、 直接调试可执行程序(*.exe)。
File->Executable ,然后找到exe程序,打开即可。
Debug->Stop Debug可以停止调试。
3、 调试正在运行中的可执行程序。
先运行程序,然后打开Windbg,File->Attach to a process。
4、 调试运行时崩溃的程序。
只要崩溃的程序没有关闭,
打开Windbg,File->Attach to a process,选中崩溃的程序。
在command中输入[.dump /ma 要保存的文件完整路径名称],提示Dump successfully written后,就会生成一个dump文件。
打开Windbg.设置好Symbol文件路径,File->Symbol File Path。
打开dump文件,File->Open Crash dump。
开始分析。。。
kb 显示当前线程调用堆栈。
~*kb 显示所有线程调用堆栈。
lm 显示当前加载的模块信息 (*.pdb *.dll)
!analyze –v 详细列出dump文件的信息
多数境况下列出堆栈信息,基本就能确定是程序哪行出现问题了。
5、调试涨内存的程序
这个时候用Debug Diagnostic Tool
6、 远程调试(没有实践过)
1)使用windbg:
你可以从机器A上调试在机器B上执行的程序。具体步骤如下:
在机器B上启动一个调试窗口(Debug Session)。你可以直接在Windbg下运行一个程序或者将Windbg附加(Attach)到一个进程。
在机器B的Windbg命令窗口上启动一个远程调试接口(remote):
.server npipe:pipe=PIPE_NAME
PIPE_NAME是该接口的名字。
在机器A上运行:
windbg –remote npipe:server=SERVER_NAME,pipe=PIPE_NAME
SERVER_NAME是机器B的名字。
2)使用vs2008:
在机器B上启动msvsmon.exe(远程调试器,在Viual studio Tool中可以找到,要拷贝所有文件到机器B(C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\Remote Debugger\x86))
点击 工具->选项 ,选择“windows身份验证”
点击 工具->权限 ,勾选“允许”
然后在机器A上打开vs2008,点击 工具->附加到进行,弹出下面窗口,输入机器B的IP,连接正常后,就能在下面看见机器B上的进程了。
四、windbg常用命令:
.dump /ma File Name 创建一个Dump文件
显示调用堆栈:在连接到一个调试窗口后,首先要知道的就是程序当前的执行情况
kb 命令显示当前线程的堆栈。
~*kb会显示所有线程的调用堆栈。如果堆栈太长,Windbg只会显示堆栈的一部分。.kframes可以用来设置缺省显示框架数。
显示局部变量:
dv显示局部变量的信息。
CTRL+ALT+V可以切换到更详细的显示模式。
关于dv要注意的是在优化过的代码中dv的输出极有可能是不准确的。这时后你能做的就是阅读汇编代码来发现你感兴趣的值是否存储在寄存器中或堆栈上。有时后当前的框架(Frame)上可能找不到你想知道的数据。如果该数据是作为参数传到当前的方法中的,可以读一读上一个或几个框架的汇编代码,有可能该数据还在堆栈的某个地址上。静态变量是储存在固定地址中的,所以找出静态变量的值较为容易。.Frame(或者在调用堆栈窗口中双击)可以用来切换当前的框架。注意dv命令显示的是当前框架的内容。你也可在watch窗口中观察局部变量的值。
显示类和链表:
dt可以显示数据结构。比如dt PEB 会显示操作系统进程结构。在后面跟上一个进程结构的地址会显示该结构的详细信息:dt PEB 7ffdf000。
Dl命令可以显示一些特定的链表结构。
显示当前线程的错误值:
!gle会显示当前线程的上一个错误值和状态值。
!error命令可以解码HRESULT。
搜索或修改内存:
s 命令来搜索字节,字或双字,QWORD或字符串。
e命令来修改内存。
计算表达式:
?命令可以用来进行计算。
关于表达式的格式请参照帮助文档。使用n命令来切换输入数字的进制。
显示当前线程,进程和模块信息:
!teb显示当前线程的环境信息。最常见的用途是查看当前线程堆栈的起始地址,然后在堆栈中搜索值。
!peb显示当前进程的环境信息,比如执行文件的路径等等。lm显示进程中加载的模块信息。
显示寄存器的值:
r命令可以显示和修改寄存器的值。如果要在表达式中使用寄存器的值,在寄存器名前加@符号(比如@eax)。
显示最相近的符号:
ln Address。如果你有一个C++对象的指针,可以用来ln来查看该对象类型。
查找符号:
x命令可以用来查找全局变量的地址或过程的地址。x命令支持匹配符号。
x kernel32!*显示Kernel32.dll中的所有可见变量,数据结构和过程。
查看lock:
!locks显示各线程的锁资源使用情况。对调试死锁很有用。
查看handle:
!handle显示句柄信息。如果一段代码导致句柄泄漏,你只需要在代码执行前后使用!handle命令并比较两次输出的区别。
!htrace对调试与句柄有关的Bug非常有用。在开始调试前输入:
!htrace –enable
然后在调试过程中使用!htrace handle_value 来显示所有与该句柄有关的调用堆栈。
显示汇编代码:
u。
程序执行控制命令:
设置代码断点:bp/bu/bm 可以用来设置代码断点。你可以指定断点被跳过的次数。假设一段代码KERNEL32!SetLastError在运行很多次后会出错,你可以设置如下断点:
bp KERNEL32!SetLastError 0x100.
在出错后使用bl 来显示断点信息(注意粗体显示的值):
0 e 77e7a3b0 004f (0100) 0:*** KERNEL32!SetLastError
重新启动调试(.restart命令)并设置如下的断点:
bp Kernel32!SetLastError 0x100-0x4f
Debugger会停在出错前最后一次调用该过程的地方。
你可以指定断点被激活时Debugger应当执行的命令串。在该命令串中使用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 选项可以用来指定在事件发生时执行的调试命令。
五、Debug Diagnostic Tool详细用法
参考此文: http://hi.baidu.com/ourok/item/47314e8db094e3814414cfba
此工具可以在程序发生崩溃或者内在溢出时自动生成dump,并有相应的崩溃/挂起分析器和内存压力分析器。
六、其他调试工具
参考此文: http://www.cnblogs.com/moonz-wu/archive/2011/07/03/2096693.html
对于C++的程序来说,那工具可就多了。这里我主要用的是IIS Debug Diagnostics Tool,这个原本是用于IIS应用程序的诊断工具,在监视系统服务这样的应用上还是很方便的。同时它可以进行自动的memory leak分析并生成报表。对于memory leak的诊断很有帮助。当然,我还用到了vmmap和rammap两个应用程序。这两个程序原先是system internal那个作者开发的,现在已经收归微软门下了。这两个文件一个用于查看进程的虚拟内存分配情况,而另一个拥有查看物理内存的时候情况。最后一个工具就是process hacker,它能帮助我们更详细的了解进程的内存分配,句柄分配,模块加载,线程数目等。当然processexplorer也可以做相同的工作,但是如果你要查看内存块的内容时,还需要windbg的配合。
调试不在编译器中运行的程序。可以解决一下两种问题。
1、 程序崩溃,能找到程序报错是运行的代码行。
2、 程序涨内存,直接定位到导致内存泄漏的语句。(这个在Debug Diagnostic Tool中使用很简单)
二、用法:
我知道两个工具,Windbg和Debug Diagnostic Tool,前者比较牛叉,能调试系统驱动级的程序,普通程序当然也能调试,后者方便易用,功能也很厉害。
1、如果要调试运行在没有开发环境的机器上的程序,可以此机器上开一个远程调试工具(Windbg或者Remote Debug),然后在自己工作机上运行调试工具(windbg或者vs2008),设置好Symbol(符号 *.pdb文件)和Source(源码)文件路径,然后Attach to Process到需要调试的进程,就能像在平时用vs2008 上调试一样方便了。
2、如果要调试崩溃的程序,可以抓一个dump(*.dmp文件,也叫内存转储文件),然后用Windbg或者DebugDiagnosticTool工具进行分析,也能找到出错的代码行,进而分析出结果。
总之,调试前一定要设置Symbol文件路径,没有Symbol,你看到的调用堆栈基本上毫无意义。
三、详细用法:
(32位机器安装x86版本 64位机器安装x64版本, 以下链接为32位版本)
下载安装windbg
http://msdl.microsoft.com/download/symbols/debuggers/dbg_x86_6.11.1.404.msi
下载安装Debug Diagnostic Tool
http://download.microsoft.com/download/B/8/0/B80535DB-DF4D-4020-B41F-ECE9005D9A65/DebugDiagx86.msi
Windbg中:
1、 设置Symbol和Source。
File->Symbol File Path File->Source File Path
Symbol中添加“srv*c:\symbols* http://msdl.microsoft.com/download/symbols”,可以自动从Microsoft中下载windows系统的pdb,自动按需下载。如果添加多个目录,可以用 ; 分隔。
2、 直接调试可执行程序(*.exe)。
File->Executable ,然后找到exe程序,打开即可。
Debug->Stop Debug可以停止调试。
3、 调试正在运行中的可执行程序。
先运行程序,然后打开Windbg,File->Attach to a process。
4、 调试运行时崩溃的程序。
只要崩溃的程序没有关闭,
打开Windbg,File->Attach to a process,选中崩溃的程序。
在command中输入[.dump /ma 要保存的文件完整路径名称],提示Dump successfully written后,就会生成一个dump文件。
打开Windbg.设置好Symbol文件路径,File->Symbol File Path。
打开dump文件,File->Open Crash dump。
开始分析。。。
kb 显示当前线程调用堆栈。
~*kb 显示所有线程调用堆栈。
lm 显示当前加载的模块信息 (*.pdb *.dll)
!analyze –v 详细列出dump文件的信息
多数境况下列出堆栈信息,基本就能确定是程序哪行出现问题了。
5、调试涨内存的程序
这个时候用Debug Diagnostic Tool
![]() |
设置symbol文件路径
|
![]() |
![]() |
![]() |
![]() |
![]() |
6、 远程调试(没有实践过)
1)使用windbg:
你可以从机器A上调试在机器B上执行的程序。具体步骤如下:
在机器B上启动一个调试窗口(Debug Session)。你可以直接在Windbg下运行一个程序或者将Windbg附加(Attach)到一个进程。
在机器B的Windbg命令窗口上启动一个远程调试接口(remote):
.server npipe:pipe=PIPE_NAME
PIPE_NAME是该接口的名字。
在机器A上运行:
windbg –remote npipe:server=SERVER_NAME,pipe=PIPE_NAME
SERVER_NAME是机器B的名字。
2)使用vs2008:
在机器B上启动msvsmon.exe(远程调试器,在Viual studio Tool中可以找到,要拷贝所有文件到机器B(C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\Remote Debugger\x86))
点击 工具->选项 ,选择“windows身份验证”
点击 工具->权限 ,勾选“允许”
然后在机器A上打开vs2008,点击 工具->附加到进行,弹出下面窗口,输入机器B的IP,连接正常后,就能在下面看见机器B上的进程了。
![]() |
四、windbg常用命令:
.dump /ma File Name 创建一个Dump文件
显示调用堆栈:在连接到一个调试窗口后,首先要知道的就是程序当前的执行情况
kb 命令显示当前线程的堆栈。
~*kb会显示所有线程的调用堆栈。如果堆栈太长,Windbg只会显示堆栈的一部分。.kframes可以用来设置缺省显示框架数。
显示局部变量:
dv显示局部变量的信息。
CTRL+ALT+V可以切换到更详细的显示模式。
关于dv要注意的是在优化过的代码中dv的输出极有可能是不准确的。这时后你能做的就是阅读汇编代码来发现你感兴趣的值是否存储在寄存器中或堆栈上。有时后当前的框架(Frame)上可能找不到你想知道的数据。如果该数据是作为参数传到当前的方法中的,可以读一读上一个或几个框架的汇编代码,有可能该数据还在堆栈的某个地址上。静态变量是储存在固定地址中的,所以找出静态变量的值较为容易。.Frame(或者在调用堆栈窗口中双击)可以用来切换当前的框架。注意dv命令显示的是当前框架的内容。你也可在watch窗口中观察局部变量的值。
显示类和链表:
dt可以显示数据结构。比如dt PEB 会显示操作系统进程结构。在后面跟上一个进程结构的地址会显示该结构的详细信息:dt PEB 7ffdf000。
Dl命令可以显示一些特定的链表结构。
显示当前线程的错误值:
!gle会显示当前线程的上一个错误值和状态值。
!error命令可以解码HRESULT。
搜索或修改内存:
s 命令来搜索字节,字或双字,QWORD或字符串。
e命令来修改内存。
计算表达式:
?命令可以用来进行计算。
关于表达式的格式请参照帮助文档。使用n命令来切换输入数字的进制。
显示当前线程,进程和模块信息:
!teb显示当前线程的环境信息。最常见的用途是查看当前线程堆栈的起始地址,然后在堆栈中搜索值。
!peb显示当前进程的环境信息,比如执行文件的路径等等。lm显示进程中加载的模块信息。
显示寄存器的值:
r命令可以显示和修改寄存器的值。如果要在表达式中使用寄存器的值,在寄存器名前加@符号(比如@eax)。
显示最相近的符号:
ln Address。如果你有一个C++对象的指针,可以用来ln来查看该对象类型。
查找符号:
x命令可以用来查找全局变量的地址或过程的地址。x命令支持匹配符号。
x kernel32!*显示Kernel32.dll中的所有可见变量,数据结构和过程。
查看lock:
!locks显示各线程的锁资源使用情况。对调试死锁很有用。
查看handle:
!handle显示句柄信息。如果一段代码导致句柄泄漏,你只需要在代码执行前后使用!handle命令并比较两次输出的区别。
!htrace对调试与句柄有关的Bug非常有用。在开始调试前输入:
!htrace –enable
然后在调试过程中使用!htrace handle_value 来显示所有与该句柄有关的调用堆栈。
显示汇编代码:
u。
程序执行控制命令:
设置代码断点:bp/bu/bm 可以用来设置代码断点。你可以指定断点被跳过的次数。假设一段代码KERNEL32!SetLastError在运行很多次后会出错,你可以设置如下断点:
bp KERNEL32!SetLastError 0x100.
在出错后使用bl 来显示断点信息(注意粗体显示的值):
0 e 77e7a3b0 004f (0100) 0:*** KERNEL32!SetLastError
重新启动调试(.restart命令)并设置如下的断点:
bp Kernel32!SetLastError 0x100-0x4f
Debugger会停在出错前最后一次调用该过程的地方。
你可以指定断点被激活时Debugger应当执行的命令串。在该命令串中使用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 选项可以用来指定在事件发生时执行的调试命令。
五、Debug Diagnostic Tool详细用法
参考此文: http://hi.baidu.com/ourok/item/47314e8db094e3814414cfba
此工具可以在程序发生崩溃或者内在溢出时自动生成dump,并有相应的崩溃/挂起分析器和内存压力分析器。
六、其他调试工具
参考此文: http://www.cnblogs.com/moonz-wu/archive/2011/07/03/2096693.html
对于C++的程序来说,那工具可就多了。这里我主要用的是IIS Debug Diagnostics Tool,这个原本是用于IIS应用程序的诊断工具,在监视系统服务这样的应用上还是很方便的。同时它可以进行自动的memory leak分析并生成报表。对于memory leak的诊断很有帮助。当然,我还用到了vmmap和rammap两个应用程序。这两个程序原先是system internal那个作者开发的,现在已经收归微软门下了。这两个文件一个用于查看进程的虚拟内存分配情况,而另一个拥有查看物理内存的时候情况。最后一个工具就是process hacker,它能帮助我们更详细的了解进程的内存分配,句柄分配,模块加载,线程数目等。当然processexplorer也可以做相同的工作,但是如果你要查看内存块的内容时,还需要windbg的配合。