Visual Studio调试之断点技巧篇补遗

 

讲完Visual Studio调试之断点技巧篇以后,翻翻以前看得一些资料和自己写的一些文章,发现还有几个关于中断程序的技巧在前面的文章里面遗漏了,决定还是在这里总结一下。当然啦,如果你知道这些技巧,忽略这篇文章好了,:)

在程序启动的时候将调试器附加上去


可能有人会对这个问题有一些争议
,因为大部分情况下我们只需要在调试器(Debugger)里面直接启动被调试程序(Debuggee)就可以在程序启动前调试程序了。

但有些情况下,你是不能控制被调试程序(Debuggee)在什么时候启动的。例如在DCOM环境里面,DCOM客户端(Client)可以通过调用CoCreateInstanceEx(, CTX_LOCAL_SERVER, )启动DCOM服务器(Server),启动DCOM服务器的过程是在COM库中进行的, 你没有办法将在DCOM服务器的WinMain函数之前将你的调试器附加(Attach)上去。Windows提供了一个功能就是在一个程序启动的时候,自动将设置好的调试器附加到这个新启动的程序上去。这里我就是要介绍这个功能:

或者说你需要调试一个Windows服务的启动部分,在服务启动的时候,让调试器附加上去岂不是比在Start()函数里面打印很多跟踪信息要好很多?

在程序启动的时候将调试器附加上去实际上是Windows操作系统提供的功能,呃……当然,调试器之所以能工作,也是CPU和操作系统通力合作的结果,:)。因此你需要修改一些操作系统里面的设置:

1.       打开注册表编辑器(regedit.exe.

2.       找到键值HKEY_LOCAL_MACHINE"SOFTWARE"Microsoft"Windows NT"CurrentVersion"Image File Execution Options"

3.       新建一个键(Key),键名就是你要调试的程序的文件名,例如notepad.exe

4.       然后在这个新建的键值(Key)下,在我们的例子里,这个键值是notepad.exe。新建一个字符串值(String ValueDebugger,值设置为你喜欢的调试器:

a.       如果选择Visual Studio的话,就是D:"Program Files"Microsoft Visual Studio 9.0"Common7"IDE"devenv.exe(假设你的Visual Studio安装在D盘)

b.      选择Windbg的话,就是c:"debuggers"windbg.exe(假设你的Windbg拷贝在c:"debuggers"目录里)。

 

设置好了以后,启动notepad.exe,这个时候你选择的调试器就会被Windows先启动起来,然后在notepad.exe的入口处中断。换句话说,所有的程序,都可以使用这种方法在启动的时候自动激活调试器,当然啦,如果你已经修复了你的问题,不需要调试器在程序启动之前启动的话,在注册表里面删掉那个键值就好了。

如下图所示:

 

如何设置验尸调试(POSTMORTEM DEBUGGER)

首先先讲一下什么叫做验尸调试,大家应该都有这个体验,当一个程序突然崩溃的时候,Windows会不失时机弹出一个对话框,问你是否要发送错误报告给微软。其实这个对话框叫做Watson Windows自带的一个非常简单的调试器,它的工作就是将程序的内存拷贝下来保存到一个文件当中。这个过程就是验尸调试的准备步骤,因为程序加载进内存以后,术语叫做进程,当进程崩溃的时候,就是意外死亡嘛,当然进程正常结束那叫做自然死亡。一般我们只对意外死亡的进程比较感兴趣,就跟电视里警匪片里面描写的那样,一个无辜的配角死掉了,警察第一步总是要保护现场,带尸体回去 ,让法医验尸。进程意外死亡的时候,Windows区别进程是否是意外死亡的方式很简单,就是看进程里面是否有未处理的异常。Windows也提供了一个选项,可以让你把犯罪现场--进程发生异常的时候的内存保留下来(进程的尸体),你可以在晚一点的时候来慢慢分析这个内存(比如看一看堆栈,一些变量什么的)--这个过程就叫做验尸。

 

验尸调试对下面这种情况很有帮助:

1.       重现很难重现的bug,比如说你有一个bug是随机出现,但是每次出现都会把程序搞死,与其绞尽脑汁去回忆重现步骤,还不如直接把犯罪现场保留下来,慢慢分析。

 

启动非托管程序的验尸调试功能

跟在程序启动时将调试器附加上去类似,验尸调试实际上也是Windows提供的功能,启动验尸调试,你需要修改下面的注册表设置:

""HKEY_LOCAL_MACHINE"Software"Microsoft"Windows NT"CurrentVersion"AeDebug里面,分别创建下面两个键:

 

名称

类型

键值

Debugger

REG_SZ

"C:"Windows"system32"vsjitdebugger.exe" -p %ld -e %ld

Auto

REG_SZ

1

 

如图所示:


 

里面的键值说明一下:

1.       Debugger:指定了用来执行验尸调试的调试器文件名的完整路径,-p-e是调试器的一些命令行参数,而%ld则是一个占位符,Windows会把死亡的进程的PID替换%ld这个值(读者如果熟悉批处理编程的话,就应该知道%ld实际上是批处理程序的参数的声明方式)。

 

vsjitdebugger.exeVisual Studio用来处理验尸调试的调试器名称,如果你安装了Visual StudioVS的安装程序应该会自动为你设置好这个键。

 

2.       Auto:如果值是1的话,那么windows就会自动在进程死亡的时候,启动调试器;如果为0的话,就会打开一个对话框问你是否要执行验尸调试但是Windows 7如果是这个选项的话,会直接禁用验尸调试,因此我推荐将Auto的值总是设置成1

 

备注:对于偏好windbg的读者,你有两个方案将Windbg设置成默认的验尸调试程序:

1.       直接执行

windbg –I

这个命令,注意I要大写。

2.       Debugger的键值设置为:

"c:"Debuggers"windbg.exe" -p %ld -e %ld –g

 

设置好了以后,我们可以写一个测试程序来试一下刚才的设置:

 

Test.cpp

#include "stdafx.h"

 

 

int _tmain(int argc, _TCHAR* argv[])

{

       int *p = NULL;

 

       // 触发一个异常

       *p = 0;

 

       return 0;

}

 

使用命令cl.exe /Zi test.cpp编译好程序以后,执行test.exe,你应该就可以看到调试器自动在进程死亡的时候弹出来了。


启动托管程序的验尸调试功能


然而,上面的设置仅对
native程序有效,如果要设置托管程序的默认验尸调试器,在""HKEY_LOCAL_MACHINE"Software"Microsoft".NETFramework里面,分别创建下面两个键:

 

名称

类型

键值

DbgManagedDebugger

REG_SZ

"C:"Windows"system32"vsjitdebugger.exe" PID %d APPDOM %d EXTEXT "%s" EVTHDL %d

DbgJITDebugLaunchSetting

REG_DWORD

2

使用上面的设置,你应该可以做所有非Winform托管程序的验尸调试了。

启动Winform程序的验尸调试功能

当你的Win form程序崩溃(Crash)的时候,你会发现你设置的默认验尸调试器没有运行起来,原因是因为Win form程序默认禁用了即时调试(JIT Debug)的功能。因此要设置Win form程序的默认验尸调试器,你除了做上面的步骤以外,你还要将Win form程序的即时调试功能打开。打开的方法:

1.       修改你机器的machine.config文件,这样机器上所有的Win form程序都会将这个即时调试功能打开。在<configuration>里面添加下面一行:

<system.windows.forms jitDebugging="true" />

2.       修改单独程序的的app.config文件打开单个Win form程序的即时调试功能。在<configuration>里面添加下面一行:

<system.windows.forms jitDebugging="true" />

例如:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.windows.forms jitDebugging="true" />

</configuration>

使用Visual StudioRPC调试功能同时调试COM程序的客户端和服务端

参看我以前的文章:
http://blog.csdn.net/Donjuan/archive/2009/01/23/3851586.aspx

posted @ 2009-10-10 11:46  donjuan  阅读(4515)  评论(7编辑  收藏  举报