noctonura
noctonura

Reputation: 13121

How to quickly get value of nested object in WinDbg?

Say I'm looking a dmp of a managed exe in WinDbg. Sos is loaded. I have the addresses of 30 objects of type Car. Cars "have-a" Engine which "have-a" serial number. Is there an easy way to write/script a function like:

long GetSerialNumber(Car car)
{
  return car.Engine.SerialNumber;
}

Right now I am just going through each object individually, which feels very inefficient!

Upvotes: 1

Views: 2549

Answers (1)

Thomas Weller
Thomas Weller

Reputation: 59303

WinDbg/SOS only

0:004> .loadby sos clr

0:004> !dumpheap -stat -type Car
Statistics:
      MT    Count    TotalSize Class Name
001c4e08        1           24 System.Collections.Generic.List`1[[DebugCarsSerialNumber.Car, DebugCarsSerialNumber]]
001c51fc        5          300 DebugCarsSerialNumber.Car[]
001c52ec       30          360 DebugCarsSerialNumber.Engine
001c4db8       30          360 DebugCarsSerialNumber.Car
Total 66 objects

0:004> !do 02213228
Name:        DebugCarsSerialNumber.Car
MethodTable: 001c4db8
EEClass:     001c1820
Size:        12(0xc) bytes
File:        C:\Users\For example John\Documents\Visual Studio 2015\Projects\DebugCarsSerialNumber\DebugCarsSerialNumber\bin\Debug\DebugCarsSerialNumber.exe
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
001c52ec  4000001        4 ...rialNumber.Engine  0 instance 02213234 Engine

So a car in my case has the engine at offset 4. With poi() you get pointer sized data, so let's apply it to the car:

0:004> !do poi(02213228+4)
Name:        DebugCarsSerialNumber.Engine
MethodTable: 001c52ec
EEClass:     001c19b4
Size:        12(0xc) bytes
File:        C:\Users\For example John\Documents\Visual Studio 2015\Projects\DebugCarsSerialNumber\DebugCarsSerialNumber\bin\Debug\DebugCarsSerialNumber.exe
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
71ce1638  4000003        4         System.Int32  1 instance 213550972 SerialNumber
71cf10f0  4000002        4        System.Random  0   static 02213240 rnd

The engine has the serial number at offset 4 as well. You would use poi() and !do again for objects, but since the serial number is primitive, use dp L1 here:

0:004> dp poi(02213228+4)+4 L1
02213238  0cba877c
0:004> ? 0cba877c
Evaluate expression: 213550972 = 0cba877c

Et voilá: the serial number.

Now that you know how to do it for a single car, let's loop over all cars:

0:004> .foreach (addr {!dumpheap -short -mt 001c4db8}) {dp poi(${addr}+4)+4 L1}
02213238  0cba877c
0221336c  215ac5aa
02213384  28c17829
[...]

If you want it without the addresses, use .printf:

0:004> .foreach (addr {!dumpheap -short -mt 001c4db8}) {.printf "%N\n", dwo(poi(${addr}+4)+4)}
0CBA877C
215AC5AA
28C17829
[...]

Or in decimal:

0:004> .foreach (addr {!dumpheap -short -mt 001c4db8}) {.printf "%i\n", dwo(poi(${addr}+4)+4)}
213550972
559596970
683767849
[...]

Easy: NetExt

Now that you know how to do it manually, use the easy shortcut of NetExt:

0:004> .load c:\debug\exts\NetExt.dll
NetExt version 2.0.1.5580 Aug  3 2015
License and usage can be seen here: !whelp license
Check Latest version: !wupdate
For help, type !whelp (or in WinDBG run: '.browse !whelp')
Questions and Feedback: http://netext.codeplex.com/discussions 
Copyright (c) 2014-2015 Rodney Viana (http://blogs.msdn.com/b/rodneyviana) 
Type: !windex -tree or ~*e!wstack to get started

0:004> !windex
Starting indexing at 21:24:48
Indexing finished at 21:24:48
36,204 Bytes in 486 Objects
Index took 00:00:00

0:004> !wfrom -type *.Car select Engine.SerialNumber
Engine.SerialNumber: 0n213550972
Engine.SerialNumber: 0n559596970
Engine.SerialNumber: 0n683767849

SOSEX

SOSEX' !mdt command can also access fields by name. Combined with the WinDbg loop you get:

 0:000> .foreach (addr {!dumpheap -short -mt 00144db8}) {!mdt -e:2 ${addr}.Engine.SerialNumber}
025e3238 (System.Int32)
    m_value:0x2d17efdf (System.Int32)
025e336c (System.Int32)
    m_value:0x45022ea6 (System.Int32)
025e3384 (System.Int32)
    m_value:0x346c6237 (System.Int32)
025e339c (System.Int32)
[...]

(Those values were received in a new debugging session, which is why the values differ)

Upvotes: 8

Related Questions