wenmo8 发布的文章

问题的引出

我在调试某个崩溃问题时,要跟踪clr的栈,于是,我先执行了指令.loadby sos clrjit,没有报错,然后我又执行!clrstack,结果却有如下输出:
0:000:x86> !clrstack
CLRDLL: Consider using ".cordll -lp <path>" command to specify .NET runtime directory.
Failed to load data access DLL, 0x80004005
Verify that 1) you have a recent build of the debugger (6.2.14 or newer)
            2) the file mscordacwks.dll that matches your version of clr.dll is
                in the version directory or on the symbol path
            3) or, if you are debugging a dump file, verify that the file
                mscordacwks_<arch>_<arch>_<version>.dll is on your symbol path.
            4) you are debugging on supported cross platform architecture as
                the dump file. For example, an ARM dump file must be debugged
                on an X86 or an ARM machine; an AMD64 dump file must be
                debugged on an AMD64 machine.

You can also run the debugger command .cordll to control the debugger's
load of mscordacwks.dll.  .cordll -ve -u -l will do a verbose reload.
If that succeeds, the SOS command should work on retry.

If you are debugging a minidump, you need to make sure that your executable
path is pointing to clr.dll as well.
也就是说执行失败了,看不了栈,根据输出建议执行.cordll -ve -u -l,有如下输出
0:000:x86> .cordll -ve -u -l
CLRDLL: C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscordacwks.dll:4.8.4042.00 f:8
doesn't match desired version 4.7.3132.00 f:8
CLRDLL: Unable to find mscordacwks_x86_x86_4.7.3132.00.dll by mscorwks search
CLRDLL: Unable to find 'mscordacwks_x86_x86_4.7.3132.00.dll' on the path
CLRDLL: Unable to get version info for 'f:\debug_symbol\symbols32\clr.dll\5B5543296ee000\mscordacwks_x86_x86_4.7.3132.00.dll', Win32 error 0n87
Cannot Automatically load SOS
CLRDLL: ERROR: Unable to load DLL mscordacwks_x86_x86_4.7.3132.00.dll, Win32 error 0n87
CLRDLL: Consider using ".cordll -lp <path>" command to specify .NET runtime directory.
CLR DLL status: ERROR: Unable to load DLL mscordacwks_x86_x86_4.7.3132.00.dll, Win32 error 0n87
傻子都知道发生了什么问题,就是没有“mscordacwks_x86_x86_4.7.3132.00.dll”和"SOS_x86_x86_4.7.3132.00.dll",由于我机子是联网的,也配好了windows符号服务器,应该说在上面的过程中应该已经下载下来了,现在没有,只能是到出问题的机器上考这两个文件了。

考了这两个文件,按理说我可以放在任何目录,可是我想放载windbg设置的符号目录,于是我将考过来的"mscordacwks.dll"和"SOS.dll"改名为“mscordacwks_x86_x86_4.7.3132.00.dll”和"SOS_x86_x86_4.7.3132.00.dll",然后在windbg设置的符号目录下以这两个文件名新建两个子文件夹,把这两个文件分别考到对应的子文件夹,在次执行.cordll -ve -u -l,跟上次输出一样,我看了下之前Windbg自行下载的其他版本的目录,发现,在还有一级目录,如下:

 

 

也就是说,我也还要需要在建立一级子目录,可是这个目录名称我该用什么呢,瞎折腾一阵,我后来注意到.cordll -ve -u -l的输出有这么一句:

0:000:x86> .cordll -ve -u -l
CLRDLL: C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscordacwks.dll:4.8.4042.00 f:8
doesn't match desired version 4.7.3132.00 f:8
CLRDLL: Unable to find mscordacwks_x86_x86_4.7.3132.00.dll by mscorwks search
CLRDLL: Unable to find 'mscordacwks_x86_x86_4.7.3132.00.dll' on the path
CLRDLL: Unable to get version info for 'f:\debug_symbol\symbols32\clr.dll\5B5543296ee000\mscordacwks_x86_x86_4.7.3132.00.dll', Win32 error 0n87
Cannot Automatically load SOS
CLRDLL: ERROR: Unable to load DLL mscordacwks_x86_x86_4.7.3132.00.dll, Win32 error 0n87
CLRDLL: Consider using ".cordll -lp <path>" command to specify .NET runtime directory.
CLR DLL status: ERROR: Unable to load DLL mscordacwks_x86_x86_4.7.3132.00.dll, Win32 error 0n87

