Windows 异常处理的研究的总结 (一)
2013-02-23 14:24
253 查看
最近半年单位的项目集中上线,出了一些 crash 的问题。
程序crash 后,会自动生成一个 dump 文件,这是通过调用系统API MiniDumpWriteDump 实现的。
通过事后对 dump 文件的分析,工程师可以找到出问题的点。
不方便的是,每次打开dump文件后,堆栈总是停在类似 ntdll!KiFastSystemCallRet 的地方,通过这个 callstack 完全找不到任何程序的蛛丝马迹。而是用 .ecxr 则报错,说没有 ContextRecord. 工程师需要执行 windbg 的 s 命令,在堆栈中寻找 ExceptionContext 去恢复现场,对于做过的人稍有些麻烦,对于生手就比较困难了。
*具体搜索思路是:根据ContextRecord 的定义,第一个成员是异常号,比如 0xc0000005,那就在堆栈上搜这个异常号:s -d esp L1000 0xc0000005
如果是 x64平台,用 s -d rsp L1000 0xc0000005
一般会找到2-3个匹配的堆栈地址,再逐个去试,就很容易找到。
还有一个问题,有时候 dump 文件可以生成,有时候程序直接退出,没有任何 dump。
基于这些现象,我有了一些问题:
1. 能否不用每次手动寻找 ExceptionContext ,而让 windbg 直接重现现场。
2. 能否保证所有的异常都被捕获并生成 dump (当然是在外围条件都满足的情况下)
由这个 Dump 文件引发了我一系列的研究和学习。现在将成果总结如下。
在windbg 定位的时候,需要用到 .cxr 指令,通过查windbg帮助手册,了解到这个指令是 Display Context Record 的缩写。在异常发生时,windows 会将异常发生时的各个寄存器数值、堆栈位置等记录下来,存在一个 ContextRecord 结构里,并把这个结构压入堆栈。.cxr 命令还带一个地址参数,用来指定 ContextRecord 的位置。这个结构对于能否恢复堆栈起着决定性的作用。
通过读《Windows核心编程(第5版)》,了解到windows会在异常发生时,向堆栈上压入3个结构体:ExceptionRecord, ContextRecord, EXCEPTION_POINTERS。
只要生成 dump 文件时这三个结构体还没有出栈,则通过搜索堆栈是一定可以找到ContextRecord,就一定可以恢复堆栈。
但为什么每次要手动搜索 ContextRecord 呢?经过研究MiniDumpWriteDump,有了答案:
BOOL WINAPI MiniDumpWriteDump(
__in HANDLE hProcess,
__in DWORD ProcessId,
__in HANDLE hFile,
__in MINIDUMP_TYPE DumpType,
__in PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
__in PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
__in PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);
第5个参数 ExceptionParam,是一个结构地址,这个结构的定义如下:
typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
DWORD ThreadId;
PEXCEPTION_POINTERS ExceptionPointers;
BOOL ClientPointers;
} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
结构体的第二个参数就是 EXCEPTION_POINTERS,而这个指针的第二个成员就是 ContextRecord。
之前生成 dump 的时候,并没有将这个结构填好传给 MiniDumpWriteDump,所以才造成直接用 .ecxr 命令找不到 ContextRecord 的错误。
通过修改代码,在异常发生时将 EXCEPTION_POINTER 保存下来(如果能确保生成 dump 时,堆栈上的 ContextRecord 依然有效,则仅保留EXCEPTION_POINTER的地址即可)。
调用 MiniDumpWriteDump 时将这个 EXCEPTION_POINTER 给传进去,就可以做到 .ecxr 指令自动恢复堆栈定位错误行了。
程序crash 后,会自动生成一个 dump 文件,这是通过调用系统API MiniDumpWriteDump 实现的。
通过事后对 dump 文件的分析,工程师可以找到出问题的点。
不方便的是,每次打开dump文件后,堆栈总是停在类似 ntdll!KiFastSystemCallRet 的地方,通过这个 callstack 完全找不到任何程序的蛛丝马迹。而是用 .ecxr 则报错,说没有 ContextRecord. 工程师需要执行 windbg 的 s 命令,在堆栈中寻找 ExceptionContext 去恢复现场,对于做过的人稍有些麻烦,对于生手就比较困难了。
*具体搜索思路是:根据ContextRecord 的定义,第一个成员是异常号,比如 0xc0000005,那就在堆栈上搜这个异常号:s -d esp L1000 0xc0000005
如果是 x64平台,用 s -d rsp L1000 0xc0000005
一般会找到2-3个匹配的堆栈地址,再逐个去试,就很容易找到。
还有一个问题,有时候 dump 文件可以生成,有时候程序直接退出,没有任何 dump。
基于这些现象,我有了一些问题:
1. 能否不用每次手动寻找 ExceptionContext ,而让 windbg 直接重现现场。
2. 能否保证所有的异常都被捕获并生成 dump (当然是在外围条件都满足的情况下)
由这个 Dump 文件引发了我一系列的研究和学习。现在将成果总结如下。
在windbg 定位的时候,需要用到 .cxr 指令,通过查windbg帮助手册,了解到这个指令是 Display Context Record 的缩写。在异常发生时,windows 会将异常发生时的各个寄存器数值、堆栈位置等记录下来,存在一个 ContextRecord 结构里,并把这个结构压入堆栈。.cxr 命令还带一个地址参数,用来指定 ContextRecord 的位置。这个结构对于能否恢复堆栈起着决定性的作用。
通过读《Windows核心编程(第5版)》,了解到windows会在异常发生时,向堆栈上压入3个结构体:ExceptionRecord, ContextRecord, EXCEPTION_POINTERS。
只要生成 dump 文件时这三个结构体还没有出栈,则通过搜索堆栈是一定可以找到ContextRecord,就一定可以恢复堆栈。
但为什么每次要手动搜索 ContextRecord 呢?经过研究MiniDumpWriteDump,有了答案:
BOOL WINAPI MiniDumpWriteDump(
__in HANDLE hProcess,
__in DWORD ProcessId,
__in HANDLE hFile,
__in MINIDUMP_TYPE DumpType,
__in PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
__in PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
__in PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);
第5个参数 ExceptionParam,是一个结构地址,这个结构的定义如下:
typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
DWORD ThreadId;
PEXCEPTION_POINTERS ExceptionPointers;
BOOL ClientPointers;
} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
结构体的第二个参数就是 EXCEPTION_POINTERS,而这个指针的第二个成员就是 ContextRecord。
之前生成 dump 的时候,并没有将这个结构填好传给 MiniDumpWriteDump,所以才造成直接用 .ecxr 命令找不到 ContextRecord 的错误。
通过修改代码,在异常发生时将 EXCEPTION_POINTER 保存下来(如果能确保生成 dump 时,堆栈上的 ContextRecord 依然有效,则仅保留EXCEPTION_POINTER的地址即可)。
调用 MiniDumpWriteDump 时将这个 EXCEPTION_POINTER 给传进去,就可以做到 .ecxr 指令自动恢复堆栈定位错误行了。
相关文章推荐
- Windows 异常处理的研究的总结 (二)
- Windows 系统异常处理顺序总结
- Windows 系统异常处理顺序总结
- Java EE项目中异常设计及处理总结
- Java 异常处理的误区和经验总结
- 函数错误处理一(总结自windows编程核心)
- 【原创】Oracle函数中对于NO_DATA_FOUND异常处理的研究
- Java总结第二篇 异常处理--基本概念
- Java 异常处理的误区和经验总结
- 处理[未处理的“System.StackOverflowException”类型的异常出现在 System.Windows.Fo ...
- Tailspin Spyworks指南第八讲:其它页面,异常处理、总结
- Java基础总结9---异常处理
- Windows异常处理流程
- springmvc-学习总结-全局异常处理
- 详谈Java 异常处理的误区和经验总结(分享)
- Windows异常机制1:异常处理
- windows 下 tomcat 内存设置(处理tomcat 内存溢出异常)
- RedHat下一些命令异常处理总结
- java中异常的处理总结(二)