织梦CMS - 轻松建站从此开始!

罗索实验室

当前位置: 主页 > 杂项技术 > VC(MFC) >

Windows程序崩溃dump分析

jackyhwei 发布于 2013-10-10 15:22 点击:次 
参考 Release版本生成调试信息 为你的Release版本程序生成 PDB 调试信息
TAG:

参考 Release版本生成调试信息 为你的Release版本程序生成 PDB 调试信息

目录

[隐藏]

dmp 文件生成

你可以通过多种途径来得到一个dmp文件,以下给出3种.

使用Windbg 进行dump

可以用Windbg Attach 某个进程,进行dump./m表示迷你dmp,/o表示覆盖文件(如果d:\testtttt.dmp已经存在).

.dump /m /o d:\testtttt.dmp

使用Dr.Watson生成dump文件

这是Windows的默认行为.一旦程序崩溃就会调用Dr.Watson.

使用代码手工生成dmp文件

  • SetUnhandledExceptionFilter

为每个线程设置SetUnhandledExceptionFilter(MyCallBack),这样该线程中发现未处理的SEH 异常时就会进入到MyCallBack 回调中.

无聊的是虽然MyCallBack 的参数是SEH异常的结构体指针,但C++异常也会进入到MyCallBack 中.所以只要SetUnhandledExceptionFilter 就能抓到C++的异常了.

按C++标准,未处理的C++异常应当是触发unexpected.而MS 放出话说它的编译器只触发terminate.而在MFC 中居然terminate 都不触发了,直接变成了SEH 而进入了MyCallBack.

  • MiniDumpWriteDump

崩溃回调中使用MiniDumpWriteDump 导出异常时的上下文,所谓MiniDump就是比较小型的dump,dump 出来的文件比较小,但信息足够了我们进行分析了.

在MyCallBack 中进行MiniDumpWriteDump.

MINIDUMP_EXCEPTION_INFORMATION eInfo;
eInfo.ThreadId = GetCurrentThreadId();
eInfo.ExceptionPointers = m_pExceptionInfo;
eInfo.ClientPointers = FALSE;
MINIDUMP_CALLBACK_INFORMATION cbMiniDump;
cbMiniDump.CallbackRoutine = CExceptionReport::miniDumpCallback;
cbMiniDump.CallbackParam = 0;
MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
hFile,
MiniDumpNormal,
m_pExceptionInfo ? &eInfo : NULL,
NULL,
&cbMiniDump);

Windbg 分析dmp 文件

配置Windbg

dmp 文件需要用Windbg 来分析.Windbg 是一种居家旅行杀人越货的必备良药.

猛击下面的链接获取Windbg:[1] 或者到 172.16.0.2 上找一下.

按 Ctrl+S 设置Symbol Path,即设置 PDB调试信息路径,有了PDB中的符号信息我们才能 将崩溃时的指令转换为函数名.


图片1

如上图 Symbol Path 有两个,被分号分隔.

一个是MS 的,MS 的推荐你设置一下,这样能看到系统DLL 的符号.不过设置了之后会下载不少的符号文件,比较慢,如果你受不了了的话就不设置啰.你也可以偶尔设置一下,偶尔让它下载一点,这样生活更轻松.

另一个必须的,是编译时生成的pdb 文件所在的路径.

symsrv*symsrv.dll*e:\localsymbols*http://msdl.microsoft.com/download/symbols;
E:\ccroot\zyn_KDM5.0\KDM5.0_VOB\80-OtherCu\thirdsdk\coredump\crashrpttest\src\Release

分析dmp 文件

analyze -v 分析

用以下代码进行测试.崩溃地址会在0x77b92f4a(不同机器会不同).

CCrashRptTestDlg::OnOK 中调用TestUnHandledException,在TestUnHandledException 中有如下会导致崩溃的代码

TCHAR* p = 0;
_tcsncpy(p,_T("sdf"),3);

Ctrl+D 载入dmp 文件.