“CLRDLL: Unable to get version info for 'f:\debug_symbol\symbols32\clr.dll\5B5543296ee000\mscordacwks_x86_x86_4.7.3132.00.dll"这里有个”5B5543296ee000“,于是我也在对应的目录下建立子文件夹”5B5543296ee000“,然后把两个文件考进去,再次执行.cordll -ve -u -l

 0:000:x86> .cordll -ve -u -l
CLRDLL: C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscordacwks.dll:4.8.4042.00 f:8
doesn't match desired version 4.7.3132.00 f:8
SYMSRV:  BYINDEX: 0x36
         f:\debug_symbol\symbols32
         mscordacwks_x86_x86_4.7.3132.00.dll
         5B5543296ee000
SYMSRV:  PATH: f:\debug_symbol\symbols32\mscordacwks_x86_x86_4.7.3132.00.dll\5B5543296ee000\mscordacwks_x86_x86_4.7.3132.00.dll
SYMSRV:  RESULT: 0x00000000
DBGHELP: f:\debug_symbol\symbols32\mscordacwks_x86_x86_4.7.3132.00.dll\5B5543296ee000\mscordacwks_x86_x86_4.7.3132.00.dll - OK
CLRDLL: Loaded DLL f:\debug_symbol\symbols32\mscordacwks_x86_x86_4.7.3132.00.dll\5B5543296ee000\mscordacwks_x86_x86_4.7.3132.00.dll
SYMSRV:  BYINDEX: 0x37
         f:\debug_symbol\symbols32
         SOS_x86_x86_4.7.3132.00.dll
         5B5543296ee000
SYMSRV:  PATH: f:\debug_symbol\symbols32\SOS_x86_x86_4.7.3132.00.dll\5B5543296ee000\SOS_x86_x86_4.7.3132.00.dll
SYMSRV:  RESULT: 0x00000000
DBGHELP: f:\debug_symbol\symbols32\SOS_x86_x86_4.7.3132.00.dll\5B5543296ee000\SOS_x86_x86_4.7.3132.00.dll - OK
Automatically loaded SOS Extension
CLR DLL status: Loaded DLL f:\debug_symbol\symbols32\mscordacwks_x86_x86_4.7.3132.00.dll\5B5543296ee000\mscordacwks_x86_x86_4.7.3132.00.dll
也就是加载成功了。

问题---“5B5543296ee000”文件夹名称是怎么来的?

加载成功,那么问题也随之而来---““5B5543296ee000”文件夹名称是怎么来的?”。

我注意到前面失败时的输出信息里:

“CLRDLL: Unable to get version info for 'f:\debug_symbol\symbols32\clr.dll\5B5543296ee000\mscordacwks_x86_x86_4.7.3132.00.dll"

对,还是这句话,不过这次的信息的重点不是“5B5543296ee000”,而是上面标粗的"clr.dll"了,它们能放在一起,说明一点:“5B5543296ee000”和“clr.dll”有关。

打开对应目录

查看下这个文件的属性

 处了版本是一样外,没有获得其他信息,后来我想到“5B5543296ee000”是不是跟一些PE信息有关呢?

打开命令行,定位到clr.dll的目录,执行“dumpbin /headers clr.dll

我注意到,在PE的FILE_HEADER里image timestamp的值为0x5B554329,在OPTIONAL HEADER里image size的值是0x6EE000,这两个值拼接在一起就是“5B5543296ee000”。

验证结论

我们上面的结论是对的吗?只能是在找一个相同结论的例子就可以,我找了之前windbg自行下载的

 

