猎捕异常(Exceptions)

 

猎捕异常(Exceptions

准备

这一节的内容需要前面讲过的一些的基本知识,请先阅读有关的内容再继续吧。有关异常(Exceptions)的知识,可以看我有关的发表的文章。

介绍:

我想是时候再写一点有关使用WinDbg 来解决问题的文章了。我的大部分时间是花费在大量的web应用程序中查找异常。所以我想,说说异常是一个很好的话题。前面说过OutOfMemory Exceptions,我现在想扩展这个系列,让它更普遍一点。我的工作中经常会出现下面两个场景:

1)  客户报告说他们第二次看到了“服务器页面错误”的异常被显示在页面上,黄页又出现了。

2)  性能很差,当我研究一下时,发现那里有好几吨的异常被抛出来了。

在这一章中我将讲述怎么样来研究由应用程序抛出的异常。

从哪里开始

好的,你有一个web的应用程序,你已经监视并相信有很多的异常产生。你已经有了该进程的dump文件,你已经准备好开始研究,你从哪里开始呢?

!dumpallexceptions (!dae)

如果你的应用程序是运行在.net 1.1 下面,你可以使用!dumpallexceptions!dae)来列出所有仍然在堆上的异常。记住,这些异常也是托管对象,他们也会被垃圾收集的。那意味着我们看到的异常是仍然在内存里面的,并不是从程序开始运行就有的每一个异常。你可以去运行这个命令,在命令的输出中,如果可能,他会给出每个异常的调用堆栈,每一个异常的调用堆栈可能是不相同的,136NullReferenceExceptions可能有20不同的调用堆栈。

不幸的,在.net 2.0 中,这个命令没有了,要得到相同的结果可以使用如下的方式:

0:000> !dumpheap -type Exception -stat

------------------------------

Heap 0

total 79 objects

------------------------------

Heap 1

total 76 objects

------------------------------

……

total 338 objects

Statistics:

      MT    Count    TotalSize Class Name

790ff624        3           36 System.Text.DecoderExceptionFallback

790ff5d8        3           36 System.Text.EncoderExceptionFallback

790f9ad4        1           72 System.ExecutionEngineException

790f9a30        1           72 System.StackOverflowException

Total 338 objects

应该过滤掉什么

当分析的时候,知道那些没有用的信息要过滤掉是非常有用的。

过去现在 的异常

有三种类型的异常是在工作进程被创建的时候就有的。这就是说你总是会看见他们在堆上,即使他们没有被抛出来。

System.ExecutionEngineException

System.StackOverflowException

System.OutOfMemoryException

那么,为什么他们会被创建,即使没有抛出他们呢? 猜猜看。

答案非常简单,你会运行进入这样一种状况:当你想创建他们的时候,你已经不能创建他们了。比如,当你已经没有可用内存了,在也不能分配最小的一个字符串的时候,你怎么还能分配足够的内存来创建一个新的异常呢?

为了避免那种情况的发生,在堆上就有一个这样的异常先存在了。你可以忽略它们。所以,当你遇到ExecutionEngineExceptions OutOfMemoryExceptions,你肯定可以找到StackOverflowException,如果你运行 !clrstack ,发现有超过200多行,这或多或少就是你的问题了。

System.Threading.ThreadAbortException

通常当你看到这样一个ThreadAbortExceptions的异常,是因为你调用了Response.Redirect

无论什么时候你调用了Response.Redirect,都会导致调用Response.End,进程会被提前结束。导致System.Threading.ThreadAbortException异常,看看下面的堆栈:

SP        IP       Function

1ED6F37C 793D74D0

mscorlib_ni!System.Threading.Thread.Abort(System.Object)+0x2c

1ED6F390 6600CA8C

System_Web_ni!System.Web.HttpResponse.End()+0x5c

1ED6F3A4 6600B8C3

System_Web_ni!System.Web.HttpResponse.Redirect(System.String, Boolean)+0x1f3

1ED6F3B8 6600B6B7

System_Web_ni!System.Web.HttpResponse.Redirect(System.String)+0x7

1ED6F3BC 1DDD2E1D

Company_Web!Company.Web.UI.Page.RedirectToPreviousPage()+0x125

我并没有说你可以忽略所有的System.Threading.ThreadAbortExceptions。即使你没有理由相信ThreadAbortExceptions是一个主要的原因,拿它来研究一下,有时候是一个好主意。花一两分钟来检查一下在Response.Redirect下面有调用Response.End。有足够的统计信息让你证明是重定向(Redirects)导致了ThreadAbortExceptions,你可以忽略,继续向下看。

检查异常

我们想看看System.Data.SqlClient.SqlException的调用堆栈,首先我们需要的是它的地址,你可能还记得,很容易的来获得:

0:000> !dumpheap -type System.Data.SqlClient.SqlException

……….

------------------------------

Heap 2

Address       MT     Size

0b62cf38 653c8d04       76    

total 1 objects

------------------------------

……..

