Reputation: 4677
Using WinDbg with TTD enabled, how to play back to the moment of creation of some managed object?
Lets say I do have its address obtained using !clrstack -a
or !dso
Upvotes: 2
Views: 154
Reputation: 544
There are several ways of doing this. If you have the address of the object and want to go back to its creation you can use a ba (break on access). When an object is created the Method Table address is written into the first word (4 bytes for 32-bit, 8 bytes for 64-bit). So adding a breakpoint per write access and going backwards will stop at the object creation. Another way is to add breakpoints pointing to all constructors of the object and also going backwards. Also notice that all of this can also be done by a 'dx' command filtering by breakpoint address or constructor call.
Imagine you have this output:
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
(...)
Approach 1: And you want to stop when this object is created you can use (for 32-bit use w4):
ba w8 000001e9849cd4b8
g-
Using 'dx' (notice that the address must be in C++ format, starting wit '0x'):
dx -g @$cursession.TTD.Memory(0x00001e9849cd4b8,0x00001e9849cd4b8+8,"w")
Again, for 32-bit use address+4 on the second parameter. The option -g will show in a grid format.
Approach 2:
Get the address(es) of the constructors by listing the methods table of the class:
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
(...)
You may simply set the breakpoint at the 'Entry' address (or use !sos.bpmd) and go backwards:
bp 00007ffb85560090
g-
Or use 'dx' to show all occasions where the code was called (note that the code was again adjusted to look like C++ '0x' and also that is in quotes):
dx -g @$cursession.TTD.Calls("0x00007ffb85560090")
Hope it works for you.
Upvotes: 3