拿“”搜索,可以看到

也是这样的存储结构

dumpbin下clr.dll

根据之前的计算方式image timestamp和image size拼接为“5D490E656ef000”,完全正确,说明上面我们得出的结论完全正确

有许多选项可用于控制符号的加载和使用方式。这些选项可以通过多种方式设置。

下表列出了这些符号选项:

FlagOption NameDefault in debuggerDefault in DBH

0x1

SYMOPT_CASE_INSENSITIVE

On

On

0x2

SYMOPT_UNDNAME

On

On

0x4

SYMOPT_DEFERRED_LOADS

On

Off

0x8

SYMOPT_NO_CPP

Off

Off

0x10

SYMOPT_LOAD_LINES

Off in KD and CDB

On in WinDbg

On

0x20

SYMOPT_OMAP_FIND_NEAREST

On

Off

0x40

SYMOPT_LOAD_ANYTHING

Off

Off

0x80

SYMOPT_IGNORE_CVREC

Off

Off

0x100

SYMOPT_NO_UNQUALIFIED_LOADS

Off

Off

0x200

SYMOPT_FAIL_CRITICAL_ERRORS

On

Off

0x400

SYMOPT_EXACT_SYMBOLS

Off

On

0x800

SYMOPT_ALLOW_ABSOLUTE_SYMBOLS

Off

On

0x1000

SYMOPT_IGNORE_NT_SYMPATH

Off

Off

0x2000

SYMOPT_INCLUDE_32BIT_MODULES

Off

Off

0x4000

SYMOPT_PUBLICS_ONLY

Off

Off

0x8000

SYMOPT_NO_PUBLICS

Off

Off

0x10000

SYMOPT_AUTO_PUBLICS

On

On

0x20000

SYMOPT_NO_IMAGE_SEARCH

On

Off

0x40000

SYMOPT_SECURE

Off

Off

0x80000

SYMOPT_NO_PROMPTS

On in KD and CDB

Off in WinDbg

Off

0x80000000

SYMOPT_DEBUG

Off

Off

更改符号选项设置

.symopt(设置符号选项)命令可用于更改或显示符号选项设置。此外,可以使用许多命令行参数和命令来更改这些设置;这些参数和命令在各个SYMOPT_XXX部分中列出。
您还可以使用-sflagscommand-line选项同时控制所有设置。此选项后面可以跟一个十进制数,也可以跟一个前缀为0x的十六进制数。建议您使用十六进制,因为符号标志是以这种方式正确对齐的。使用此方法时要小心,因为它设置了整个位字段并将覆盖所有符号处理程序默认值。例如,-sflags 0x401不仅将启用SYMOPT_EXACT_SYMBOLS和SYMOPT_CASE_INSENSITIVE,而且还将禁用默认情况下正常打开的所有其他选项!
当这些程序在没有任何与符号相关的命令行选项的情况下启动时,WinDbg中的总标志位的默认值为0x30237,CDB和KD中的默认值为0xB0227,DBH工具中的默认值为0x10C13。

SYMOPT_CASE_INSENSITIVE

此符号选项导致对符号名的所有搜索都不区分大小写。默认情况下,在所有调试器中都启用此选项。调试器运行后,可以分别使用.symopt+0x1或.symopt-0x1来打开或关闭它。在DBH中,默认情况下启用此选项。一旦DBH运行,就可以分别使用symopt+1或symopt-1来打开或关闭它。

SYMOPT_UNDNAME

这个符号选择公共符号的名称在显示时被理解,原因搜索符号名称以忽略符号装饰。私人符号名称从来没有装饰过,请注意此选项是否有效。此选项适用于所有调试器。当调试器运行时,它可以通过SYMOPT+0x2或SYMOPT-0x2分别打开或关闭。这个选项在DBH中是默认打开的。如果使用了-d选项,就取消了。一旦DBH运行,它可以通过SYMOPT+2或SYMOPT+2打开或关闭。

SYMOPT_DEFERRED_LOADS

