从头开始使用WinDbg 第一部分 web
基本配置: sql
1) 把sos.dll 拷贝到WinDbg的安装目录,确保你拷贝的这个dll和你想要研究问题的程序的.net 版本一致。若是你同时在.net1.1 和 .net2.0下,你能够把sos.dll 重命名为 sos11.dll 或sos20.dll 或者把它们放在不一样的目录下面。 shell
2) 建立一个文件夹,用来放置你的符号文件,例如:“C:\symbols” 数组
3) 打开WinDbg,打开配置符号文件路径的对话框 缓存
4) 输入路径,而且下载符号文件的路径,当你的本地没有符号文件的时候,它就须要去服务器上下载。结构以下: 服务器
srv*[cache path]*[symbols path] session
I'd recommend the following path: async
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] |
从下读到上,咱们能够看到 调用了LdapConnection的SendRequest方法,该方法又调用了SendRequestHelper,这样一层一层的。
0:050> !clrstack -p
|
咱们看看这些参数,好比传递到SendRequest 和 SendRequestHelper的 DirectoryRequest ,咱们要查看它的值,只要把地址记住,而后用下面的命令。
!dumpobject (!do)
这是另一个很是有用的命令,根据地址,能够把一个对象dump出来,把requeset的地址传过去,就能够把这个对象dump出来
0:050> !do 0x27246e38
|
好的,那是什么呢?它是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 循环。