【转】从头开始使用WinDbg

从头开始使用WinDbg 第一部分

基本配置:

1)  sos.dll 拷贝到WinDbg的安装目录,确保你拷贝的这个dll和你想要研究问题的程序的.net 版本一致。如果你同时在.net1.1  .net2.0下,你可以把sos.dll 重命名为 sos11.dll sos20.dll 或者把它们放在不同的目录下面。

2)  创建一个文件夹,用来放置你的符号文件,例如:“C:\symbols”

3)  打开WinDbg,打开配置符号文件路径的对话框

4)  输入路径,并且下载符号文件的路径,当你的本地没有符号文件的时候,它就需要去服务器上下载。结构如下:

srv*[cache path]*[symbols path]

I'd recommend the following path:

srv*c:\symbols\public*http://msdl.microsoft.com/download/symbols

相信你已经设置好,下面开始了。

 

很有用的命令:

我会使用一个从IIS6上取来的dump文件来演示一些很有用的命令。

第一件事要做的就是加载SOS,这个命令是 .load [extension filename] 这个结构很简单。load 前面有一个  . ”。

 

.load sos

 

你现在已经有了一个很酷的带有SOS扩展的命令提示工具。扩展命令都是以 !”前置的,因此如果你想运行一个帮助命令:

 

!help

如果你刚好有两个扩展,而且分别有个命令,它们的名字是一样的,你可以这样来分开它们:

![extension name].[command]

 

!sos.help

现在你知道了如何来运行命令,那么运行help来看看,下面是给你的结果:

 

 

0:000> !help

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

SOS is a debugger extension DLL designed to aid in the debugging of managed

programs. Functions are listed by category, then roughly in order of

importance. Shortcut names for popular functions are listed in parenthesis.

Type "!help " for detailed info on that function.

 

Object Inspection                  Examining code and stacks

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

DumpObj (do)                       Threads

DumpArray (da)                     CLRStack

DumpStackObjects (dso)             IP2MD

DumpHeap                           U

DumpVC                             DumpStack

GCRoot                             EEStack

ObjSize                            GCInfo

FinalizeQueue                      EHInfo

PrintException (pe)                COMState

TraverseHeap                       BPMD

 

Examining CLR data structures      Diagnostic Utilities

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

DumpDomain                         VerifyHeap

EEHeap                             DumpLog

Name2EE                            FindAppDomain

SyncBlk                            SaveModule

DumpMT                             GCHandles

DumpClass                          GCHandleLeaks

DumpMD                             VMMap

Token2EE                           VMStat

EEVersion                          ProcInfo

DumpModule                         StopOnException (soe)

ThreadPool                         MinidumpMode

DumpAssembly                      

DumpMethodSig                      Other

DumpRuntimeTypes                   -----------------------------

DumpSig                            FAQ

RCWCleanupList

DumpIL

更多的信息你可以查看帮助,输入 !help [name of command]

 

.time

这个不是SOS扩展命令,因为明显它不是一个以“!”开头的命令。Time 这个命令会显示和时间有关系的信息。例如:系统启动到现在的时间(uptime)、进程启动到现在的时间,和在用户和核心模式下的时间总和。

 

0:000> .time

Debug session time: Tue Oct 23 08:38:35.000 2007 (GMT+1)

System Uptime: 4 days 17:48:01.906

Process Uptime: 0 days 0:24:37.000

  Kernel time: 0 days 0:04:23.000

  User time: 0 days 0:03:28.000

你可以看到系统已经启动有4天多了,进程已经运行了24½ 分钟。消耗的cpu时间总计是8分钟。这样我们可以得到一个cpu使用率的大概是32.5%

 

!threadpool

我们可以使用这个命令完整的找出在dump文件在创建的时候得多少的cpu 使用率。并能得到一些很有用的信息,比如队列中的工作请求数目,完整端口线程数目和计时器的数目。

 

0:000> !threadpool