此符号选项称为延迟符号加载或延迟符号加载。当它处于活动状态时,在加载目标模块时不会实际加载符号。相反,调试器根据需要加载符号。默认情况下,在所有调试器中都启用此选项。在CDB和KD中,-s选项将关闭此选项。也可以在CDB中使用tools.ini文件中的LazyLoad变量来关闭它。调试器运行后,可以分别使用.symopt+0x4或.symopt-0x4来打开或关闭此选项。默认情况下,在DBH中此选项处于禁用状态。一旦DBH运行,就可以分别使用symopt+4或symopt-4来打开或关闭它。

SYMOPT_NO_CPP

这个符号选项关闭C++翻译。设置此符号选项时,所有符号中的::将替换为__ 。默认情况下,在所有调试器中都禁用此选项。它可以通过使用-snc选项激活。调试程序运行后,可以分别使用.symopt+0x8或.symopt-0x8来打开或关闭它。默认情况下,在DBH中此选项处于禁用状态。一旦DBH运行,就可以分别使用symopt+8或symopt-8来打开或关闭它。

SYMOPT_LOAD_LINES

此符号选项允许从源文件中读取行号信息。必须启用此选项,源调试才能正常工作。在KD和CDB中,此选项默认为关闭;在WinDbg中,此选项默认为打开。在CDB和KD中,-lines命令行选项将启用此选项。调试程序运行后,可以分别使用.symopt+0x10或.symopt-0x10来打开或关闭它。也可以使用.lines(Toggle Source Line Support)命令来打开和关闭它。在DBH中,默认情况下启用此选项。一旦DBH运行,就可以分别使用symopt+10或symopt-10来打开或关闭它。

SYMOPT_OMAP_FIND_NEAREST

当代码已优化且预期位置没有符号时,此选项将使用最近的符号。默认情况下,在所有调试器中都启用此选项。调试器运行后,可以分别使用.symopt+0x20或.symopt-0x20来打开或关闭它。在DBH中,默认情况下启用此选项。运行DBH之后,可以分别使用symopt+20或symopt-20来打开或关闭它。

SYMOPT_LOAD_ANYTHING

此符号选项可减少符号处理程序在尝试匹配符号时的挑剔程度。默认情况下,在所有调试器中都禁用此选项。调试程序运行后,可以分别使用.symopt+0x40或.symopt-0x40来打开或关闭它。默认情况下,在DBH中此选项处于禁用状态。一旦DBH运行,就可以分别使用symopt+40或symopt-40来打开或关闭它。

SYMOPT_IGNORE_CVREC

此符号选项使符号处理程序在搜索符号时忽略加载的image header中的CV记录。默认情况下,在所有调试器中都禁用此选项。它可以通过使用-sicv命令行选项激活。调试程序运行后,可以分别使用.symopt+0x80或.symopt-0x80来打开或关闭它。默认情况下,在DBH中此选项处于禁用状态。一旦DBH运行,就可以分别使用symopt+80或symopt-80来打开或关闭它。

SYMOPT_NO_UNQUALIFIED_LOADS

此符号选项禁用符号处理程序自动加载模块。当设置此选项并且调试器尝试匹配符号时,它将只搜索已加载的模块。此选项可用于防止错误输入符号名称。通常,键入错误的符号将导致调试器在搜索所有卸载的符号文件时暂停。当此选项处于活动状态时,在加载的模块中找不到键入错误的符号,然后搜索将终止。默认情况下,在所有调试器中都禁用此选项。它可以通过使用-snul命令行选项激活。调试器运行后,可以分别使用.symopt+0x100或.symopt-0x100来打开或关闭它。默认情况下,在DBH中此选项处于禁用状态。运行DBH之后,可以分别使用symopt+100或symopt-100来打开或关闭它。

SYMOPT_FAIL_CRITICAL_ERRORS

