从头开始使用WinDbg 第二部分

 

从头开始使用WinDbg 第二部分

这是前面富有想像力的名字为“从头开始使用WinDbg 第一部分”的内容的继续,我已经假设你已经读过了它,因此,如果你还没有阅读过,我强烈建议你先读一下。我们会继续使用前面的dump例子,我非常乐意从上次我们停下来的地方继续前进。

 

更多有用的命令

上一次我们使用了一些来自SOS扩展的非常好的命令来查看运行堆栈调用、请求、cpu中运行着的进程等等。我们也向更深处挖掘了一些信息,我们会继续使用这些命令,还会有一些其他命令。

 

!dumpstackobjects (!dso)

假设我们正在查看一个特殊的进程,我们想要知道被当前栈引用的所有托管对象。我们有什么方法可以这么做呢? 非常肯定我们有这样一个命令:!dumpstackobjects 简称:dso

我们运行这个命令,我们就会看到当前进程的当前调用堆栈的所有对象的列表。输出会像下面这样:

因为篇幅关系仅列了一部份。

0:050> !dso

OS Thread Id: 0x1e8c (50)

ESP/REG  Object   Name

17a9e534 0741f860 System.RuntimeType

17a9e6b8 271fdfe0 System.DirectoryServices.Protocols.LdapConnection

17a9e6bc 27246f20

System.DirectoryServices.Protocols.SEC_WINNT_AUTH_IDENTITY_EX

17a9e740 271fdfe0 System.DirectoryServices.Protocols.LdapConnection

17a9e744 27246f20

System.DirectoryServices.Protocols.SEC_WINNT_AUTH_IDENTITY_EX

17a9e764 27246f20

System.DirectoryServices.Protocols.SEC_WINNT_AUTH_IDENTITY_EX

17a9e768 271fdfe0 System.DirectoryServices.Protocols.LdapConnection

17a9e780 271fdfe0 System.DirectoryServices.Protocols.LdapConnection

当我们想要看被当前进程单独引用的所有对象的时候,这个命令非常的有用。如果你想要就某一个对象,你仅需要把地址拷贝下来,然后用 !dumpoubect 命令来查看。

0:050> !do 271fdfe0

Name: System.DirectoryServices.Protocols.LdapConnection

MethodTable: 14a2040c

EEClass: 149daf08

Size: 56(0x38) bytes

(C:\WINDOWS\assembly\GAC_MSIL\System.DirectoryServices.Protocols\2.0.0.0__b03f5f7f11d50a3a\System.DirectoryServices.Protocols.dll)

Fields:

      MT    Field   Offset                 Type VT     Attr    Value Name

14a2078c  40000c3        4 ...NetworkCredential  0 instance 00000000 directoryCredential

14a2144c  40000c4        8 ...ificateCollection  0 instance 271fe018 certificatesCollection

1202af88  40000c5       10      System.TimeSpan  1 instance 271fdff0 connectionTimeOut

1466fe50  40000c6        c ...rectoryIdentifier  0 instance 271fdf14 directoryIdentifier

14a2034c  4000236       24         System.Int32  0 instance        2 connectionAuthType

14a223a4  4000237       18 ...dapSessionOptions  0 instance 271fe2d8 options

0fb896d8  4000238       28        System.IntPtr  0 instance 564180944 ldapHandle

120261c8  4000239       2c       System.Boolean  0 instance        0 disposed

120261c8  400023a       2d       System.Boolean  0 instance        0 bounded

120261c8  400023b       2e       System.Boolean  0 instance        0 needRebind

14a22084  400023e       1c ...pResponseCallback  0 instance 271fe03c fd

120261c8  4000243       2f       System.Boolean  0 instance        0 setFQDNDone

120261c8  4000244       30       System.Boolean  0 instance        1 automaticBind

120261c8  4000245       31       System.Boolean  0 instance        1 needDispose

120261c8  4000246       32       System.Boolean  0 instance        1 connected

14a2267c  4000247       20 ...s.QUERYCLIENTCERT  0 instance 271fe394 clientCertificateRoutine

0fd314bc  400023c       20 ...ections.Hashtable  0   shared   static handleTable

    >> Domain:Value  0019daf0:NotInit  11b42540:073fe504 <<

02c36ca0  400023d       24        System.Object  0   shared   static objectLock

    >> Domain:Value  0019daf0:NotInit  11b42540:073fe53c <<

0fd314bc  400023f       28 ...ections.Hashtable  0   shared   static asyncResultTable

    >> Domain:Value  0019daf0:NotInit  11b42540:073fe610 <<

14a21864  4000240       2c ...lResultsProcessor  0   shared   static partialResultsProcessor

    >> Domain:Value  0019daf0:NotInit  11b42540:073fe678 <<

12305e94  4000241       30 ....ManualResetEvent  0   shared   static waitHandle

    >> Domain:Value  0019daf0:NotInit  11b42540:073fe64c <<