CPU utilization 100%

Worker Thread: Total: 5 Running: 4 Idle: 1 MaxLimit: 200 MinLimit: 2

Work Request in Queue: 16

Unknown Function: 6a2d945d  Context: 023ede30

Unknown Function: 6a2d945d  Context: 023ee1e8

AsyncTimerCallbackCompletion TimerInfo@11b53760

Unknown Function: 6a2d945d  Context: 023ee3a8

Unknown Function: 6a2d945d  Context: 023e3040

Unknown Function: 6a2d945d  Context: 023ee178

Unknown Function: 6a2d945d  Context: 023edfb0

AsyncTimerCallbackCompletion TimerInfo@11b36428

AsyncTimerCallbackCompletion TimerInfo@11b53868

Unknown Function: 6a2d945d  Context: 023ee060

Unknown Function: 6a2d945d  Context: 023ee290

Unknown Function: 6a2d945d  Context: 023eded0

Unknown Function: 6a2d945d  Context: 023edd88

Unknown Function: 6a2d945d  Context: 023ede98

Unknown Function: 6a2d945d  Context: 023ee258

Unknown Function: 6a2d945d  Context: 023edfe8

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

Number of Timers: 9

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

Completion Port Thread:Total: 3 Free: 3 MaxFree: 4 CurrentLimit: 2 MaxLimit: 200 MinLimit: 2

所以我们看见现在的cpu使用率是100%,然后我们再看下一个命令。

 

!runaway

这是一个很好的命令,将会列出所有运行中的线程和cpu使用时间,这个是解决高cpu占用率很有用的一个命令。

 

0:000> !runaway

User Mode Time

  Thread       Time

  25:1a94      0 days 0:00:39.937

  16:1bc0      0 days 0:00:38.390

  50:1e8c      0 days 0:00:08.859

  52:1e40      0 days 0:00:08.687

  20:1c2c      0 days 0:00:08.234

  51:1340      0 days 0:00:08.171

  21:1bcc      0 days 0:00:06.953

  26:13ec      0 days 0:00:06.671

  44:131c      0 days 0:00:03.906

  22:d8c       0 days 0:00:03.375

  33:78c       0 days 0:00:02.656

你会看到这个总的时间和上面那个从.time命令中得到的cpu 利用(cpu utilization)的时间不一样,那是因为进程被重用了和回收了。这意味着总的cpu使用时间被若干个页面请求线程分割了。

 

!threads

要得到更多的运行线程信息,我们可以运行这个命令,这个会列出所有当前应用程序域下的托管线程。输出类似如下:

 

0:000> !threads

ThreadCount: 48

UnstartedThread: 0

BackgroundThread: 29

PendingThread: 0

DeadThread: 19

Hosted Runtime: no

                                      PreEmptive   GC Alloc           Lock

       ID OSID ThreadOBJ    State     GC       Context       Domain   Count APT Exception

  16    1 1bc0 001fccd0   1808220 Enabled  00000000:00000000 0019daf0     0 Ukn (Threadpool Worker)

  22    2  d8c 002016f0      b220 Enabled  00000000:00000000 0019daf0     0 MTA (Finalizer)

  14    4  4ac 00242e58   880a220 Enabled  00000000:00000000 0019daf0     0 MTA (Threadpool Completion Port)

  23    5 14a4 11b39f18    80a220 Enabled  00000000:00000000 0019daf0     0 MTA (Threadpool Completion Port)

  24    6 1d2c 11b41ad8      1220 Enabled  00000000:00000000 0019daf0     0 Ukn

  25    7 1a94 11b46c70   180b220 Enabled  27240c98:27241fd8 11b42540     1 MTA (Threadpool Worker)

  26    9 13ec 12ce2888   200b220 Enabled  2a9f1434:2a9f33c0 11b42540     0 MTA

  27    a 190c 12d85eb8   200b220 Enabled  00000000:00000000 11b42540     0 MTA

  29    b 1f5c 13df6a50   200b220 Enabled  2ab1da6c:2ab1f1c0 11b42540     0 MTA