此符号选项将禁止显示文件访问错误对话框。如果禁用此选项,则在符号加载期间遇到的文件访问错误(如“驱动器未就绪”)将导致出现对话框。如果启用此选项,则这些框将被抑制,所有访问错误都将收到“失败”响应。默认情况下,在所有调试器中都启用此选项。可以使用-sdce命令行选项停用它。调试程序运行后,可以分别使用.symopt+0x200或.symopt-0x200来打开或关闭它。默认情况下,在DBH中此选项处于禁用状态。运行DBH之后,可以分别使用symopt+200或symopt-200来打开或关闭它。

SYMOPT_EXACT_SYMBOLS

此符号选项使调试器对所有符号文件执行严格的求值。启用此选项时,即使符号文件与符号处理程序的期望值之间存在微小差异,也会导致忽略符号。默认情况下,在所有调试器中都禁用此选项。它可以通过使用-ses命令行选项激活。调试程序运行后,可以分别使用.symopt+0x400或.symopt-0x400来打开或关闭它。-failinc命令行选项还可以打开SYMOPT_EXACT_SYMBOLS。此外,如果正在调试用户模式minidump或内核模式minidump,-failinc将阻止调试器加载其映像无法映射的任何模块。在DBH中,默认情况下启用此选项。一旦DBH运行,就可以分别使用symopt+400或symopt-400来打开或关闭它。

SYMOPT_ALLOW_ABSOLUTE_SYMBOLS

此符号选项允许DbgHelp读取存储在内存中绝对地址的符号。在绝大多数情况下不需要这种选择。默认情况下,在所有调试器中都禁用此选项。调试程序运行后,可以分别使用.symopt+0x800或.symopt-0x800来打开或关闭它。在DBH中,默认情况下启用此选项。一旦DBH运行,就可以分别使用symopt+800或symopt-800来打开或关闭它。

SYMOPT_IGNORE_NT_SYMPATH

此符号选项使调试器忽略符号路径和可执行映像路径的环境变量设置。默认情况下,在所有调试器中都禁用此选项。它可以通过使用-sins命令行选项激活。但是,一旦调试器运行,它就不能由.symopt控制,因为环境变量只在启动时读取。此选项在DBH中默认为禁用,在所有情况下都被DBH忽略。

SYMOPT_PUBLICS_ONLY

此符号选项使DbgHelp忽略私有符号数据,并仅搜索公共符号表以获取符号信息。这将在添加对这些类型的支持之前模拟DbgHelp的行为。默认情况下,在所有调试器中都禁用此选项。调试程序运行后,可以分别使用.symopt+0x4000或.symopt-0x4000来打开或关闭它。默认情况下,在DBH中此选项处于禁用状态。如果使用-d命令行选项,则该选项处于启用状态。一旦DBH运行,就可以分别使用symopt+4000或symopt-4000来打开或关闭它。

SYMOPT_NO_PUBLICS

此符号选项防止DbgHelp搜索公共符号表。这可以使符号枚举和符号搜索更快。如果您只关心搜索速度,则SYMOPT_AUTO_PUBLICS选项通常比此选项更可取。有关公共符号表的信息。默认情况下,在所有调试器中都禁用此选项。调试程序运行后,可以分别使用.symopt+0x8000或.symopt-0x8000来打开或关闭它。默认情况下,在DBH中此选项处于禁用状态。DBH运行后,可以分别使用symopt+8000或symopt-8000来打开或关闭它。

SYMOPT_AUTO_PUBLICS

此符号选项使DbgHelp只能作为最后的手段在.pdb文件中搜索公共符号表。如果在搜索专用符号数据时发现任何匹配项,则不会搜索公用符号。这提高了符号搜索速度。默认情况下,在所有调试器中都启用此选项。可以使用-sup命令行选项停用它。调试程序运行后,可以分别使用.symopt+0x10000或.symopt-0x10000来打开或关闭它。在DBH中,默认情况下启用此选项。如果使用-d命令行选项,则该选项将关闭。运行DBH之后,可以分别使用symopt+10000或symopt-10000来打开或关闭它。

SYMOPT_NO_IMAGE_SEARCH

