现在的位置: 首页 > 综合 > 正文

ASP.NET Debug系列之三:Crash调试

2012年12月27日 ⁄ 综合 ⁄ 共 3753字 ⁄ 字号 评论关闭

原文作者:Tess
原文链接:http://blogs.msdn.com/tess/archive/2008/02/11/net-debugging-demos-lab-2-crash-review.aspx
译者:Wencui  http://www.cnblogs.com/David-Qian

前两天翻译了Tess Debug系列的Hang调试,今天就继续和大家看看Tess的第三篇文章,Crash调试。同样,由于机器的差异,可能你的调试结果和Tess有所差异,不过这个不要紧,只要你知道其中的原理就可以了。文章的图片可能会不太清楚,如果需要,大家可以去Tess的原文上看。

ASP.NET  Debug系列之一:环境搭配 

Windbgsostinygetadplus常用命令

ASP.NET Debug系列之二:Hang调试

 

1. 问题重现

1) 浏览到http://localhost/BuggyBits/Reviews.aspx页面。

2) 点击Refresh按钮,它将使得w3wp.exe程序crash(IIS 5下是aspnet_wp.exe)。注意,如果你装有Visual Studio的话,Just-In-Time的消息将会弹出,你可以点击no按钮,此时我们不需要来实时调试。

2. 检查系统日志

打开日志(在命令行中可以输入eventvwr.exe)。日志对于不同版本的系统和IIS可能会有所不同。在系统日志中,我们可以看到这条信息:

问题1:你的日志是什么样子的?
结果1:在Tess的机器中(Windows 2003,.NET 2.0 SP1),系统日志如上所示。同时,在程序日志中, 有如下两条信息:

从第一条信息看,它像是一个nullreference的异常所致

第二条信息显示,Review的Finalize方法导致了nullreference的异常。但也有可能你看不到第二条信息,这个因不同的系统,IIS版本,.NET版本等而异。

问题2:exit code 0xe0434f4d代表着什么呢?
结果2:这个code是.NET异常的一个等价物。不管是SQL exception,nullreference exception还是FileNotFoundException,它们都将会用0xe0434f4d code来表示。

问题3:从这些日志中,你能判断是什么导致了程序的crash吗?
结果3:从程序日志的第二条信息中,我们可以很容易的看出,程序的crash是因为Finalize Review这个对象时产生的异常引起的。

3. 获取一个dump文件

1) 打开http://localhost/BuggyBits/Reviews.aspx页面,但是不要点击"Refresh"按钮。

2) 打开命令行窗口并切换到debugger tools所在的目录,然后输入:adplus -crash -pn w3wp.exe,并立即敲回车。

问题1:一个新的窗口将出现在工具栏上,这个程序是什么?
结果1:这个程序的图标是:

其实这就是debugger,它正在运行着cdb,是windbg没有UI状态下的等价物。

问题2:dubugger在等待着什么呢?
结果2:下面的一张表将显示什么样的events/exceptions adplus会监视,以及当其中的之一发生时,adplus将会做什么。

一些缩写:L=在debuglog中写日志,T=记录时间,S=记录stack,M=最小的dump,F=全的dump,E=在eventlog中写日志。如果你想adplus捕捉你想要的events,你可以自定义一个cfg文件供adplus使用。

3) 在reviews页面上点击"Refresh"按钮以重现问题。

问题1:在debugger tools目录,什么文件被创建了?
结果1:在Tess的机器上,如下文件被创建在crash文件夹(名字中含有crash_mode)下:

ADPlus_report.txt
<DIR> CDBScripts
PID-4628__W3WP.EXE__Date_02-11-2008__Time_13-14-0808.log
Process_List.txt
PID-4628__W3WP.EXE__1st_chance_AccessViolation__mini_17a4_2008-02-11_13-14-19-358_1214.dmp
PID-4628__W3WP.EXE__1st_chance_Process_Shut_Down__full_17a4_2008-02-11_13-14-31-889_1214.dmp
PID-4628__W3WP.EXE__2nd_chance_NET_CLR__full_17a4_2008-02-11_13-14-20-093_1214.dmp

从上面的文件中,我们可以看出,adplus产生了三个dump文件。第一次的access violation(NullReferenceException)将产生一个最小的dump文件。由于此exception并未被处理,于是引起了第二次的CLR exception。最终,导致了process的shutdown。其实,从三个dump文件的时间戳可以很容易的得知它们产生的顺序。

4. 使用windbg调试

1) 打开CLR exception的dump文件(xx_2nd_chance_NET_CLR_xx)。

这里有些要说明的地方。.NET中,exception是比较常见的。当我们的程序中抛出异常时,这个叫做1st chance。此时我们可以使用try/catch语句来捕获这个异常。但是,如果我们并没有及时捕获这个异常,并且这个异常不能被.NET的全局异常处理掉,它就变成了2nd异常。任何2nd的异常都将导致process的crash。

2) 设置symbols的path,并且load sos。这些都是基本步骤,前面都提到过,不再多说。

在crash mode下,由于dump是在异常发生时产生的,因而active的thread就是发生异常的thread。

问题1:你的dump中,哪个thread是active的?
结果1:active的thread是#20。如果我们用!threads命令来查看thread的信息的话,我们可以发现#20 thread是Finalizer thread。

5. 检查callstack以及exception

1) 检查native以及managed callstack。

问题1:这个thread是什么类型的thread?
结果1:从kb 2000命令中,我们可以看出它是Finalizer thread,但是由于它是发生异常的thread,我们无法使用!clrstack来查看更多的managed callstack的信息。

问题2:这个thread正在做什么?
结果2:它正在回收某些objects,在回收的过程中抛出了异常,并最终成为2nd异常。

2) 检查exception

!pe

!pe/!PrintException 命令将打印出当前堆栈上正在被抛出的exception。

问题1:这是个什么类型的exception?
结果1:这个是空引用的异常 NullReferenceException。

注意,在大多数的情况下,我们很难得到2nd exception发生前的stack trace。这样的话,我们需要使用别的命令来找出更多的关于1st exception的信息。

3) 在堆栈上查看更多关于1st exception的信息

问题1:1st exception的地址是什么?
结果1:在上图中,我们可以看到2nd exception的地址是0x2f0cb3c,因而,我们比较下图可知,1st exception的地址是 0x6f1e5e4。

4) 查看1st exception的信息以及callstack。

问题1:exception是在哪个方法中被抛出的?
结果1:在Review class的Finalizer方法中。

问题2:什么object正在被回收?
结果2:是Review object。

问题3:通常情况下,当异常被抛出时,我们如果没有使用try/catch块的话,它将会被ASP.NET全局异常来处理,同时显示给用户一个错误提示页面,为什么这个异常并不能被处理,而致使process的crash呢?
结果3:ASP.NET的全局异常处理仅仅用来捕获处理requests时的exception。在我们的例子中,异常是发生在Finalizer thread中的,它并不属于某一个特定用户,因而也无法将错误信息显示给某一特定用户。这样就导致了process的crash。

6. 验证我们的代码

1) 打开Review.cs,查看它的Finalizer方法。

问题1:哪一行代码会抛出exception?
结果1:从代码中,我们可以很容易的看出,当string非空时,它被赋值null,这将导致NullReferenceException。如果我们并不能查看源代码,我们可以通过!u命令来查看JIT生成的汇编代码。这里暂不做讨论,在以后的文章中再做解释。

同样,这篇文章非常的长,也翻译了很长时间,希望能够对大家有所帮助 :)

Have a nice day!

抱歉!评论已关闭.