14a21954  4000242       34 ...lResultsRetriever  0   shared   static retriever

    >> Domain:Value  0019daf0:NotInit  11b42540:073fe6a8 <<

!dumparray (!da)

你可能已经注意到,我们有再栈上一双对象数组。查找 System.Object[] 类型,你会看到它们。 如果你对这个数组来执行 !dumpobject 命令,你只能看到数组本身的一些信息,而不是数组的内容。必须使用 !dumparray 简称 !da 命令来查看才可以。

0:050> !do 27239b98

Name: System.Object[]

MethodTable: 02c3896c

EEClass: 02c388ec

Size: 24(0x18) bytes

Array: Rank 1, Number of elements 2, Type CLASS

Element Type: System.String

Fields:

None

 

0:050> !da 27239b98

Name: System.String[]

MethodTable: 02c3896c

EEClass: 02c388ec

Size: 24(0x18) bytes

Array: Rank 1, Number of elements 2, Type CLASS

Element Methodtable: 02c39310

[0] 272399a8

[1] 27239a44

你可以看到 !dumparray 命令给了我们多一点的信息。该数组包含了两个System.String 的元素,并且有它们的地址。所以我们可以使用  !dumpobject 来查看这些String对象。

 

!objsize

你看上面列出的数组对象的大小是24B,这里的大小仅仅是这个System.Object[] 这个对象自己的大小,仅代表这个结构的大小,并不包含它里面的内容的大小。这个System.Object[]对象包含两个String 对象,这些String 对象是独立的对象,它们有32M的大小。所以24B并不是这个数组的总大小,但它是正确的表示了System.Object[] 这个结构的大小只有24B

要取得一个对象的大小我们使用如下命令:

0:050> !objsize 27239b98

sizeof(27239b98) =          348 (       0x15c) bytes (System.Object[])

这个命令会遍历这个对象所引用的的儿子对象和孙子对象等等。这个数组的精确的尺寸是包含了它子代的对象,大小是348B

如果里面有很多的子代对象,使用 !objsize 这个命令来计算对象的大小是很慢的。你还有一点也是必须知道的,这个命令不是和你想象的这么智能的。比如:你有一个自定义按钮控件,它引用了父对象aspx 页面对像,那么你会得到aspx 页面的大小和它里面子控件的大小。这样是错误的,你得到的 objsize会非常可笑得很大,你需要使用 !dumpobject 来手动的确认一下引用的对象。

 

!dumpheap

这是另外一个非常有用的命令。只用这个命令的时候你需要加一个参数,没有参数的输出会是整个堆上的所有对象。所以我一般都加上 –stat 这个参数。它自己内部会写很多信息,但最后会有一个统计信息,下面是一段截取的输出。

0:050> !dumpheap -stat

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

Heap 0

total 2754508 objects

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

Heap 1

total 2761329 objects

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

total 5515837 objects

Statistics:

      MT    Count    TotalSize Class Name

…….

14ef4718        1           12 System.Net.HttpRequestCreator

..

它是一个按照对象类型的大小来排列的,一般strings 都会排在最后面,因为它是用的最多的。另外比较有用的参数是 –type –mt ,使用它们你能看到特别类型的对象。例如,你想看HttpRequestCreators这个对象,你可以使用如下方式:

0:050> !dumpheap -mt 14ef4718       

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

Heap 0

Address       MT     Size

0342ccf8 14ef4718       12    

total 1 objects

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

Heap 1

Address       MT     Size

total 0 objects

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

total 1 objects

Statistics:

      MT    Count    TotalSize Class Name

14ef4718        1           12 System.Net.HttpRequestCreator

他会给我们这个对象的地址,你想要更深入的了解这个对象,就可以使用 !dumpobject 这个命令。

!dumpheap –type 的工作也非常的相似,不同的是你这次是用类名,它会进行子字符串匹配,所以,如果你输入:!dumpheap -type System.Web ,你就会得到类名中包含“System.Web”的所有的类。

另外几个比较有忧的参数是 –min –max , 后面跟上一个代表对像大小的代表字节的数字。这个在你解决字符串问题时非常有用。另外,!dumpheap -stat -min 85000 会列出所有的在LOH上的对象。-min 含义是除了 85000更小的。

 

把工具拿来使用

我会是用前面介绍的这些命令来进行一些实际的演示。这个dump文件是从前面一个case中得到的。这个有问题的应用程序是运行在2个工作进程的web 园模式下。Session状态是被放在sqlServer中的。客户在测试性能的问题,对出现的问题的描述也是很模棱两可的。我有好几吨的dump要分析!因此,我大概的看看我能发现什么。一件事,我在乐意很早就开始做的是查看cache。根据客户所说,它们根本没有使用cache。但我认为通常最好再检查一下。