此符号选项防止DbgHelp在加载符号时在磁盘中搜索image的副本。默认情况下,在所有调试器中都启用此选项。调试程序运行后,可以分别使用.symopt+0x20000或.symopt-0x20000来打开或关闭它。默认情况下,在DBH中此选项处于禁用状态。一旦DBH运行,就可以分别使用symopt+20000或symopt-20000来打开或关闭它。

如何在调试会话中找到调试对象中使用的.NET运行时版本?以自动/脚本方式,不使用调试器扩展或符号?

答案:

!for_each_module .if ( ($sicmp( "@#ModuleName" , "mscorwks") = 0) | ($sicmp( "@#ModuleName" , "mscorsvr") = 0) | ($sicmp( "@#ModuleName" , "clr") = 0)) {.echo @#ProductVersion}

0:000:x86>  !for_each_module .if ( ($sicmp( "@#ModuleName" , "mscorwks") = 0) | ($sicmp( "@#ModuleName" , "mscorsvr") = 0) | ($sicmp( "@#ModuleName" , "clr") = 0)) {.echo @#ProductVersion}
4.7.3132.0
1a: Unable to retrieve module names (8007007e)
34: Unable to retrieve module names (8007007e)
87: Unable to retrieve module names (8007007e)
8a: Unable to retrieve module names (8007007e)
9f: Unable to retrieve module names (8007007e)
b0: Unable to retrieve module names (8007007e)
b8: Unable to retrieve module names (8007007e)
c0: Unable to retrieve module names (8007007e)
c1: Unable to retrieve module names (8007007e)
c5: Unable to retrieve module names (8007007e)
cc: Unable to retrieve module names (8007007e)
d2: Unable to retrieve module names (8007007e)

简介

让我们从我写这篇文章的原因开始。一天,一个同事让我帮他调试他遇到的问题。所以我看着他在输入代码,这时我注意到下面一行:

int test = GetLastError();

他这样做是因为他想知道错误代码,如果之前的函数失败了。他每次想知道错误代码就加上这一行。我建议他删除所有这些行并在他的监视窗口中使用@ERR伪寄存器。他不知道这是什么,在办公室里到处打听,很多人都不知道,所以我为从来没有听说过伪寄存器的人写了这篇文章。

什么是伪寄存器?

伪寄存器不是当前的硬件寄存器,而是像硬件寄存器一样显示。使用伪寄存器,您可以在调试器中看到并使用某些值(错误代码、线程信息块…)。让我们看看@ERR伪寄存器。用您最喜欢的应用程序启动调试器。在代码中放置断点,以便调试器中断执行。打开“监视”窗口(如果尚未打开)(右键单击某个空工具栏空间,然后从该列表中选择“监视”来执行此操作)。在这个监视窗口中添加@ERR。您应该在值列中看到0。现在检查一下你的代码,看看这个值。它将始终显示当前线程的GetLastError()数字。所以如果你的代码出错了,这个值就会改变。
如果你想测试这个,但是你的代码没有任何错误,我建议你放一些进去(但是不要忘记以后删除它们)。您可以插入如下内容:

FILE *fp = fopen("c:\\a_file_that_does_not_exist.txt", "r");

如果执行这一行,就会看到@ERR值变为2。转到“工具”->“错误查找”查看此错误值的含义(“如果您想知道,系统找不到指定的文件”)。像我这样懒散的流浪汉,还有像你这样聪明的小伙子/姑娘,可以把@ERR伪寄存器改成@ERR,hr。这样做会将伪寄存器的值更改为错误字符串。现在您甚至不必查找错误。我一直把“@ERR,hr”放在观察窗里。

条件表达式

伪寄存器也可用于条件表达式。要尝试此操作,请在fopen后面加上以下行:

if (fp)
{
fclose(fp);
}

在if(fp)行上设置断点。转到“编辑”->“断点”(或按Alt-F9)。选择刚才插入的断点并按“条件”按钮。在这里,您可以输入@ERR==2条件。现在启动调试器。如果fopen()由于找不到文件而失败,调试器将在此断点上中断。如果文件确实存在,调试器将不会中断,即使它遇到另一个错误(例如错误4:无法打开文件)。通过在创建后运行代码(不是步进),并在c:\中删除“AyFieleToSodoSoNothOx.Txt”文件来尝试这一点。

本文讨论如何使用Windows事件查看器获取实际崩溃的模块以及代码中崩溃的位置。示例代码是用C++编写的,以生成不同类型的崩溃,例如访问冲突和堆栈溢出。

简介

我经常听同事和QA那里听说,一个特定的崩溃很容易在客户机上重现,而不是在他们的机器上重现。这是一个棘手的问题,因为开发人员无法在客户机上调试崩溃。最终的结果是支持团队和客户之间无休止的沟通,甚至是现场会议。很少有聪明的程序员自己开发一个崩溃日志系统来确定导致崩溃的代码。很少有人会在代码中全面地实现try-catch块,以缩小问题的范围。

背景

近年来,我开始使用事件查看器检查在特定计算机上注册的各种警告和错误的日志。我注意到应用程序或程序崩溃记录在应用程序事件日志中,并且在大多数情况下都有足够的信息来获取崩溃或问题位置。事件查看器通常位于C:\ Windows\system32\eventvwr.exe中,一旦启动,就可以轻松查看应用程序事件日志。

当应用程序或程序在特定机器上崩溃时,类似的信息也会显示给用户。

如何调试崩溃?

为了更好地理解事件记录器/查看器,我决定创建一个简单的程序,当某个特定的命令行参数传递给它时,该程序将崩溃。

 

HowToFindCrashInExeCode.exe以1到4之间的数字作为参数,然后通过生成适当的异常来相应地崩溃。1号和3号生成访问冲突异常,而2号和4号分别在从属DLL和主EXE中生成StackOverflow异常。下面的两个图像显示了当程序在命令行上崩溃时,使用1作为输入参数的崩溃报告和应用程序事件日志。

 

 

 

应用程序事件日志提供给我们的重要细节是错误应用程序路径、错误模块名称和路径、异常代码以及最重要的错误偏移量。错误应用程序路径、错误模块名称和路径的目的非常明显。异常代码揭示了崩溃发生的细节和/或情况。故障偏移量是加载的故障模块内的内存位置,即它为我们提供了日志中提到的故障模块中的准确故障位置。从客户处获取应用程序事件日志后,请检查故障模块名称、路径和故障偏移量,然后在计算机上启动应用程序并将其附加到调试器。找到加载的故障模块的起始内存地址,并将故障偏移量添加到此地址。然后使用反汇编跳转到内存地址。反汇编将准确地告诉您崩溃的位置。这不是一个很酷很快就能解决问题的方法吗。上面的事件管理器日志告诉我们,错误模块是HowToFindCrashInDLLCode.dll,异常代码是0xc000005,这是访问冲突异常,错误偏移量是0x00001032。下图描述了howtoFindCrashHindllcode.dll的反汇编以及模块加载地址。

 

模块加载地址为0x73D60000,现在添加错误偏移量0x00001032。产生的内存地址是0x73D61032。跳转到此内存位置后,可以看到崩溃来自函数crashForAccessViolation,生成此崩溃的代码是pVal[0]=10;因为pVal是未实例化的整数指针。

比较有趣的点

在开发人员的机器上调试相同版本/配置/平台的程序以获得准确的错误位置是很重要的。另外,如果为程序生成了pdb,那么一旦跳转到错误偏移量,就可以看到反汇编和源代码。不需要在禁用优化的情况下构建程序,因为该程序的错误偏移量是通用的,开发人员需要自己做一些基本的数学计算。有时,崩溃模块是系统DLL之一,例如kernel.DLL、nt.DLL或msvcr100.DLL,然后按上述方法检查故障偏移量,并检查异常代码。这两件事将帮助您猜测代码中的问题,例如STL或CRT库抛出一些异常,如逻辑错误,有时会生成未处理的异常,这些异常会被系统DLL捕获。