按ALT+1 调出Command View

在左下角输入

!analyze -v

图片1

然后按Enter 键.得到下面结果.崩溃地址0x77b92f4a,崩溃原因c0000005 (Access violation).

关键是看STACK TEXT, 从下往上看.

可以发现是OnOK 调用了TestUnHandledException, TestUnHandledException 调用了_tcsncpy, 最后在msvcrt::mbsnbcpy 的地方挂了.其中CrashRptTestDlg.cpp @ 178 行号是大致的,不精确的(应该是Release 优化的原因).

……//以上省略若干.
EXCEPTION_RECORD: ffffffff -- (.exr ffffffffffffffff)
ExceptionAddress: 77b92f4a (msvcrt!_mbsnbcpy+0x0000004f)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000001
Parameter[1]: 00000000
Attempt to write to address 00000000
LAST_CONTROL_TRANSFER: from 0040155f to 77b92f4a
STACK_TEXT(第一列是函数所压栈地址,第二列是函数地址,后面是三个参数值)
0013f7c4 00401c55 00000000 00404080 00000003 msvcrt!mbsnbcpy+0x4f
0013f7d8 00401b47 00000000 00404080 00000003 CrashRptTest!_tcsncpy+0x15 [tchar.h @ 786]
0013f848 0040161b 0000000c 0013fe8c 0013f868 CrashRptTest!TestUnHandledException+0x11c
[……testhelper.cpp @ 151]
0013f858 7552e938 00000000 754e650c 0013f89c CrashRptTest!CCrashRptTestDlg::OnOK+0xe
[……CrashRptTestDlg.cpp @ 178]
……//以下省略若干.

无法得到函数名时的分析

以下内容来自 <<windows用户态程序高效排错>>

  • 用~*kb打印所有线程的callstack.找到UnHandledExceptionFilter.
ChildEBP RetAddr Args to Child
0012f74c 7c821b74 77e99ea d0000144 00000004 ntdll!KiFastSystemCallRet
0012f750 77e999ea d0000144 00000004 00000000 ntdll!ZwRaiseHardError+0xc
0012f9bc 004339be 0012fa08 7ffdd000 0044c4d8 kernel32!UnHandledExceptionFilter+0x4b4

UnHandledExceptionFilter 第一个参数 0012fa08 保存的就是异常信息和异常上下文地址:

  • 使用dd查看0x0012fa08
0:000> dd 0x0012fa08
0012fa08 0012faf4 0012fb10 0012fa37 7c82eeb2

0012faf4 保存了异常信息,如下文所述可用.exr查看之.

0012fb10 保存了异常上下文,如下文所述可用.cxr切换之.

  • 使用.exr加上异常信息地址打印出异常的信息
0:000> .exr 0012faf4
ExceptionAddress: 0041a5a8 (release_crash!main+0x00000028)
ExceptionCode: c0000005(Access violation)
ExceptionFlags: 0000000
NumberParameters: 2
Pararmeter[0]: 00000001
Pararmeter[1]: 00000000
Attempt to write to address 00000000
  • 用.cxr加上异常上下文地址来切换上下文.
0:000 .cxr 0012fb10
eax=00000000 ebx=7ffde000 ecx=00000000 ......
release_crash!main+0x28:
0041a5a8 c60000 mov byte ptr [eax],0x0 ds:0023:00000000=??
  • 上下文切换完成后,可以用kb命令重新打印出该上下文的callstack,就可以看到异常发生时候的状态:
*** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr Args to Child
0012fedc 00427c90 00000001 00361748 003617d0 release_crash!main+0x28[c:\src\release_crash.cpp@51]
0012ffc0 77e523cd 00000000 00000000 7ffde000 release_crash!mainCRTStartup+0x170
0012fff0 00000000 00418b18 00000000 78746340 kernel32!BaseProcessStart+0x23
(秩名)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www1.rosoo.net/a/201310/16757.html]
本文出处:Wiki 作者:秩名
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容