最近在看相关内容,作者写的相当不错,自己随手学习了一下,非常棒,顺便也锻炼一下自己的翻译能力(好久没有处理相关内容了,能力直线下降啊)
原文地址:
Getting started with windbg - part I
Ok,在此之前随便写了一些帖子,关于“如何设置 windbg”和“如何查找OutOfMemoryException”。我的重新复习一些基础内容,为了更好的使用这些特殊的工具。
windbg基础配置:
1ã 将sos.dll 从
framework 安装目录拷贝到 windbg 的安装目录。请确保 sos.dll 文件的版本和即将要分析的版本一致。如果正同时使用 1.1 和 2.0 版本,可以重新命名文件为 sos.dll 为 sos11.dll 和 sos20.dll ,或则将 sos.dll 单独放置到不同的目录;
2ã 创建缓存符号文件夹。比如: c:/symbols
3ã启动 windbg,打开配置对话框(File -> Open Symbol Path,快捷键 Ctrl + S)
4ã输入符号文件夹位置。使用如下语法缓存缺失的符号文件(符号文件夹位置: c:/symbols public)
srv*[cache path]*[symbols path]
推荐输入如下内容:
Srv*c:/symbols public*http://msdl.microsoft.com/download/symbols
经过上述步骤后,即可打开已经存储的 dump 文件或附加进程,进行调试分析。
常用命令
以下实例基于 IIS6 的dump文件【标记1】 。
首先做的就是加载 sos 扩展功能dll。使用的是 load 命令,命令比较简单,语法如下:
.load [extension filename]
所以想加载未修改名称的sos.dll
文件,只需要简单的输入:
.load sos.dll
等加载成功后,即可使用sos 扩展的非常酷的命令,就像windbg 的默认命令一样。扩展命令通常都是以
“!” 开头。如果想运行 help 命令,只需要输入:
!help
即可显示当前扩展的帮助命令。
如果使用多个标识扩展命令,可使用 ![extension name].[command] 进行区分,例如:
!sos.help 【标记2】
Ok,到这里我们知道了如何运行扩展命令,尝试输入 !help 回车,可以看到如下结果:
0:000> !help *New* Use ------------------------------------------------------------------------------- SOS is a programs. importance. Type
Object ----------------------------- ----------------------------- DumpObj DumpAllExceptions DumpStackObjects DumpHeap DumpVC DumpStack GCRoot EEStack ObjSize GCInfo FinalizeQueue COMState DumpDynamicAssemblies DumpField TraverseHeap GCRef
Examining CLR ----------------------------- ----------------------------- DumpDomain VerifyHeap (vh) EEHeap DumpLog Name2EE FindAppDomain SyncBlk SaveModule DumpASPNETCache DumpMT GCHandles DumpClass GCHandleLeaks DumpMD FindDebugTrue Token2EE FindDebugModules EEVersion Bp DumpSig ProcInfo DumpModule StopOnException ThreadPool ConvertTicksToDate ConvertVTDateToDate RWLock CheckCurrentException (cce) DumpConfig CurrentExceptionName DumpHttpRuntime ExceptionBp DumpSessionStateConfig FindTable DumpBuckets LoadCache DumpHistoryTable SaveCache DumpRequestTable ASPXPages DumpCollection DumpDataTables CLRUsage GetWorkItems DumpLargeObjectSegments DumpModule DumpAssembly Other DumpMethodSig DumpRuntimeTypes FAQ PrintIPAddress DumpHttpContext DumpXmlDocument |
如果想查找某个命令更多详细的内容,输入 !help [command name] 即可,例如:
0:000> !help eeversion *New* Use ------------------------------------------------------------------------------- !EEVersion
This prints is running in garbage "Server"
A handy will provide loaded from. |
.time
这个不是 sos 命令。很明显该命令不是以 ! 开头。.time 命令显示与时间有关的信息,比如系统运行时间、处理器运行时间、内核总运行时间和用户模式处理时间。
0:000> .time Debug session System Uptime: Process Uptime: Kernel time: 0 days 1:04:32.000 User time: 0 days 16:55:49.000 |
如你所见,系统已运行2天,处理器运行 1天16小时左右,CPU累计运行18小时(kernel time + user time),根据这些信息,可以推测出CPU平均使用率为44%左右。
!threadpool
可以使用 !threadpool 命令精确的查找出,dump文件时cpu的使用情况。同时可以得到其他的有用的信息,例如:等待请求队列的数量,已完成的线程和时间【标注3】
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 34:1a8c 0 days 0:00:00.906 29:1f5c 0 days 0:00:00.828 6:e28 0 days 0:00:00.625 5:1c78 0 days 0:00:00.546 23:14a4 0 days 0:00:00.484 4:5ac 0 days 0:00:00.437 45:5dc 0 days 0:00:00.421 3:13b4 0 days 0:00:00.421 47:19c8 0 days 0:00:00.375 28:1b6c 0 days 0:00:00.250 46:1dac 0 days 0:00:00.156 7:1dd8 0 days 0:00:00.109 48:cdc 0 days 0:00:00.093 49:1eac 0 days 0:00:00.062 15:1a64 0 days 0:00:00.062 0:1804 0 days 0:00:00.046 36:4a4 0 days 0:00:00.031 11:1eb4 0 days 0:00:00.031 1:10b4 0 days 0:00:00.031 31:16ac 0 days 0:00:00.015 14:4ac 0 days 0:00:00.015 2:186c 0 days 0:00:00.015 59:590 0 days 0:00:00.000 58:294 0 days 0:00:00.000 57:16d0 0 days 0:00:00.000 56:1578 0 days 0:00:00.000 55:1428 0 days 0:00:00.000 54:16d8 0 days 0:00:00.000 53:fd8 0 days 0:00:00.000 43:1b8c 0 days 0:00:00.000 42:1c24 0 days 0:00:00.000 41:1e2c 0 days 0:00:00.000 40:11b0 0 days 0:00:00.000 39:edc 0 days 0:00:00.000 38:1a08 0 days 0:00:00.000 37:171c 0 days 0:00:00.000 35:1254 0 days 0:00:00.000 32:1f9c 0 days 0:00:00.000 30:1ae8 0 days 0:00:00.000 27:190c 0 days 0:00:00.000 24:1d2c 0 days 0:00:00.000 19:1e38 0 days 0:00:00.000 18:ee4 0 days 0:00:00.000 17:fb8 0 days 0:00:00.000 13:1b54 0 days 0:00:00.000 12:1a48 0 days 0:00:00.000 10:f64 0 days 0:00:00.000 9:1024 0 days 0:00:00.000 8:1b78 0 days 0:00:00.000 |
如你所见,时间总和和前面通过.time 命令获得的cpu时间不一致。是因为线程可以被反复使用和回收,即:一个线程使用cpu总量可以分开为多个页请求。【标注4】
!threads
为了获取更多的关于线程的信息,可以使用 !threads 命令。该命令将列表显示出应用程序所有正在运行的线程,当前应用程序域中后台正在运行的程序,等等线程相关内容。输出如下:
0:000> !threads ThreadCount: 8 UnstartedThread: 0 BackgroundThread: 2 PendingThread: 0 DeadThread: 5 XXX XXX XXX 10 XXX XXX |
如果线程ID列值为 XXXX ,则表示该线程已经终止,正等待被回收。终止线程Finalizer thread 的ID为2。如果在2号线程上有不正常的活动,运行 !threads 命令就可以知道终止线程相关的事件。
线程间的切换
为了切换到特定的线程上查看相关内容,可以使用
~[thread id]s 命令。譬如,想切换到finalizer
thread ,可以使用如下命令:
0:002> ~2s
现在我们就切换到了2号线程中了,在此基础上就可以继续使用其他的有用命令。
!clrstack
这个超强的命令可以列出当前线程中的调用栈信息。如果想查看额外的信息,添加
“-p”选项,该选项将同时显示相关参数和本地变量。
如下是在线程 10 上运行 clrstack 的清单表:
0:010> !clrstack Thread 10 ESP EIP 0x0d0ff5f8 0x7c90e4f4 [FRAME: 0x0d0ff608 0x05081deb [DEFAULT] I4 0x0d0ff644 0x050817c9 [DEFAULT] [hasThis] Boolean 0x0d0ff664 0x05081673 [DEFAULT] [hasThis] Boolean 0x0d0ff66c 0x05081618 [DEFAULT] [hasThis] I4 0x0d0ff6a4 0x00c1ecb0 [DEFAULT] [hasThis] I4 0x0d0ff6f8 0x00c1eb2a [DEFAULT] [hasThis] I4 0x0d0ff71c 0x00c1b51a [DEFAULT] [hasThis] I4 0x0d0ff76c 0x00c1b3ce [DEFAULT] [hasThis] I4 0x0d0ff794 0x00c1b1ff [DEFAULT] [hasThis] I4 0x0d0ff7a0 0x00c1a54c [DEFAULT] [hasThis] Class 0x0d0ff7b4 0x00c170b0 [DEFAULT] Class 0x0d0ff804 0x0093660e [DEFAULT] Class 0x0d0ff858 0x00935814 [DEFAULT] [hasThis] Void 0x0d0ff8a4 0x009351c5 [DEFAULT] [hasThis] Void 0x0d0ff8f0 0x05086a13 [DEFAULT] [hasThis] Boolean 0x0d0ff944 0x00c11a48 [DEFAULT] [hasThis] Void 0x0d0ff9d0 0x00c1175d [DEFAULT] [hasThis] Void 0x0d0ff9e8 0x7b29984d [DEFAULT] [hasThis] Void |
如果运行 !clrstack –p将得到如下结果:
0:010> !clrstack -p Thread 10 ESP EIP 0x0d0ff5f8 0x7c90e4f4 [FRAME: 0x0d0ff608 0x05081deb [DEFAULT] I4 0x0d0ff644 0x050817c9 [DEFAULT] [hasThis] Boolean 0x0d0ff664 0x05081673 [DEFAULT] [hasThis] Boolean 0x0d0ff66c 0x05081618 [DEFAULT] [hasThis] I4 0x0d0ff6a4 0x00c1ecb0 [DEFAULT] [hasThis] I4 0x0d0ff6f8 0x00c1eb2a [DEFAULT] [hasThis] I4 0x0d0ff71c 0x00c1b51a [DEFAULT] [hasThis] I4 0x0d0ff76c 0x00c1b3ce [DEFAULT] [hasThis] I4 0x0d0ff794 0x00c1b1ff [DEFAULT] [hasThis] I4 0x0d0ff7a0 0x00c1a54c [DEFAULT] [hasThis] Class 0x0d0ff7b4 0x00c170b0 [DEFAULT] Class 0x0d0ff804 0x0093660e [DEFAULT] Class 0x0d0ff858 0x00935814 [DEFAULT] [hasThis] Void 0x0d0ff8a4 0x009351c5 [DEFAULT] [hasThis] Void 0x0d0ff8f0 0x05086a13 [DEFAULT] [hasThis] Boolean 0x0d0ff944 0x00c11a48 [DEFAULT] [hasThis] Void 0x0d0ff9d0 0x00c1175d [DEFAULT] [hasThis] Void Sample.UI.Win.Service.MaterialManager.tm_Elapsed(Object,Class PARAM: 0x0d0ff9e8 0x7b29984d [DEFAULT] [hasThis] Void |
使用该命令我们可以查看相关参数。如上所述,将Sample.UI.Win.Service.MaterialManager作为 Sample.UI.Win.Service.MaterialManager.tm_Elapsed的参数Object 进行传递,如果想得到更多的内容,就需要继续往下使用下个命令.
!dumpobject【标注5】 (!do 为简写)
这是另一个至关重要的命令。Dumpobj 显示指定地址对象的内容,只需要指定需要观察对象的地址即可,如下:
0:000> !dumpobj 0xf32fcc Name: MethodTable 0x00822c28 EEClass 0x00822ba4 Size 144(0x90) bytes GC Generation: 2 Array: Rank 1, Type VALUETYPE Element Type: Content: 11 items |
Ok,看到了什么?是的,0xf32fcc 是一个 System.Collections.Hashtable/bucket[]
对象,该对象有11个对象或属性,如果想继续查看对象的值,只需要继续使用 !dumpobj 或查询 MSDN 关于 Hashtable 类的说明即可,一下是该对象的更详细的内容:
0:000> !dumpobj -v 0xf32fcc Name: MethodTable 0x00822c28 EEClass 0x00822ba4 Size 144(0x90) bytes GC Generation: 2 Array: Rank 1, Type VALUETYPE Element Type: Content: 11 items 【标注6 ???】 key: 0x00f6709c 0x00f6709c 0x79b946b0 System.String val: 0x00f670b8 0x00f670b8 0x79baaff0 System.Int32
key: 0x00f54720 0x00f54720 0x79b946b0 System.String val: 0x00f5473c 0x00f5473c 0x79baaff0 System.Int32 |
根据上述内容,为了更加了解key中存储的是何内容,只需要继续使用 !dumpobj 即可。
0:000> !dumpobj 0x00f6709c String: zh-cn |
Ok,这样我们就找到了,上述hastable的一个key为 zh-CN ,value 为 23
0:000> !dumpobj 0x00f670b8 Name: System.Int32 MethodTable 0x79baaff0 EEClass 0x79bab17c Size 12(0xc) bytes GC Generation: 2 mdToken: 0x0200009a FieldDesc*: 0x79bab1f4 0x79baaff0 0x40001e9 0x4 System.Int32 instance 23 m_value |
对于其他的托管堆中的对象,只要不停的使用 !dumpobj 即可找到最终的数据。
在接下来的文章中,我将继续使用 !dumpobj 命令来查找 w3wp 进程的内容,并介绍一下其他的非常棒的命令,敬请期待。
(未完待续)
原文:johan
翻译:AloneSword (部分内容在原有文章基础上做了修改)
【标记1】:由于没有拿到 iis6 下的 dmp 文件,翻译时是使用的xp sp3 中的一个应用程序的dmp 文件进行;
【标记2】:
在做这个命令时,一开始老是不正确。当输入 !sos.help 时,提示如下:
0:000> !sos.help The call to LoadLibrary(sos) failed, Please check your debugger configuration |
不知道为啥,最后再看 load 命令的帮助(命令行下输入: .hh load)时,才知道需要输入sos.dll 的绝对路径(或相对路径)才可以,而我将 sos.dll 放到了 windbg/clr10 目录下,所以需要输入 !clr10/sos.help ,终于见到了熟悉的面孔了。
【标注3】:由于是app 的dump 没有多线程的实例,所以当输入 !threadpool 时,显示如下:
0:000> !threadpool Loaded Son of Strike data table Version 5 CPU utilization 80% Worker Thread: Total: 1 Running: 1 Idle: Work Request in Queue: 0 -------------------------------------- Number of Timers: 0 -------------------------------------- Completion Port Thread: Total: 0 Free: 0 |
【标注4】:大脑在此处转不过来玩了,搞不明白了。被分配为多个了,咋时间还少了呢?
【标注5】:原文档中说是 !dumpobject 命令,但是使用 !help 找到的命令为 !dumpobj ,没有 !dumpobject ,相信是作者的笔误。
【标注6】:为啥这里显示的条目内容只有2条 Key-value ,但!dumpobj 时显示11,为啥呢?