Visual Studio 作为一种强大的开发平台,已经提供了非常多的调试手段。但这些调试手段相对来说还是停留在表面上,无非是设置断点、变量查看以及调用堆栈列表等。某些时候 我们希望了解更多的东西,尤其是那些被隐藏到背后和运行期的东西,诸如对象运行状态、内存分布等等,这些相对底层的知识可以让我们更好地理解 .NET CLR / JIT 的一些行为。当然,并不是所有人都需要了解这些知识,毕竟汇编和高级调试器使用起来还是非常麻烦的。
SOS.dll 是 Microsoft 提供的一种调试扩展,全称是 Son of Strike,可用来调试托管代码。SOS.dll 拥有非常强大的功能,包括 Cracker 常用的内存脱壳等。本文的目的并不是研究如何破解,而是如何使用 SOS.dll 来协助我们学习 .NET CLR / JIT 的一些知识。我们也不打算使用专业级别的 WinDbg,而是直接将 SOS.dll 载入到 VS 中使用。
1. 准备工作
(1) 打开 VS2005,在 "工具 > 选项 > 调试 > 符号" 对话框中添加符号文件位置,诸如 "C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\symbols"。
(2) 创建一个 ConsoleApplication 工程,输入测试代码。
(3) 打开项目属性对话框,在 "调试" 页选中 "启用非托管代码调试"。
(4) 在 "Console.WriteLine("Press any key to exit...");" 行设置断点。
2. 操作演示
按 F5 启动程序调试,执行到断点后,打开 "即时窗口",输入 ".load sos" 载入调试扩展。
输入 "!help" 可以查看全部的调试指令。
接下来,我们看看对象 o 是如何实现虚方法调用的。(上面代码中 Main 方法中的变量 o)
(1) 查看当前堆栈信息。
LOCALS 中的对象就是我们的目标。(如何你看过我写的 MSIL 系列文章,想必对此理解会更深。)
(2) 查看对象信息。
找到 MethodTable 的内存地址了,接下来看看这个表里面有什么东西。
(3) 查看方法表信息。
JIT 会将基类的虚方法插入到当前类型的方法表中。
要是我们将 Derived Test() 删除,方法表会是下面这个样子。
而改成 "public new void Test()" 则又有所不同。
对比这些差异能帮助我们更好地理解多态。好了,回到主题,我们看看 Main() 中的调用代码。
(4) 查看 IL 代码。
这次我们使用 "!name2ee" 来查找某个类型或方法的地址。然后使用 "!dumpil" 来看看编译器生成的 IL 代码。
callvirt 在 MSIL 系列文章中已经说过很多次,这就不重复啰嗦了。
除了上面这些,我们还可以做更多的事情。
(5) 查看对象信息。
(6) 查看托管堆状态。
(7) 查看应用程序域状态。
domain 地址可以使用 !eeheap 指令获取。
(8) 查看线程池状态。
SOS.dll 提供了大量的命令,大家可以通过 !help 指令查看其使用方法,本文不再一一详述。
SOS.dll 是 Microsoft 提供的一种调试扩展,全称是 Son of Strike,可用来调试托管代码。SOS.dll 拥有非常强大的功能,包括 Cracker 常用的内存脱壳等。本文的目的并不是研究如何破解,而是如何使用 SOS.dll 来协助我们学习 .NET CLR / JIT 的一些知识。我们也不打算使用专业级别的 WinDbg,而是直接将 SOS.dll 载入到 VS 中使用。
1. 准备工作
(1) 打开 VS2005,在 "工具 > 选项 > 调试 > 符号" 对话框中添加符号文件位置,诸如 "C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\symbols"。
![uploads/200710/11_123728_1.gif](https://i-blog.csdnimg.cn/blog_migrate/d9c1b69b12ddbfe2a26f1808f9a82a03.gif)
(2) 创建一个 ConsoleApplication 工程,输入测试代码。
namespace ConsoleApplication1
{
class Base
{
public virtual void Test() { }
}
class Derived : Base
{
public override void Test() { }
}
public class Program
{
static void Main(string[] args)
{
Derived o = new Derived();
o.Test();
(o as Base).Test();
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
Environment.Exit(0);
}
}
}
{
class Base
{
public virtual void Test() { }
}
class Derived : Base
{
public override void Test() { }
}
public class Program
{
static void Main(string[] args)
{
Derived o = new Derived();
o.Test();
(o as Base).Test();
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
Environment.Exit(0);
}
}
}
(3) 打开项目属性对话框,在 "调试" 页选中 "启用非托管代码调试"。
![uploads/200710/11_123732_2.gif](https://i-blog.csdnimg.cn/blog_migrate/2d1b27e33f3f691d10ab196746f27cdc.gif)
(4) 在 "Console.WriteLine("Press any key to exit...");" 行设置断点。
2. 操作演示
按 F5 启动程序调试,执行到断点后,打开 "即时窗口",输入 ".load sos" 载入调试扩展。
![uploads/200710/11_123736_3.gif](https://i-blog.csdnimg.cn/blog_migrate/d82cc8fb0b6556e7fba1a244ac3576e8.gif)
输入 "!help" 可以查看全部的调试指令。
!help
PDB symbol for mscorwks.dll not loaded
-------------------------------------------------------------------------------
SOS is a debugger extension DLL designed to aid in the debugging of managedprograms. Functions are listed by category, then roughly in order of importance. Shortcut names for popular functions are listed in parenthesis.
Type "!help <functionname>" for detailed info on that function.
... ...
PDB symbol for mscorwks.dll not loaded
-------------------------------------------------------------------------------
SOS is a debugger extension DLL designed to aid in the debugging of managedprograms. Functions are listed by category, then roughly in order of importance. Shortcut names for popular functions are listed in parenthesis.
Type "!help <functionname>" for detailed info on that function.
... ...
接下来,我们看看对象 o 是如何实现虚方法调用的。(上面代码中 Main 方法中的变量 o)
(1) 查看当前堆栈信息。
!clrstack -a
PDB symbol for mscorwks.dll not loaded
OS Thread Id: 0xfc8 (4040)
ESP EIP
0012f434 00f500bc ConsoleApplication1.Program.Main(System.String[])
PARAMETERS:
args = 0x013f1c20
LOCALS:
<CLR reg> = 0x013f1c74
0012f69c 79e79dd3 [GCFrame: 0012f69c]
PDB symbol for mscorwks.dll not loaded
OS Thread Id: 0xfc8 (4040)
ESP EIP
0012f434 00f500bc ConsoleApplication1.Program.Main(System.String[])
PARAMETERS:
args = 0x013f1c20
LOCALS:
<CLR reg> = 0x013f1c74
0012f69c 79e79dd3 [GCFrame: 0012f69c]
LOCALS 中的对象就是我们的目标。(如何你看过我写的 MSIL 系列文章,想必对此理解会更深。)
(2) 查看对象信息。
!dumpobj 0x013f1c74
Name: ConsoleApplication1.Derived
MethodTable: 00a73120
EEClass: 00a714d4
Size: 12(0xc) bytes
(ConsoleApplication1.exe)
Fields:
None
Name: ConsoleApplication1.Derived
MethodTable: 00a73120
EEClass: 00a714d4
Size: 12(0xc) bytes
(ConsoleApplication1.exe)
Fields:
None
找到 MethodTable 的内存地址了,接下来看看这个表里面有什么东西。
(3) 查看方法表信息。
!dumpmt -md 00a73120
EEClass: 00a714d4
Module: 00a72c3c
Name: ConsoleApplication1.Derived
mdToken: 02000004 (ConsoleApplication1.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 6
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
79369154 791474f8 PreJIT System.Object.ToString()
79367ec0 79147500 PreJIT System.Object.Equals(System.Object)
79367eb0 79147518 PreJIT System.Object.GetHashCode()
7935e4c0 79147520 PreJIT System.Object.Finalize()
00a7c0b0 00a73110 JIT ConsoleApplication1.Derived.Test()
00a7c0c0 00a73118 JIT ConsoleApplication1.Derived..ctor()
EEClass: 00a714d4
Module: 00a72c3c
Name: ConsoleApplication1.Derived
mdToken: 02000004 (ConsoleApplication1.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 6
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
79369154 791474f8 PreJIT System.Object.ToString()
79367ec0 79147500 PreJIT System.Object.Equals(System.Object)
79367eb0 79147518 PreJIT System.Object.GetHashCode()
7935e4c0 79147520 PreJIT System.Object.Finalize()
00a7c0b0 00a73110 JIT ConsoleApplication1.Derived.Test()
00a7c0c0 00a73118 JIT ConsoleApplication1.Derived..ctor()
JIT 会将基类的虚方法插入到当前类型的方法表中。
要是我们将 Derived Test() 删除,方法表会是下面这个样子。
EEClass: 00a714d0
Module: 00a72c3c
Name: ConsoleApplication1.Derived
mdToken: 02000004 (ConsoleApplication1.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 6
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
79369154 791474f8 PreJIT System.Object.ToString()
79367ec0 79147500 PreJIT System.Object.Equals(System.Object)
79367eb0 79147518 PreJIT System.Object.GetHashCode()
7935e4c0 79147520 PreJIT System.Object.Finalize()
00a7c070 00a730a8 JIT ConsoleApplication1.Base.Test()
00a7c0a0 00a73110 JIT ConsoleApplication1.Derived..ctor()
Module: 00a72c3c
Name: ConsoleApplication1.Derived
mdToken: 02000004 (ConsoleApplication1.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 6
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
79369154 791474f8 PreJIT System.Object.ToString()
79367ec0 79147500 PreJIT System.Object.Equals(System.Object)
79367eb0 79147518 PreJIT System.Object.GetHashCode()
7935e4c0 79147520 PreJIT System.Object.Finalize()
00a7c070 00a730a8 JIT ConsoleApplication1.Base.Test()
00a7c0a0 00a73110 JIT ConsoleApplication1.Derived..ctor()
而改成 "public new void Test()" 则又有所不同。
EEClass: 00a714d4
Module: 00a72c3c
Name: ConsoleApplication1.Derived
mdToken: 02000004 (ConsoleApplication1.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 7
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
79369154 791474f8 PreJIT System.Object.ToString()
79367ec0 79147500 PreJIT System.Object.Equals(System.Object)
79367eb0 79147518 PreJIT System.Object.GetHashCode()
7935e4c0 79147520 PreJIT System.Object.Finalize()
00a7c070 00a730a8 JIT ConsoleApplication1.Base.Test()
00a7c0b0 00a73110 JIT ConsoleApplication1.Derived.Test()
00a7c0c0 00a73118 JIT ConsoleApplication1.Derived..ctor()
Module: 00a72c3c
Name: ConsoleApplication1.Derived
mdToken: 02000004 (ConsoleApplication1.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 7
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
79369154 791474f8 PreJIT System.Object.ToString()
79367ec0 79147500 PreJIT System.Object.Equals(System.Object)
79367eb0 79147518 PreJIT System.Object.GetHashCode()
7935e4c0 79147520 PreJIT System.Object.Finalize()
00a7c070 00a730a8 JIT ConsoleApplication1.Base.Test()
00a7c0b0 00a73110 JIT ConsoleApplication1.Derived.Test()
00a7c0c0 00a73118 JIT ConsoleApplication1.Derived..ctor()
对比这些差异能帮助我们更好地理解多态。好了,回到主题,我们看看 Main() 中的调用代码。
(4) 查看 IL 代码。
!name2ee ConsoleApplication1.exe ConsoleApplication1.Program.Main
Module: 00a72c3c (ConsoleApplication1.exe)
Token: 0x06000009
MethodDesc: 00a73040
Name: ConsoleApplication1.Program.Main(System.String[])
JITTED Code Address: 00f50070
Module: 00a72c3c (ConsoleApplication1.exe)
Token: 0x06000009
MethodDesc: 00a73040
Name: ConsoleApplication1.Program.Main(System.String[])
JITTED Code Address: 00f50070
这次我们使用 "!name2ee" 来查找某个类型或方法的地址。然后使用 "!dumpil" 来看看编译器生成的 IL 代码。
!dumpil 00a73040
ilAddr = 004020c0
IL_0000: nop
IL_0001: newobj ConsoleApplication1.Derived::.ctor
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: callvirt ConsoleApplication1.Base::Test
IL_000d: nop
IL_000e: ldloc.0
IL_000f: callvirt ConsoleApplication1.Base::Test
IL_0014: nop
IL_0015: ldstr "Press any key to exit..."
IL_001a: call System.Console::WriteLine
IL_001f: nop
IL_0020: ldc.i4.1
IL_0021: call System.Console::ReadKey
IL_0026: pop
IL_0027: ldc.i4.0
IL_0028: call System.Environment::Exit
IL_002d: nop
IL_002e: ret
ilAddr = 004020c0
IL_0000: nop
IL_0001: newobj ConsoleApplication1.Derived::.ctor
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: callvirt ConsoleApplication1.Base::Test
IL_000d: nop
IL_000e: ldloc.0
IL_000f: callvirt ConsoleApplication1.Base::Test
IL_0014: nop
IL_0015: ldstr "Press any key to exit..."
IL_001a: call System.Console::WriteLine
IL_001f: nop
IL_0020: ldc.i4.1
IL_0021: call System.Console::ReadKey
IL_0026: pop
IL_0027: ldc.i4.0
IL_0028: call System.Environment::Exit
IL_002d: nop
IL_002e: ret
callvirt 在 MSIL 系列文章中已经说过很多次,这就不重复啰嗦了。
除了上面这些,我们还可以做更多的事情。
(5) 查看对象信息。
!clrstack -a
OS Thread Id: 0xfc8 (4040)
ESP EIP
0012f434 00f500bc ConsoleApplication1.Program.Main(System.String[])
PARAMETERS:
args = 0x013f1c20
LOCALS:
<CLR reg> = 0x013f1c74
0012f69c 79e79dd3 [GCFrame: 0012f69c]
!dumpobj 0x013f1c74
Name: ConsoleApplication1.Derived
MethodTable: 00a73120
EEClass: 00a714d4
Size: 12(0xc) bytes
(ConsoleApplication1.exe)
Fields:
None
OS Thread Id: 0xfc8 (4040)
ESP EIP
0012f434 00f500bc ConsoleApplication1.Program.Main(System.String[])
PARAMETERS:
args = 0x013f1c20
LOCALS:
<CLR reg> = 0x013f1c74
0012f69c 79e79dd3 [GCFrame: 0012f69c]
!dumpobj 0x013f1c74
Name: ConsoleApplication1.Derived
MethodTable: 00a73120
EEClass: 00a714d4
Size: 12(0xc) bytes
(ConsoleApplication1.exe)
Fields:
None
(6) 查看托管堆状态。
!eeheap
Loader Heap:
--------------------------------------
System Domain: 7a3c4690
LowFrequencyHeap: Size: 0x0(0)bytes.
HighFrequencyHeap: 00a62000(8000:1000) Size: 0x1000(4096)bytes.
StubHeap: 00a6a000(2000:1000) Size: 0x1000(4096)bytes.
Virtual Call Stub Heap:
IndcellHeap: Size: 0x0(0)bytes.
LookupHeap: Size: 0x0(0)bytes.
ResolveHeap: Size: 0x0(0)bytes.
DispatchHeap: Size: 0x0(0)bytes.
CacheEntryHeap: Size: 0x0(0)bytes.
Total size: 0x2000(8192)bytes
--------------------------------------
Shared Domain: 7a3c4330
LowFrequencyHeap: 00a90000(2000:1000) Size: 0x1000(4096)bytes.
HighFrequencyHeap: Size: 0x0(0)bytes.
StubHeap: Size: 0x0(0)bytes.
Virtual Call Stub Heap:
IndcellHeap: Size: 0x0(0)bytes.
LookupHeap: Size: 0x0(0)bytes.
ResolveHeap: Size: 0x0(0)bytes.
DispatchHeap: Size: 0x0(0)bytes.
CacheEntryHeap: Size: 0x0(0)bytes.
Total size: 0x1000(4096)bytes
--------------------------------------
Domain 1: 14c2d8
LowFrequencyHeap: 00a70000(2000:2000) Size: 0x2000(8192)bytes.
HighFrequencyHeap: 00a72000(8000:2000) Size: 0x2000(8192)bytes.
StubHeap: Size: 0x0(0)bytes.
Virtual Call Stub Heap:
IndcellHeap: Size: 0x0(0)bytes.
LookupHeap: Size: 0x0(0)bytes.
ResolveHeap: Size: 0x0(0)bytes.
DispatchHeap: Size: 0x0(0)bytes.
CacheEntryHeap: Size: 0x0(0)bytes.
Total size: 0x4000(16384)bytes
--------------------------------------
Jit code heap:
LoaderCodeHeap: 00f50000(10000:1000) Size: 0x1000(4096)bytes.
Total size: 0x1000(4096)bytes
--------------------------------------
Module Thunk heaps:
Module 790c2000: Size: 0x0(0)bytes.
Module 00a72c3c: Size: 0x0(0)bytes.
Total size: 0x0(0)bytes
--------------------------------------
Module Lookup Table heaps:
Module 790c2000: Size: 0x0(0)bytes.
Module 00a72c3c: Size: 0x0(0)bytes.
Total size: 0x0(0)bytes
--------------------------------------
Total LoaderHeap size: 0x8000(32768)bytes
=======================================
Number of GC Heaps: 1
generation 0 starts at 0x013f1018
generation 1 starts at 0x013f100c
generation 2 starts at 0x013f1000
ephemeral segment allocation context: none
segment begin allocated size
001967a0 790d7f90 790f76fc 0x0001f76c(128876)
013f0000 013f1000 013f1ff4 0x00000ff4(4084)
Large object heap starts at 0x023f1000
segment begin allocated size
023f0000 023f1000 023f3250 0x00002250(8784)
Total Size 0x229b0(141744)
------------------------------
GC Heap Size 0x229b0(141744)
Loader Heap:
--------------------------------------
System Domain: 7a3c4690
LowFrequencyHeap: Size: 0x0(0)bytes.
HighFrequencyHeap: 00a62000(8000:1000) Size: 0x1000(4096)bytes.
StubHeap: 00a6a000(2000:1000) Size: 0x1000(4096)bytes.
Virtual Call Stub Heap:
IndcellHeap: Size: 0x0(0)bytes.
LookupHeap: Size: 0x0(0)bytes.
ResolveHeap: Size: 0x0(0)bytes.
DispatchHeap: Size: 0x0(0)bytes.
CacheEntryHeap: Size: 0x0(0)bytes.
Total size: 0x2000(8192)bytes
--------------------------------------
Shared Domain: 7a3c4330
LowFrequencyHeap: 00a90000(2000:1000) Size: 0x1000(4096)bytes.
HighFrequencyHeap: Size: 0x0(0)bytes.
StubHeap: Size: 0x0(0)bytes.
Virtual Call Stub Heap:
IndcellHeap: Size: 0x0(0)bytes.
LookupHeap: Size: 0x0(0)bytes.
ResolveHeap: Size: 0x0(0)bytes.
DispatchHeap: Size: 0x0(0)bytes.
CacheEntryHeap: Size: 0x0(0)bytes.
Total size: 0x1000(4096)bytes
--------------------------------------
Domain 1: 14c2d8
LowFrequencyHeap: 00a70000(2000:2000) Size: 0x2000(8192)bytes.
HighFrequencyHeap: 00a72000(8000:2000) Size: 0x2000(8192)bytes.
StubHeap: Size: 0x0(0)bytes.
Virtual Call Stub Heap:
IndcellHeap: Size: 0x0(0)bytes.
LookupHeap: Size: 0x0(0)bytes.
ResolveHeap: Size: 0x0(0)bytes.
DispatchHeap: Size: 0x0(0)bytes.
CacheEntryHeap: Size: 0x0(0)bytes.
Total size: 0x4000(16384)bytes
--------------------------------------
Jit code heap:
LoaderCodeHeap: 00f50000(10000:1000) Size: 0x1000(4096)bytes.
Total size: 0x1000(4096)bytes
--------------------------------------
Module Thunk heaps:
Module 790c2000: Size: 0x0(0)bytes.
Module 00a72c3c: Size: 0x0(0)bytes.
Total size: 0x0(0)bytes
--------------------------------------
Module Lookup Table heaps:
Module 790c2000: Size: 0x0(0)bytes.
Module 00a72c3c: Size: 0x0(0)bytes.
Total size: 0x0(0)bytes
--------------------------------------
Total LoaderHeap size: 0x8000(32768)bytes
=======================================
Number of GC Heaps: 1
generation 0 starts at 0x013f1018
generation 1 starts at 0x013f100c
generation 2 starts at 0x013f1000
ephemeral segment allocation context: none
segment begin allocated size
001967a0 790d7f90 790f76fc 0x0001f76c(128876)
013f0000 013f1000 013f1ff4 0x00000ff4(4084)
Large object heap starts at 0x023f1000
segment begin allocated size
023f0000 023f1000 023f3250 0x00002250(8784)
Total Size 0x229b0(141744)
------------------------------
GC Heap Size 0x229b0(141744)
(7) 查看应用程序域状态。
domain 地址可以使用 !eeheap 指令获取。
!dumpdomain 14c2d8
--------------------------------------
Domain 1: 0014c2d8
LowFrequencyHeap: 0014c2fc
HighFrequencyHeap: 0014c354
StubHeap: 0014c3ac
Stage: OPEN
SecurityDescriptor: 0014d608
Name: Learn.CUI.exe
Assembly: 00192db0 [C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 00192e48
SecurityDescriptor: 00193fc0
Module Name
790c2000 C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll
Assembly: 0019e4c8 [ConsoleApplication1.exe]
ClassLoader: 0019e560
SecurityDescriptor: 0019e3f8
Module Name
00a72c3c ConsoleApplication1.exe
--------------------------------------
Domain 1: 0014c2d8
LowFrequencyHeap: 0014c2fc
HighFrequencyHeap: 0014c354
StubHeap: 0014c3ac
Stage: OPEN
SecurityDescriptor: 0014d608
Name: Learn.CUI.exe
Assembly: 00192db0 [C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 00192e48
SecurityDescriptor: 00193fc0
Module Name
790c2000 C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll
Assembly: 0019e4c8 [ConsoleApplication1.exe]
ClassLoader: 0019e560
SecurityDescriptor: 0019e3f8
Module Name
00a72c3c ConsoleApplication1.exe
(8) 查看线程池状态。
!ThreadPool
CPU utilization 0%
Worker Thread: Total: 0 Running: 0 Idle: 0 MaxLimit: 0 MinLimit: 0
Work Request in Queue: 0
--------------------------------------
Number of Timers: 0
--------------------------------------
Completion Port Thread:Total: 0 Free: 0 MaxFree: 0 CurrentLimit: 0 MaxLimit: 1000 MinLimit: 0
CPU utilization 0%
Worker Thread: Total: 0 Running: 0 Idle: 0 MaxLimit: 0 MinLimit: 0
Work Request in Queue: 0
--------------------------------------
Number of Timers: 0
--------------------------------------
Completion Port Thread:Total: 0 Free: 0 MaxFree: 0 CurrentLimit: 0 MaxLimit: 1000 MinLimit: 0
SOS.dll 提供了大量的命令,大家可以通过 !help 指令查看其使用方法,本文不再一一详述。
!help dumpclass
-------------------------------------------------------------------------------
!DumpClass <EEClass address>
The EEClass is a data structure associated with an object type. !DumpClass will show attributes, as well as list the fields of the type. The output is similar to !DumpObj. Although static field values will be displayed, non-static values won't because you need an instance of an object for that.
You can get an EEClass to look at from !DumpMT, !DumpObj, !Name2EE, and !Token2EE among others.
-------------------------------------------------------------------------------
!DumpClass <EEClass address>
The EEClass is a data structure associated with an object type. !DumpClass will show attributes, as well as list the fields of the type. The output is similar to !DumpObj. Although static field values will be displayed, non-static values won't because you need an instance of an object for that.
You can get an EEClass to look at from !DumpMT, !DumpObj, !Name2EE, and !Token2EE among others.