从头开始使用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: Unknown Function: AsyncTimerCallbackCompletion TimerInfo@11b53760 Unknown Function: Unknown Function: Unknown Function: Unknown Function: AsyncTimerCallbackCompletion TimerInfo@11b36428 AsyncTimerCallbackCompletion TimerInfo@11b53868 Unknown Function: Unknown Function: Unknown Function: Unknown Function: Unknown Function: Unknown Function: Unknown Function: -------------------------------------- Number of Timers: 9 -------------------------------------- |
所以我们看见现在的cpu使用率是100%,然后我们再看下一个命令。
!runaway
这是一个很好的命令,将会列出所有运行中的线程和cpu使用时间,这个是解决高cpu占用率很有用的一个命令。
0:000> !runaway User Mode Time Thread Time 25: 16:1bc0 0 days 0:00:38.390 50:1e 52:1e40 0 days 0:00:08.687 20: 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: 22:d 33: |
你会看到这个总的时间和上面那个从.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 d 14 4 23 5 24 6 1d 25 7 26 9 13ec 12ce2888 200b220 Enabled 29 b |
那些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: 0x1e ESP EIP [NDirectMethodFrameSlim: System.DirectoryServices.Protocols.Wldap32.ldap_bind_s(IntPtr, System.String, System.DirectoryServices.Protocols.SEC_WINNT_AUTH_IDENTITY_EX, System.DirectoryServices.Protocols.BindMethod) System.DirectoryServices.Protocols.LdapConnection.BindHelper(System.Net.NetworkCredential, Boolean) System.DirectoryServices.Protocols.LdapConnection.SendRequestHelper(System.DirectoryServices.Protocols.DirectoryRequest, Int32 ByRef) System.DirectoryServices.Protocols.LdapConnection.SendRequest(System.DirectoryServices.Protocols.DirectoryRequest, System.TimeSpan) |
从下读到上,我们可以看到 调用了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: EEClass: 0fb Size: 112(0x70) bytes GC Generation: 0 (C:\WINDOWS\assembly\GAC_32\mscorlib\ String: CN=Dummy,CN=Accounts,CN=useradm,DC=dummy,DC=net Fields: MT Field Offset 0fd3da00 4000096 4 System.Int32 1 instance 0fd3da00 4000097 8 System.Int32 1 instance 0fb80010 >> Domain:Value 0019daf0:03380310 11b42540:03380310 << 0fb86d44 >> Domain:Value 0019daf0:03380324 11b42540:033855bc << |
确切的说,里面放的是"CN=Dummy,CN=Accounts,CN=useradm,DC=dummy,DC=net",如果你想找出更多的信息,可以继续用 !do 这个命令来检查它们。
下一章,我们继续使用 !do 这些命令来分析dump文件,另外还会介绍更多的命令。