本文為 Dennis Gao 原創技術文章,發表于博客園博客,未經作者本人允許禁止任何形式的轉載。
系列博文
《WinDbg 命令三部曲:(一)WinDbg 命令手冊》
《WinDbg 命令三部曲:(二)WinDbg SOS 擴展命令手冊》
《WinDbg 命令三部曲:(三)WinDbg SOSEX 擴展命令手冊》
導航目錄
SOS 調試命令手冊
擴展加載命令
命令
描述
.loadby
.loadby sos clr
.load
.load C:\Windows\Microsoft.NET\Framework64\v4.0.30319\sos.dll
對象審查命令
命令
描述
!DumpObj (do)
!DumpObj 顯示指定地址的對象的信息。
!DumpObj -nofields 在顯示結果中不顯示字段信息,這對類似 String 類型等非常有用
!DumpArray (da)
!DumpArray 檢查數組對象元素
!DumpArray -start 可選項,只支持一維數組,從指定索引處開始顯示數組元素
!DumpArray -length 可選項,只支持一維數組,指定顯示元素的數量
!DumpArray -details 可選項,通過使用 !DumpObj 和 !DumpVC 來打印更多詳細信息
!DumpArray -nofields 可選項,僅在 -details 選項使用時有效,不顯示對象的字段信息
!DumpStackObjects (dso)
!DumpStackObjects 顯示當前調用棧上的所有托管對象的信息,可配合 k 或 CLRStack 命令使用
!DumpStackObjects -verify 將對非靜態類中的所有字段進行檢查
!DumpHeap
!DumpHeap 將遍歷 GC 堆對對象進行分析。通過指定不同的選項,可以查看特定的類型、數組和鎖。
如果不加任何選項,該命令的輸出首先為堆中對象的列表,然后是包含已發現類型的列表、大小和數量的報表。
其中 “Free” 對象代表的是垃圾回收器可以使用的區域。如果此區域的大小超過30%則可能意味著出現了堆碎片。
這通常是由于某些對象被持有了較長時間,并且結合了大量高頻率的內存分配。
!DumpHeap 會針對此情況提供一個關于堆碎片化的警告。
-stat 限定輸出為類型統計分析的匯總
-strings 限定輸出為字符串類型的統計分析匯總
-short 限定輸出僅為對象的地址,這將為串行化命令調試帶來便利
-min <size> 忽略尺寸小于給定的 bytes 值的對象
-max <size> 忽略尺寸大于給定的 bytes 值的對象
-live 僅輸出仍然存活的對象
-dead 僅輸出已死亡的對象 (這些對象將在下一個 Full GC 中被回收)
-thinlock ThinLocks 的報告 (參考 !SyncBlk)
-startAtLowerBound 強制堆指向可使用的地址的低地址邊界
-mt <MethodTable address> 僅列出包含 MethodTable 的對象
-type <partial type name> 僅列出對象類型字符串中包含給定子字符串的對象
start 從給定地址處開始列出對象
end 從給定地址處停止檢索
start/end 的參數可以通過 !EEHeap -gc 命令來獲取。例如,下面的圖中顯示列出大對象堆中的對象。
!DumpVC
!DumpVC <MethodTable address> <Address>
檢查值類型對象的字段,在 C# 中指的是 struct,存活于棧中或者被裝箱為 Object 后存放在 GC 堆中。
需要為 SOS 提供值對象的方法表地址,因為值對象與一級對象不同,一級對象的第一個字段即為方法表。
!GCRoot
!GCRoot [-nostacks] <Object address> 查詢一個對象的所有引用根。
對象的引用根可能存在于如下位置:
棧上
包含在 GC 句柄中
準備被終結的對象中
在上述三點中的對象的成員中
在查詢引用根時,首先在 棧上查詢,然后是句柄表,最后是對象終結器中的隊列中的可達對象。
注:!GCRoot 不會棧上的對象根進行有效性校驗。可以使用 !CLRStack 或 !U 來檢查對象是否仍在被使用。
-nostacks 限定僅在句柄表和終結器隊列中查找。
!ObjSize
!ObjSize [<Object address>]
如果不加參數,!ObjSize 將列出托管線程中所有對象的尺寸。
同時,也會列出進程中的所有 GC 句柄,和句柄指向對象的大小。
在計算對象的尺寸時,!ObjSize 將計算對象及其所有子對象的大小。
!FinalizeQueue
!FinalizeQueue [-detail] | [-allReady] [-short]
!FinalizeQueue 列出所有注冊為終結化的對象。
GC 堆是按照代來劃分,此處同樣列出每代中將被終結的對象的數量。
上圖中顯示了只有 0 代堆中包含了注冊終結對象。"(0015bc90->0015bca0)" 提示了對象指針的內存查詢區域。
-allReady 指定此選項后,將列出所有準備終結化的對象,無論其是否被標注為在當前輪 GC 還是下一輪 GC。
那些已經不在 "Ready for finalization" 列表中的對象則已經失去了引用根。
這個選項可能會有些開銷,因為其會驗證是否終結化隊列中的對象是否仍然存在引用根。
-short 限定輸出僅為對象的地址。
如果與 -allReady 選項同時使用,則將列出所有存在終結器中并且不再是引用根的對象。
如果單獨使用,則將列出 "Ready for finalization" 隊列中的所有對象。
-detail 顯示額外的信息,例如需要被終結器清理的緩存的數據結構等。
!PrintException (pe)
!PrintException [-nested] [-lines] [<Exception object address>]
!PrintException 將對任意 System.Exception 的衍生對象的字段進行格式化。
例如,將對 _stackTrace 字段進行格式化。
如果不加任何參數,!PrintException 將查找當前線程上最有一個出現的異常。
這與使用 !Threads 中顯示的異常是相同的。
-nested 顯示嵌套的異常信息。
-lines 顯示異常的可用的源信息。
!TraverseHeap
!TraverseHeap [-xml] [-verify] <filename>
!TraverseHeap 將以一種 CLR Profiler 可理解的格式將 GC 堆信息輸出到文件。
可以在如下鏈接下載 CLR Profiler:
http://www.microsoft.com/downloads/details.aspx?FamilyId=86CE6052-D7F4-4AEB- 9B7A-94635BEEBDDA&displaylang=en
CLR Profiler 將以圖形化的方式來幫助分析應用程序 GC 堆的狀態。
-verify 將進行更多合法性檢測,可在有任何疑似堆腐化時使用。
-xml 輸出格式指定為 XML 格式。
數據結構審查命令
命令
描述
!DumpDomain
!DumpDomain [<Domain address>]
在無參數時,!DumpDomain 將列出進程中所有的 AppDomain 。同時也會遍歷所有已加載的程序集。
在應用程序的的 AppDomain 之外,還存在另外兩個特殊的應用程序域:Shared Domain 和 System Domain。
所列出的任意程序集的指針均可用于 !DumpAssembly 命令。任何 AppDomain 指針均可被使用于 !DumpDomain 命令。
!EEHeap
!EEHeap [-gc] [-loader] 遍歷進程內存中的 CLR 數據結構。
!EEHeap -gc
!EEHeap -loader
!Name2EE
!Name2EE <module name> <type or method name>
!Name2EE <module name>!<type or method name>
!Name2EE 用于將給定的類名稱轉換為 MethodTable 或 EEClass 的地址。或將方法名稱轉換為 MethodDesc。
!SyncBlk
!SyncBlk [-all | <syncblk number>]
SyncBlock 負責持有一些不是為每個對象都需創建的額外信息,例如 COM Interop 數據、HashCodes、鎖信息等。
例如,假設有如下代碼:
則將設置 MyObject 為當前線程所擁有。一個 SyncBlock 將會為 MyObject 創建,并且包含線程的宿主信息等。
如果另外一個線程試圖執行同樣的代碼,該線程將不能進入該 Block 中直到上一個線程退出。
這將使 !SyncBlk 在檢測托管線程死鎖時非常有用途。例如有如下代碼情形:
Resource r1 = new Resource();
Resource r2 = new Resource();
lock (r1)
{
lock (r2)
{
...
}
}
lock (r2)
{
lock (r1)
{
...
}
}
通過上面的描述可以了解到,線程 e04 持有著對象 00a7a194,而線程 ab8 持有著對象 00a7a1a4。
再結合調用棧信息可發現死鎖。
此處,可通過運行 !U 或 !DumpHeap -ThinLock 獲取更多信息。
!DumpMT
!DumpMT [-MD] <MethodTable address> 顯示方法表。每個托管對象都在其起始位置包含一個方法表指針。
-MD 顯示對象中定義的方法列表。
!DumpClass
!DumpClass <EEClass address> 顯示 EEClass 中定義的屬性和字段類型。
EEClass 是一種描述對象類型的數據結構。
!Token2EE
!Token2EE <module name> <token> 將 Token 元數據轉換為 MethodTable 或 MethodDesc。
!EEVersion
顯示 CLR 版本。同時也顯示應用程序代碼是運行在 "Workstation" 或 "Server" 模式。
類似的功能可以通過命令:"lm v m clr"
!DumpModule
!DumpModule [-mt] <Module address> 通過模塊地址獲取模塊信息。
-mt 顯示模塊內定義的類型信息。
!ThreadPool
顯示線程池的基本信息,包括隊列中請求的數量、完成端口線程的數量和計時器的數量。
!DumpAssembly
!DumpAssembly <Assembly address> 顯示指定地址程序集的信息。
!DumpSigElem
!DumpSigElem <sigaddr> <moduleaddr> 顯示簽名對象中的一個指定元素信息。
!DumpRuntimeTypes
!DumpRuntimeTypes 從 GC 堆中尋找 System.RuntimeType 類型的對象,并且打印類型名稱和方法表。
!DumpSig
!DumpSig <sigaddr> <moduleaddr> 顯示給定地址的方法或字段的簽名信息。
!RCWCleanupList
!RCWCleanupList [address] 顯示在下一次清理周期內回收的 COM 對象信息。
RuntimeCallableWrapper 是 CLR 內部的數據結構,用于宿主 COM 對象。
通過 System.__ComObject 類向托管代碼暴露。
當相應的對象被 GC 回收之后,相關的 COM 對象引用也不在需要,所以相應的 RCW 也需要被清理。
!DumpIL
!DumpIL <Managed DynamicMethod object> | <DynamicMethodDesc pointer> | <MethodDesc pointer> | /i <IL pointer>
打印托管方法的 IL 代碼。在調試 DynamicMethod 時非常有效,但同樣適合 non-DynamicMethod。
可以在下列 4 種條件下使用:
如果使用了 System.Reflection.Emit.DynamicMethod 對象,則可將指針作為第一個參數。
如果使用了 DynamicMethodDesc 指針,可以打印相關動態方法的 IL 代碼。
如果使用了常規的 MethodDesc,可以將其作為第一個參數來查看 IL 代碼。
如果有直接的 IL 指針,則可使用 /i 選項和 IL 地址作為參數。
!DumpRCW
!DumpRCW <RCW address> 顯示 RuntimeCallableWrapper 的信息。
!DumpCCW
!DumpCCW <CCW address or COM IP> 顯示 COMCallableWrapper 的信息。
代碼堆棧審查命令
命令
描述
!Threads
!Threads [-live] [-special] 列出進程中所有的托管線程。
-live 可選項。僅顯示活躍的線程。
-special 可選項。顯示由 CLR 創建的特殊線程,這些線程有可能不是托管線程。
例如 GC 線程、調試器線程、終結器線程、應用程序域卸載線程、線程池計時器線程等。
ID 列涵義:
調試器用 ID
CLR 線程 ID
OS 線程 ID
!ThreadState
!ThreadState value 顯示線程狀態
可能的線程狀態包括:
Thread Abort Requested
GC Suspend Pending
User Suspend Pending
Debug Suspend Pending
GC On Transitions
Legal to Join
Yield Requested
Hijacked by the GC
Blocking GC for Stack Overflow
Background
Unstarted
Dead
CLR Owns
CoInitialized
In Single Threaded Apartment
In Multi Threaded Apartment
Reported Dead
Fully initialized
Task Reset
Sync Suspended
Debug Will Sync
Stack Crawl Needed
Suspend Unstarted
Aborted
Thread Pool Worker Thread
Interruptible
Interrupted
Completion Port Thread
Abort Initiated
Finalized
Failed to Start
!IP2MD
!IP2MD <Code address> 根據給定的托管 JITTED 代碼,查找相關的 MethodDesc。
上面的例子中,我們通過 Mainy.Main 的返回地址來尋找相關的方法信息。
!U
!U [-gcinfo] [-ehinfo] [-n] <MethodDesc address> | <Code address>
根據給定方法的 MethodDesc 指針,輸出反匯編代碼。
-gcinfo 同時獲得方法的 GCInfo 信息。相關信息可通過 !GCInfo 獲得。
-ehinfo 同時獲得方法的異常信息。相關信息可通過 !EHInfo 獲得。
-n 不顯示行號和符號等信息。
!DumpStack
!DumpStack [-EE] [-n] [top stack [bottom stack]] 提供詳細甚至過于冗余混淆的調用棧信息。
-EE 僅顯示托管函數。
-n 不顯示行號或符號信息。
!EEStack
!EEStack [-short] [-EE] 這個命令用于在進程內的所有線程上運行 !DumpStack。
-EE 該選項將直接被傳遞給 !DumpStack 命令。
-short 嘗試僅顯示可能感興趣的線程,包括:
線程獲取了一個鎖
線程為 "jijacked" 狀態,并允許被 GC 回收
線程當前運行至托管代碼
!CLRStack
!CLRStack [-a] [-l] [-p] [-n]
!CLRStack [-a] [-l] [-p] [-i] [variable name] [frame]
!CLRStack 試圖僅為托管代碼提供真實的調用棧信息。
-p 顯示托管函數的參數信息。
-l 顯示幀內局部變量的信息。
-a = -p + -l 的組合。
-n 不顯示行信息和符號信息。
!GCInfo
!GCInfo (<MethodDesc address> | <Code address>) 用于診斷 JIT 編譯器是否存在Bug。
!EHInfo
!EHInfo (<MethodDesc address> | <Code address>) 用于顯示 JITTED 方法的異常處理部分。
!BPMD
!BPMD [-nofuturemodule] <module name> <method name> [<il offset>]
!BPMD <source file name>:<line number>
!BPMD -md <MethodDesc>
!BPMD -list
!BPMD -clear <pending breakpoint number>
!BPMD -clearall
!BPMD 用于提供托管代碼的斷點支持。
!COMState
顯示進程的 COM Apartment Model。
垃圾回收歷史審查命令
命令
描述
!HistInit
!HistInit 在運行任何 Hist 族命令之前,需要先根據被調試程序的壓縮日志中初始化 SOS 結構。
!HistRoot
!HistRoot <root> 顯示 promotion 和 relocation 信息。
!HistObj
!HistObj <obj_address> 從日志中檢查 GC relocation 鏈。
!HistObjFind
!HistObjFind <obj_address> 從日志中檢索與對象的 relocation 相關的所有信息。
!HistClear
!HistClear 釋放用于 Hist 族命令的所有資源。通常無需顯式的調用此命令,因為每次 HistInit 會首先清理資源。
診斷工具命令
命令
描述
!VerifyHeap
!VerifyHeap 是一個用于檢測 GC 堆中是否有腐化跡象的診斷工具。
其以如下的模式逐個的走查對象:
!VerifyObj
!VerifyObj <object address> 是一個用于檢查被傳遞的對象參數是否存在腐化的跡象的診斷工具。
!FindRoots
!FindRoots -gen <N> | -gen any | <object address> 用于查找對象的引用根的診斷工具。
!HeapStat
!HeapStat [-inclUnrooted | -iu] 顯示GC堆中每個代的大小和總和,同時顯示空閑空間的大小。
-inclUnrooted 報告中包含那些在 GC 堆中已標識為不再引用的托管對象。
!GCWhere
!GCWhere <object address> 顯示指定對象在 GC 堆中的位置。
!ListNearObj (lno)
!ListNearObj <object address> 用于顯示對象前后的對象的診斷工具。
!GCHandles
!GCHandles [-type handletype] [-stat] [-perdomain] 提供對進程中 GCHandles 的統計分析。
-stat 僅顯示統計信息,而不列出句柄和其指向的信息。
-perdomain 根據 AppDomain 來顯示統計信息。
-type 句柄類型的過濾。
可用的句柄類型包括:
Pinned
RefCounted
WeakShort
WeakLong
Strong
Variable
AsyncPinned
!GCHandleLeaks
!GCHandleLeaks 幫助檢測 GCHandle 泄漏的工具。
!FindAppDomain
!FindAppDomain <Object address> 嘗試根據對象查找出 AppDomain。
!SaveModule
!SaveModule <Base address> <Filename> 將內存鏡像保存至文件。
!ProcInfo
!ProcInfo [-env] [-time] [-mem] 列出進程中的環境變量,內核 CPU 時間,內存使用率等。
!StopOnException (soe)
!StopOnException [-derived] [-create | -create2] <Exception> [<Pseudo-register number>]
!StopOnException 當需要調試器在遇到特定的托管異常時停止。
例如,當遇到 System.OutOfMemoryException 時停止,而遇到其他異常時繼續運行。
!DumpLog
!DumpLog [-addr <addressOfStressLog>] [<Filename>] 允許將 CLR in-memory stress log 日志寫入文件。
通過下面注冊表內的信息更改 Stress Log 設置:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework:
(DWORD) StressLog = 1
(DWORD) LogFacility = 0xffffffbf
(DWORD) StressLogSize = 65536
(DWORD) LogLevel = 6
LogFacility 定義:
GC 0x00000001
GCINFO 0x00000002
STUBS 0x00000004
JIT 0x00000008
LOADER 0x00000010
METADATA 0x00000020
SYNC 0x00000040
EEMEM 0x00000080
GCALLOC 0x00000100
CORDB 0x00000200
CLASSLOADER 0x00000400
CORPROF 0x00000800
REMOTING 0x00001000
DBGALLOC 0x00002000
EH 0x00004000
ENC 0x00008000
ASSERT 0x00010000
VERIFIER 0x00020000
THREADPOOL 0x00040000
GCROOTS 0x00080000
INTEROP 0x00100000
MARSHALER 0x00200000
IJW 0x00400000
ZAP 0x00800000
STARTUP 0x01000000
APPDOMAIN 0x02000000
CODESHARING 0x04000000
STORE 0x08000000
SECURITY 0x10000000
LOCKS 0x20000000
BCL 0x40000000
!VMMap
!VMMap 遍歷虛擬地址空間,列出 Region Protection 類型。
!VMStat
!VMStat 提供虛擬地址空間的綜合報告。
!MinidumpMode
!MinidumpMode <0 or 1>
通過 ".dump /m" 或 ".dump" 來獲得 CLR 數據的子集,僅適合使用 SOS 的命令的子集,一些 SOS 命令可能失敗。
默認值為 0。
!AnalyzeOOM (ao)
!AnalyzeOOM 顯示最后一個 OOM 的信息。
參考資料
本文為 Dennis Gao 原創技術文章,發表于博客園博客,未經作者本人允許禁止任何形式的轉載。
系列博文
《WinDbg 命令三部曲:(一)WinDbg 命令手冊》
《WinDbg 命令三部曲:(二)WinDbg SOS 擴展命令手冊》
《WinDbg 命令三部曲:(三)WinDbg SOSEX 擴展命令手冊》