那些ID XXXX 的线程是已经结束的,正在等待被回收。我们可以看见 一个 finalizer”的线程的ID号是22。因此当如果我们运行 !runaway 命令看到一个不正常的活动的编号是22,那我们现在知道是finalizer 的问题。

 

切换到某一个指定的进程

我们使用 ~[number of thread]s ,所以如果要转到50号进程,按下面的方法:

 

0:000> ~50s

如果我们能切换到50号进程,那我们有很多其他有用的命令。

 

!clrstack

这个命令列出当前进程的调用堆栈。 -p”开关量可以提供参数和局部变量的信息。

 

0:050> !clrstack

OS Thread Id: 0x1e8c (50)

ESP       EIP    

17a9e750 7d61c828

[NDirectMethodFrameSlim: 17a9e750]

 System.DirectoryServices.Protocols.Wldap32.ldap_bind_s(IntPtr, System.String,

System.DirectoryServices.Protocols.SEC_WINNT_AUTH_IDENTITY_EX, System.DirectoryServices.Protocols.BindMethod)

17a9e768 14df70f9

System.DirectoryServices.Protocols.LdapConnection.BindHelper(System.Net.NetworkCredential, Boolean)

17a9e794 14df6de0 System.DirectoryServices.Protocols.LdapConnection.Bind()

17a9e79c 14df59e9

System.DirectoryServices.Protocols.LdapConnection.SendRequestHelper(System.DirectoryServices.Protocols.DirectoryRequest, Int32 ByRef)

17a9e8b8 14df56e8

System.DirectoryServices.Protocols.LdapConnection.SendRequest(System.DirectoryServices.Protocols.DirectoryRequest, System.TimeSpan)

17a9e8bc 14df5657 [InlinedCallFrame: 17a9e8bc]

从下读到上,我们可以看到 调用了LdapConnectionSendRequest方法,该方法又调用了SendRequestHelper,这样一层一层的。

 

0:050> !clrstack -p
OS Thread Id: 0x1e8c (50)
ESP       EIP     
17a9e750 7d61c828 [NDirectMethodFrameSlim: 17a9e750] System.DirectoryServices.Protocols.Wldap32.ldap_bind_s(IntPtr, System.String, System.DirectoryServices.Protocols.SEC_WINNT_AUTH_IDENTITY_EX, System.DirectoryServices.Protocols.BindMethod)
17a9e768 14df70f9 System.DirectoryServices.Protocols.LdapConnection.BindHelper(System.Net.NetworkCredential, Boolean)
    PARAMETERS:
        this = 0x271fdfe0
        newCredential = 
        needSetCredential = 

17a9e794 14df6de0 System.DirectoryServices.Protocols.LdapConnection.Bind()
    PARAMETERS:
        this = 

17a9e79c 14df59e9 System.DirectoryServices.Protocols.LdapConnection.SendRequestHelper(System.DirectoryServices.Protocols.DirectoryRequest, Int32 ByRef)
    PARAMETERS:
        this = 0x271fdfe0
        request = 0x27246e38
        messageID = 0x17a9e8ec

17a9e8b8 14df56e8 System.DirectoryServices.Protocols.LdapConnection.SendRequest(System.DirectoryServices.Protocols.DirectoryRequest, System.TimeSpan)
    PARAMETERS:
        this = 0x271fdfe0
        request = 0x27246e38
        requestTimeout = 

17a9e8bc 14df5657 [InlinedCallFrame: 17a9e8bc]

 

我们看看这些参数,比如传递到SendRequest  SendRequestHelper DirectoryRequest ,我们要查看它的值,只要把地址记住,然后用下面的命令。

 

!dumpobject (!do)

