回放在 WinDbg TTD 中创建某个托管对象的时刻

Play back to the moment of creation of some managed object in WinDbg TTD

使用启用了 TTD 的 WinDbg,如何回放到创建某个托管对象的时刻? 可以说我确实使用 !clrstack -a!dso

获得了它的地址

有几种方法可以做到这一点。如果你有对象的地址并想回到它的创建,你可以使用 ba (访问中断)。创建对象时,方法 Table 地址被写入第一个字(32 位为 4 个字节,64 位为 8 个字节)。因此,为每次写入访问添加一个断点并向后移动将在对象创建时停止。另一种方法是添加指向对象的所有构造函数的断点并向后移动。另请注意,所有这些也可以通过 'dx' 命令过滤断点地址或构造函数调用来完成。

假设你有这样的输出:

0:016> !dso
OS Thread Id: 0x2f54 (16)
RSP/REG          Object           Name
000000A2CD5FC770 000001e9849cd4b8 ConceptNetConsole1.SampleClass1
(...)

0:016> !DumpObj /d 000001e9849cd4b8
Name:        ConceptNetConsole1.SampleClass1
MethodTable: 00007ffb85545ef8 <<< This is the method table
EEClass:     00007ffb85542d68
Size:        136(0x88) bytes
File:        C:\Projects\ConceptNetConsole1\ConceptNetConsole1\bin\x64\Release\ConceptNetConsole1.exe
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ffbe38e70b0  40005a8        8        System.Object  0 instance 000001e9849cd718 __identity
(...)

方法一: 你想在创建这个对象时停止,你可以使用(对于 32 位使用 w4):

ba w8 000001e9849cd4b8
g-

使用'dx'(注意地址必须是C++格式,以'0x'开头):

dx -g @$cursession.TTD.Memory(0x00001e9849cd4b8,0x00001e9849cd4b8+8,"w")

同样,对于 32 位,在第二个参数上使用地址+4。选项 -g 将以网格格式显示。

方法二:

通过列出 class 的方法 table 获取构造函数的地址:

0:016> !dumpmt -md 00007ffb85545ef8
EEClass:         00007ffb85542d68
Module:          00007ffb85545408
Name:            ConceptNetConsole1.SampleClass1
mdToken:         0000000002000005
File:            C:\Projects\ConceptNetConsole1\ConceptNetConsole1\bin\x64\Release\ConceptNetConsole1.exe
BaseSize:        0x88
ComponentSize:   0x0
Slots in VTable: 23
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
           Entry       MethodDesc    JIT Name
00007ffbe36fb1f0 00007ffbe3257538 PreJIT System.Object.ToString()
00007ffbe36ffd90 00007ffbe3257540 PreJIT System.Object.Equals(System.Object)
00007ffbe3721dc0 00007ffbe3257568 PreJIT System.Object.GetHashCode()
00007ffbe36fce50 00007ffbe3257580 PreJIT System.Object.Finalize()
00007ffbe37d8f40 00007ffbe333cfd0 PreJIT System.MarshalByRefObject.GetLifetimeService()
00007ffbe36f8b10 00007ffbe333cfd8 PreJIT System.MarshalByRefObject.InitializeLifetimeService()
00007ffbe37cbd80 00007ffbe333cfe0 PreJIT System.MarshalByRefObject.CreateObjRef(System.Type)
00007ffb85560090 00007ffb85545d58    JIT ConceptNetConsole1.SampleClass1..ctor()  <<< This is the constructor
(...)

您可以简单地在'Entry'地址设置断点(或使用!sos.bpmd)并向后走:

bp 00007ffb85560090
g-

或使用'dx'显示代码被调用的所有场合(请注意,代码再次被调整为看起来像 C++ '0x' 并且在引号中):

dx -g @$cursession.TTD.Calls("0x00007ffb85560090")

希望对你有用。