total 1 objects

Statistics:

      MT    Count    TotalSize Class Name

653c8d04        1           76 System.Data.SqlClient.SqlException

Total 1 objects

现在我们有了异常的地址,为了研究这个异常,我们可以使用 !dumpobject,但我先介绍另外一个命令

!printexception (!pe)

0:000> !pe 0b62cf38

Exception object: 0b62cf38

Exception type: System.Data.SqlClient.SqlException

Message: Transaction (Process ID 96) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

InnerException:

StackTrace (generated):

    SP       IP       Function

1DACECA8 1D0A4D4C

Company_Database_1d3c0000!Company.Database.AuditTrail.Write(System.String, System.Guid, System.String)+0x194

1DACED6C 1D0A4B98

Company_Database_1d3c0000!Company.Database.AuditTrail.AddEntry(EntryType, System.Guid, System.String)+0x48

     …………..

StackTraceString:

HResult: 80131904

The current thread is unmanaged

真是一个好伙计,它还把调用堆栈给了我们。(并不总是这样,因为调用堆栈可能已经超出了范围)。

!dumpobject (!do)仍然有用

我并没有说!printexception可以完全代替!dumpobject来检查异常。!Printexception会把异常格式为一个标准的模版,因为一些异常可能会包含更多的信息,我们还需要!dumpobject。那个SqlException 有一个属性叫_errors,是System.Data.SqlClient.SqlErrorCollection类型的,我们看那个可能会有用,但上面并没有列出来,所以我们要用!dumpobject来查看。

0:000> !do 0b62cf38

Name: System.Data.SqlClient.SqlException

MethodTable: 653c8d04

EEClass: 6540a0d0

Size: 76(0x4c) bytes

(C:\WINDOWS\assembly\GAC_32\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll)

Fields:

      MT    Field   Offset                 Type VT     Attr    Value Name

790fdb60  40000bf       34         System.Int32  1 instance        0 _remoteStackIndex

790f8a7c  40000c0       2c        System.Object  0 instance 00000000 _dynamicMethods

790fdb60  40000c1       38         System.Int32  1 instance -2146232060 _HResult

790f9244  40000c2       30        System.String  0 instance 00000000 _source

790fcfa4  40000c3       3c        System.IntPtr  1 instance        0 _xptrs

790fdb60  40000c4       40         System.Int32  1 instance -532459699 _xcode

653c8b28  40017e0       44 ...qlErrorCollection  0 instance 0b62cd90 _errors

我们有了,我们可以使用 !dumpobject来进行更深入的研究。

内部错误

如果我们找个HttpUnhandledExceptions来看看,会发现它有一个内存错误,它会告诉我们更多的信息。

0:000> !pe 10544f64

Exception object: 10544f64

Exception type: System.Web.HttpUnhandledException

Message:

InnerException: System.NullReferenceException, use !PrintException 10544df8 to see more

StackTrace (generated):

    SP       IP       Function

1E3BE1D8 6614FDB2

System_Web_ni!System.Web.UI.Page.HandleError(System.Exception)+0x3e6

1E3BE220 6615681A

System_Web_ni!System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)+0x1b3a

那意味着System.NullReferenceException导致了我们正在研究的HttpUnhandledException异常的发生。如果我们想要找出根本的原因我们还需要看看这个内部错误。

其他信息

在我的“高级命令”一文中讲了 .foreach 命令。这个命令很好用,例如,你想看看System.ArgumentNullExceptions的调用堆栈,除了手动的每次列举所有的这些异常,你可以把他们一次性的列出来,然后看调用堆栈。

0:000> .foreach(myVariable {!dumpheap -type System.ArgumentNullException -short}){!pe myVariable;.echo *************}

Exception object: 03c6fbb8

Exception type: System.ArgumentNullException

Message: Value cannot be null.

InnerException:

StackTrace (generated):

    SP       IP       Function

1F15E3E0 795FC73C

mscorlib_ni!System.Guid..ctor(System.String)+0x2a14bc

1F15E43C 1D0AB97F

Foo_1cfd0000!Pages.PersonHomePage.Page_Load(System.Object, System.EventArgs)+0x77

 

StackTraceString:

HResult: 80004003

The current thread is unmanaged

*************

Exception object: 08378e24

Exception type: System.ArgumentNullException

Message: Value cannot be null.

InnerException:

StackTrace (generated):

    SP       IP       Function

1CE6EC60 795FC73C

mscorlib_ni!System.Guid..ctor(System.String)+0x2a14bc

1CE6ECBC 1D0AB97F

Foo_1cfd0000!Pages.PersonHomePage.Page_Load(System.Object, System.EventArgs)+0x77

 

StackTraceString:

HResult: 80004003

The current thread is unmanaged

*************

Exception object: 084c0b30

Exception type: System.ArgumentNullException

Message: Value cannot be null.

…………………

 

 

posted @ 2008-02-02 12:39  softfair  阅读(1256)  评论(1编辑  收藏  举报