WinDbg 命令三部曲:(二)WinDbg SOS 扩展命令手册

本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载。

系列博文

  1. 《WinDbg 命令三部曲:(一)WinDbg 命令手册》
  2. 《WinDbg 命令三部曲:(二)WinDbg SOS 扩展命令手册》
  3. 《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>  查询一个对象的所有引用根。

对象的引用根可能存在于如下位置:

  1. 栈上
  2. 包含在 GC 句柄中
  3. 准备被终结的对象中
  4. 在上述三点中的对象的成员中

在查询引用根时,首先在栈上查询,然后是句柄表,最后是对象终结器中的队列中的可达对象。

注:!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、锁信息等。

例如,假设有如下代码:

lock (MyObject)
{
  ...  
}

则将设置 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 种条件下使用:

  1. 如果使用了 System.Reflection.Emit.DynamicMethod 对象,则可将指针作为第一个参数。
  2. 如果使用了 DynamicMethodDesc 指针,可以打印相关动态方法的 IL 代码。
  3. 如果使用了常规的 MethodDesc,可以将其作为第一个参数来查看 IL 代码。
  4. 如果有直接的 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 列涵义:

  1. 调试器用 ID
  2. CLR 线程 ID
  3. 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  尝试仅显示可能感兴趣的线程,包括:

  1. 线程获取了一个锁
  2. 线程为 "jijacked" 状态,并允许被 GC 回收
  3. 线程当前运行至托管代码

!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 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载。

系列博文

  1. 《WinDbg 命令三部曲:(一)WinDbg 命令手册》
  2. 《WinDbg 命令三部曲:(二)WinDbg SOS 扩展命令手册》
  3. 《WinDbg 命令三部曲:(三)WinDbg SOSEX 扩展命令手册》

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。