在进程崩溃的时候自动抓取一个DUMP文件
在如下的情况下需要使用这样的技巧:
1. 生产环境上出问题, 我们需要抓取dump文件, 在线下去debug.
2. 正在运行一大堆测试, 当其中一个崩溃的时候, 不希望在运行时干扰整个测试动作, 仅需要收集一些测试信息.
3. 问题在连接到debugger后无法重现.
简单来说, 就是你希望在对环境影响最小的前提下, 抓取尽可能多的信息.
为了满足这种需求, 最好的方式是配置 just-in-time (JIT) debugger , 让它在进程崩溃的任何时候能够: 启动, 抓取dump, 退出.
JIT debugger的基本思想是:当一个进程崩溃的时候,加载debugger, attach debugger到进程上, 以便我们弄清楚为什么会崩溃.
有注册表键值可以提供这项基本的功能, 针对托管的, 非托管的都有. 如果你的应用程序是用托管代码编写的, 你也许会问, "我的应用程序是托管代码, 为什么我要关心native code?" 即使是给你最基本的托管代码应用程序都会运行native code, 如果你的需求是收集任何crash的数据, 你将需要为这两种类型的代码设置注册表键值. 在CLR第四版中, 已经定义了带有native code的托管JIT debugger. 然而, 这个修改并不影响我这里的指导, 这里, V2 V4都适用.
我如何配置debugger?
=====================
1. 下载并安装最新的“Debugging Tools for Windows.”
a. 如果你是在运行64-bit OS, 你将会需要32-Bit and 64-bit 两个版本.
b. 你在机器上既可以安装整个的工具集(这很快, 安装很小), 或者你可以在一台机器上安装, 然后从安装路径拷贝"cdb.exe"到任何目标机器上.
注意, 下面的sample.reg文件假设你安装32位debugger到c:\debuggers\x86\下, 64位debugger到c:\debuggers\x64\ 下
2. 创建或配置下面的注册表键和值(如果你使用的是64位版的windows, 你还需要在Wow6432节点下配置这些值)
a. 键: HKLM\Software\Microsoft\Windows NT\Current Version\AeDebug:
i. 值: "Debugger"
1. 类型: String
2. 值数据: <path to cdb> -pv -p %ld -c “.dump /u /ma <dump file path\name.dmp>;.kill;qd"
ii. 值: “Auto”
1. 类型: String
2. 值数据: "1"
b. 键: HKLM\Software\Microsoft\.NETFramework
i. 值: “DbgManagedDebugger"
1. 类型: String
2. 值数据: <path to cdb> -pv -p %ld -c ".dump /u /ma <dump file path\name.dmp>;.kill;qd"
ii. 值: "DbgJITDebugLaunchSetting"
1. 类型: DWORD(32-bit)
2. 值数据: 2
注意: 你应该根据合适的debugger的位数, 比如说, 你想要OS/CLR为64位进程崩溃加载64位的debugger, 32位的进程崩溃加载32为的debugger. 请确保你的debugger的路径是被正确地设置了的.
下面的sample.reg文件会配置机器上的cdb.exe为自动加载, 并在每个进程崩溃的时候生成一个crash dump文件. 注意文件中的关于debugger路径和dump文件存放路径的假设.
Windows Registry Editor Version 5.00
;This reg file installs just-in-time debuggers to capture a dump of all process
;crashes for the machine.;
;Assumes 32-bit debugger is cdb.exe and is installed to c:\debuggers\x86\.
;Assumes 64-bit debugger is cdb.exe and is installed to c:\debuggers\x64\.
;
;Assumes crash dumps can be written to c:\crash_dumps\.
;Make sure all users have write access to this directory.
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
"DbgManagedDebugger"="\"c:\\debuggers\\x64\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""
"DbgJITDebugLaunchSetting"=dword:00000002
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug]
"Debugger"="\"c:\\debuggers\\x64\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""
"Auto"="1"
;The following keys are only used on 64-bit versions of Windows (note Wow6432Node).
;They can be safely created with no side-effects on 32-bit versions of Windows.
;Alternatively, you can delete the remainder of this file if you’re running a
;32-bit version of Windows.
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug]
"Debugger"="\"c:\\debuggers\\x86\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""
"Auto"="1"
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework]
"DbgManagedDebugger"="\"c:\\debuggers\\x86\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""
"DbgJITDebugLaunchSetting"=dword:00000002
这些键值有什么作用?
=====================
“Debugger” 和“DbgManagedDebugger” 的值数据基本上就是在进程崩溃的时候运行的命令行(printf格式的字符串). OS 或者 CLR 用实际值替代掉其中的格式指示符, 然后使用当前用户的崩溃进程的上下文来运行命令. 比如说, 会用崩溃的process id去替换掉 "%ld".
在命令行中, 我指定了:
- 加载cdb.exel, 也就是debugger
- 很显然, 你必须为debugger指定正确的路径
- “-pv %ld” : 非侵入性地(仅仅是把线程暂停)挂接到崩溃的进程上(操作系统或CLR会实质上地帮你添写PID)
- “.dump /u /ma <dump file path\name.dmp>”: 拿下完整的内存dump, 使用一个独一无二的名字(使用日期, 时间, 和进程ID来扩展名称), 把它保存到指定的路径
- 路径和文件名可以是任何你想指定的形式. 因为debugger是在崩溃的进程的上下文中加载的, 所以你要确保你指定的路径是任何用户都有权限写入的
- “.kill”: 干掉目标进程, 因为你已经得到了你需要的数据.
- “qd”: 离开debugger
“Auto” 和“DbgJITDebugLaunchSetting”的值设定了何时加载debugger的策略规则(policy). 正如我上面写道的, 我们希望尽快地得到数据, 并继续, 所以我们不希望用户干预的介入. 比如说, 在一个服务器上, 可能不会有人登录并点击什么OK按钮. 我描述的配置会自动地为机器上的所有进程加载注册了的debugger, 而不会弹出框来让你选择(参考Enabling JIT-attach Debugging, 其中有关于弹这个框的更多信息). 注意, 当这些配置存在了之后, 机器上运行的所有进程在崩溃的时候都会自动地加载起debugger, 从而不会让你有机会去在"Windows Error Reporting"里呈交这个crash.
我不在乎一台机器上的绝大多数的进程, 我可以只抓取某一个进程的crash dump么?
====================
这个问题的答案取决于你OS的版本, 还有CLR的版本. 下面是规则:
- 对于native code来说: 你的OS必须是Vista/Server 2008 或者更高.
- 对于managed code来说: 你的CLR的版本必须是V4(或者更高)
下面是如何配置的方法:
1. 如同上面的2.a.i 和2.b.i一样, 配置debugger键值. (AeDebug\Debugger 和.NETFramework\DbgManagedDebugger)
2. 确保AeDebug\Auto 和.NETFramework\DbgJITDebugLaunchSetting 已经被配置为自动加载(再一次强调, 参考Enabling JIT-attach Debugging 来查找更多信息).
a. 或者你可以删除它们Or you can delete them.
3. 创建如下的注册表键值对Create the following registry keys and values:
a. HKLM\Software\Microsoft\Windows\Windows Error Reporting\DebugApplications
i. 值: <Name of application executable> (e.g. “myapp.exe”)
1. 类型: DWORD (32-bit)
2. 值数据: 1
b. 为每一个你希望debugger自动加载的应用程序重复这个操作.
如果你更喜欢个人的控制, 你可以配置HKCU中的DebugApplications 键值对. 当这些配置生效的时候, debugger会仅为你指定的进程加载起来, 而其他进程所使用的会是普通的错误处理方式(比如说, 最默认的配置下, 会弹出一个框, 让你提交这个错误到微软去.)
下面的例子中的sample.reg文件, 会配置cdb.exe为自动加载的, 但仅仅是为HelloWorld.exe. 你可以替代HelloWorld.exe为你想要抓取dump的进程.
Windows Registry Editor Version 5.00
;This reg file installs just-in-time debuggers to capture a dump of only the
;processes listed under the [DebugApplications] key, below.;
;Assumes 32-bit debugger is cdb.exe and is installed to c:\debuggers\x86\.
;Assumes 64-bit debugger is cdb.exe and is installed to c:\debuggers\x64\.
;
;Assumes crash dumps can be written to c:\crash_dumps\.
;Make sure all users have write access to this directory.
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
"DbgManagedDebugger"="\"c:\\debuggers\\x64\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""
"DbgJITDebugLaunchSetting"=dword:00000000
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug]
"Debugger"="\"c:\\debuggers\\x64\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""
"Auto"="0"
;The following keys are only used on 64-bit versions of Windows (note Wow6432Node).
;They can be safely created with no side-effects on 32-bit versions of Windows.
;Alternatively, you can delete the remainder of this file if you’re running a
;32-bit version of Windows.
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug]
"Debugger"="\"c:\\debuggers\\x86\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""
"Auto"="0"
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework]
"DbgManagedDebugger"="\"c:\\debuggers\\x86\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""
"DbgJITDebugLaunchSetting"=dword:00000000
;For each application you want the debugger to be auto-launched, add a row below
;similar to “HelloWorld.exe"=dword:00000001 but replacing HelloWorld.exe with
;your application .exe name.
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\DebugApplications]
"HelloWorld.exe"=dword:00000001
除了抓dump, 我还可以做更多的事情么?
=========================
关于Windows Debugger是的命令行开关的更多的信息, 可以在Debugging Tools fow Windows中包括的文档中找到. 你并没有被限制仅仅抓取dump, 你还可以执行很多debugger的自动化操作呢.
嗯, 那么究竟是什么导致了问题发生?
========================
现在, 你已经有了dump文件, 是时候来弄清楚为什么应用程序崩溃了. 对于那些已经熟悉了dump debugging的读者, 到这里你就拉出WinDBG + SOS(托管debugging的扩展组件), 并深入研究了. 但是请等一下! 如果你的应用程序运行在CLR V4上(.Net Framework 4.0), 你可以在Visual Studio 2010中进行debug了. 我们的目标是在VS2010中, debug dump像live debug一样的体验(如同走到了断点).
针对如何debug托管代码的crash, 搜索"managed dump debugging"会返回不少结果. 一个很好的初学者去处是Tess Ferrandez’s blog (一个微软的技术支持大牛). 她有不少关于这个话题的精彩文章, 包括dump debugging in VS 2010, 还有一些实验, 手把手的文章.
Automatically Capturing a Dump When a Process Crashes
Enabling JIT-attach Debugging
http://msdn.microsoft.com/en-us/library/2ac5yxx6(VS.80).aspx