为了找出有多少数据被放在了cache中,我首先需要查找 System.Web.Caching.Cache 类。我运行 !dumpheap -stat -type System.Web.Caching.Cache ,注意我使用了 –stat 参数,否则我会得到很长的信息,包括 Caching.CacheKeys  Caching.CacheEntrys等,下面是输出。

0:050> !dumpheap -type System.Web.Caching.Cache -stat

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

Heap 0

total 665 objects

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

Heap 1

total 1084 objects

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

total 1749 objects

Statistics:

      MT    Count    TotalSize Class Name

123056f8        1           12 System.Web.Caching.CacheKeyComparer

1230494c        1           12 System.Web.Caching.Cache

……

……..

123063fc      832        16640 System.Web.Caching.CacheKey

12306820      732        52704 System.Web.Caching.CacheEntry

Total 1749 objects

我现在知道了 cache 对象,知道了Method Table ,我现在使用 !dumpobject 来列出这个对象

0:050> !dumpheap -mt 1230494c       

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

Heap 0

Address       MT     Size

03392d20 1230494c       12    

total 1 objects

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

Heap 1

Address       MT     Size

total 0 objects

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

total 1 objects

Statistics:

      MT    Count    TotalSize Class Name

1230494c        1           12 System.Web.Caching.Cache

Total 1 objects

现在知道了地址,那我就可以运行 !objsize 来知道它到底有多少大,花费了一些时间,因为cache很复杂,而且还有很多子对象。结果出来了。

0:050> !objsize 03392d20

sizeof(03392d20) =    266640828 (   0xfe49dbc) bytes (System.Web.Caching.Cache)

cache 266MB ,这是非常多的,但事实中,客户说它们并没有使用cache

那什么被缓存了呢?

为了抽取一些样本看看什么被缓存了,我看了看 CacheEntrys。我已经有了MethodTable,刚才在执行 !dumpheap -type System.Web.Caching.Cache –stat(前面)的时候已经有了这个MT,所以有下面的输出:

0:050> !dumpheap -mt 12306820     

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

Heap 0

Address       MT     Size

033950bc 12306820       72    

033a20d8 12306820       72    

033ac79c 12306820       72    

033da21c 12306820       72    

....etc...

03e160f8 12306820       72   

total 382 objects

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

total 732 objects

另外一个可以使用的命令,有相同的输出的,我已经给出的,当然是:

!dumpheap -type System.Web.Caching.CacheEntry

OK,让我们看看这个CacheEntrys,抽取一些样本,我执行了:

0:050> !do 03b2c674

Name: System.Web.Caching.CacheEntry

MethodTable: 12306820

EEClass: 122f6470

Size: 72(0x48) bytes

(C:\WINDOWS\assembly\GAC_32\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)

Fields:

      MT    Field   Offset                 Type VT     Attr    Value Name

02c39310  4001327        4        System.String  0 instance 03b2c600 _key

0fb8f1f8  4001328        c          System.Byte  0 instance        2 _bits

0fd3da00  4001329        8         System.Int32  0 instance -1314181915 _hashCode

02c36ca0  4001330       10        System.Object  0 instance 03b2c644 _value

120219d0  4001331       1c      System.DateTime  1 instance 03b2c690 _utcCreated

120219d0  4001332       24      System.DateTime  1 instance 03b2c698 _utcExpires

根据已经dump出来的结果,我最感兴趣的是 _value,因此我把地址拷贝下来,然后运行:

0:000> !do 03e160c8

Name: System.Web.SessionState.InProcSessionState

MethodTable: 14dbad5c

EEClass: 14e43af8

Size: 48(0x30) bytes

(C:\WINDOWS\assembly\GAC_32\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)

Fields:

      MT    Field   Offset                 Type VT     Attr    Value Name

1466c9d8  4001d89        4 ...ateItemCollection  0 instance 1a7f5438 _sessionItems

1292672c  4001d8a        8 ...ObjectsCollection  0 instance 00000000 _staticObjects

0fd3da00  4001d8b        c         System.Int32  0 instance       20 _timeout

120261c8  4001d8c       18       System.Boolean  0 instance        0 _locked

120219d0  4001d8d       1c      System.DateTime  1 instance 03e160e4 _utcLockDate

0fd3da00  4001d8e       10         System.Int32  0 instance        1 _lockCookie

1202bf60  4001d8f       24 ...ReadWriteSpinLock  1 instance 03e160ec _spinLock

0fd3da00  4001d90       14         System.Int32  0 instance        0 _flags

这里我发现了很有趣的一个东西,被缓存的东西是一个InProcSessionState对象,这个东西以前可能不知道是放在缓存里的吧。有了这个对象,那么客户说的它们使用sqlServer来保存Session是不正确的。

结果是这样的:客户临时为了概要性地检测把它改成了In-process 模式,但是忘记把它改成SqlServer模式了。

posted @ 2008-01-28 14:33  softfair  阅读(693)  评论(0编辑  收藏  举报