这是另外一个非常有用的命令,根据地址,可以把一个对象dump出来,把requeset的地址传过去,就可以把这个对象dump出来

 

0:050> !do 0x27246e38
Name: System.DirectoryServices.Protocols.SearchRequest
MethodTable: 14b394c4
EEClass: 14d97ce0
Size: 52(0x34) bytes
GC Generation: 0
(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
02c39310  4000102        4        System.String  0 instance 00000000 directoryRequestID
14b398bc  4000103        8 ...ControlCollection  0 instance 27246e90 directoryControlCollection
02c39310  4000111        c        System.String  0 instance 27246d00 dn
12579f5c  4000112       10 ....StringCollection  0 instance 27246eb4 directoryAttributes
02c36ca0  4000113       14        System.Object  0 instance 27246ddc directoryFilter
14b39344  4000114       18         System.Int32  1 instance        1 directoryScope
14b393fc  4000115       1c         System.Int32  1 instance        0 directoryRefAlias
0fd3da00  4000116       20         System.Int32  1 instance        0 directorySizeLimit
1202af88  4000117       28      System.TimeSpan  1 instance 27246e60 directoryTimeLimit
120261c8  4000118       24       System.Boolean  1 instance        0 directoryTypesOnly

 

好的,那是什么呢?它是System.DirectoryServices.Protocols.SearchRequest 对象,那就是说它有很多不同的由System.DirectoryServices.Protocols.SearchRequest类定义的属性。可以 查看msdn来知道更多的信息。它包含RequestId, Scope, DistinguishedName等字段。

让我们大胆的猜测 dn 这个东西里面是什么东西。利用27246d00 这个地址,我们来看看

 

0:050> !do 27246d00

Name: System.String

MethodTable: 02c39310

EEClass: 0fb610ac

Size: 112(0x70) bytes

GC Generation: 0

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

String: CN=Dummy,CN=Accounts,CN=useradm,DC=dummy,DC=net

Fields:

      MT    Field   Offset                 Type VT     Attr    Value Name

0fd3da00  4000096        4         System.Int32  1 instance       48 m_arrayLength

0fd3da00  4000097        8         System.Int32  1 instance       47 m_stringLength

0fb80010  4000098        c          System.Char  1 instance       43 m_firstChar

02c39310  4000099       10        System.String  0   shared   static Empty

    >> Domain:Value  0019daf0:03380310 11b42540:03380310 <<

0fb86d44  400009a       14        System.Char[]  0   shared   static WhitespaceChars

    >> Domain:Value  0019daf0:03380324 11b42540:033855bc <<

确切的说,里面放的是"CN=Dummy,CN=Accounts,CN=useradm,DC=dummy,DC=net",如果你想找出更多的信息,可以继续用 !do 这个命令来检查它们。

下一章,我们继续使用 !do 这些命令来分析dump文件,另外还会介绍更多的命令。

 

从头开始使用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模式了。

从头开始使用WinDbg 第三部分(高级命令)

你知道么,你可以使用一些 for eache if 等来构建你的高级命令。下面列出了控制关键词:

.if

.else

.elseif

.foreach

.for

.while

.do

.break

.continue

.catch

.leave

.printf

.block

 

使用这些关键词,你可以给调试器发送很多高级指令,那会让你的工作更加的简单。极大的提升你的工作成果。

..foreach

让我们从一个简单的例子开始,假如你想研究堆上所有大于65000字节或更大的字符串。 要列出它们你只要输入:!dumpheap -type System.String -min 6500

 

0:000> !dumpheap -type System.String -min 6500

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

Heap 0

Address       MT     Size

790da154 790f9244     9280    

0264c4d0 790f9244    32788    

total 2 objects

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

Heap 1

Address       MT     Size

total 0 objects

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

Heap 2

Address       MT     Size

0b62e790 790f9244    11284    

total 1 objects

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

Heap 3

Address       MT     Size

0e6839d0 790f9244    32788    

0e717904 790f9244    32788    

0fb2a320 790f9244     6828    

total 3 objects

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

total 6 objects

Statistics:

      MT    Count    TotalSize Class Name

790f9244        6       125756 System.String

Total 6 objects

现在的问题是这样的,为了研究每一个字符串,你需要对每一个地址运行 !dumpobject 这个命令,在这里也许是可以接受的,但如果有25 100个怎么办呢? 不知道你是否注意到了这一点。如果你 通过 –short 这个参数,那!dumpheap 会给你最少的信息,仅仅是每个对象的地址

 

0:000> !dumpheap -type System.String -min 6500 -short

790da154

0264c4d0

0b62e790

0e6839d0

0e717904

0fb2a320

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

现在让我们来用 .foreache 子句吧。

 

0:000> .foreach(myVariable {!dumpheap -type System.String -min 6500 -short}){!do myVariable;.echo *************}

Name: System.String

MethodTable: 790f9244

EEClass: 790f91a4

Size: 9280(0x2440) bytes

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

String:

WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWGRkAiEPDxYCHwEFZFhYWFhYWFhYWFhYWFhYWFhYWFhYWF

hYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhY

WFhYWFhYWFhYWFhkZAInDw8WCh8CBQFFHwMFCjIyLzExLzIwMDcfBAUBVh8FBQFFHwYFASpkZAIpD2QWBGYPZBYEAg

ETC...

 

Fields:

      MT    Field   Offset                 Type VT     Attr    Value Name

790fdb60  4000096        4         System.Int32  1 instance     4632 m_arrayLength

790fdb60  4000097        8         System.Int32  1 instance     4631 m_stringLength

790fad38  4000098        c          System.Char  1 instance       3c m_firstChar

790f9244  4000099       10        System.String  0   shared   static Empty

    >> Domain:Value  000d5eb8:790d57b4 000fb4c0:790d57b4 000ca848:790d57b4 1d8334d8:790d57b4 <<

79122994  400009a       14        System.Char[]  0   shared   static WhitespaceChars

    >> Domain:Value  000d5eb8:026203f0 000fb4c0:02624504 000ca848:026745f0 1d8334d8:026dcef4 <<

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

Name: System.String

MethodTable: 790f9244

EEClass: 790f91a4

。。。。。。。

由于篇幅的关系,这里仅列出一部分。让我们来分析一下

.foreach    (myVariable

{!dumpheap -type System.String -min 6500 -short}

)

{!do myVariable;.echo *************}

 

myVariable 和它的字面意思一样,是变量名,我希望用它来存放由第一个命令子集的输出数据。第二个命令子集是来执行循环的,首先运行!do,然后我使用 .echo 命令来输出一个分割符,这样便于阅读。

还有更多的参数你可以使用。可以查看帮助来得到更多的信息。

 

.shell

我第一次看见我的一个同事使用过这个命令,他是一个调试方面的专家。

 

0:000> .shell -i - -ci "!iisinfo.clientconns" FIND /c "Request active"

40

.shell: Process exited

它首先运行 !iisinfo.clientonns”,然后用MS Dos  FING 命令来计算字符串“Request active”出现的次数。当然你可以再任何输出中来搜索特定的字符串。例如“”shell -i - -ci "!do 0b62e790" FIND /c /i "<table" ”,或其他你需要的东西。

让我们很快的看一下这个结构: -i”是可选的,用来指定输入,这里我们不使用文件,所以我们使用一个连字符(hyphen)。“.shell  –i  – ”,使用 “-ci”的意思是把紧跟的命令当作输入。最后我们指定 用怎么样的shell 命令来处理输入:“ FIND /c "Request active"  。事实上,你可以使用任意复杂的命令来代替“!iisinfo.clientconns”,比如说一个 foreach 循环。

posted @ 2014-05-12 01:40  eagle0719  阅读(441)  评论(0编辑  收藏  举报