MANAGED DEBUGGING with WINDBG. Call Stacks. Part 1

Hi all,

This post is a continuation of MANAGED DEBUGGING with WINDBG. Setting a Breakpoint. Part 3.

CALL STACKS. Part 1

· We can see the call stack of the current thread:

When the debugger breaks for any reason (i.e. we reached a breakpoint, we got an exception or we stopped execution manually - Ctrl+Break), WinDbg command prompt shows the ID of the current thread where our commands will take effect:

0:000>

We can take a look to its call stack like we would do in any unmanaged application. For instance, we would like to see the call stack (k) with parameters (p), without line numbers (L), and at least 0x100 frames (100):

0:000> kpL100

ChildEBP RetAddr

0027e600 79f071ac KERNEL32!RaiseException(unsigned long dwExceptionCode = 0xe0434f4d, unsigned long dwExceptionFlags = 1, unsigned long nNumberOfArguments = 1, unsigned long * lpArguments = 0x0027e638)+0x58

0027e660 79f9293a mscorwks!RaiseTheExceptionInternalOnly(class Object * throwable = 0x01966fd0, int rethrow = 0, int fForStackOverflow = 0)+0x2a8

0027e698 7a129a34 mscorwks!UnwindAndContinueRethrowHelperAfterCatch(class Frame * pEntryFrame = 0x0027e6f0, class Exception * pException = 0x00443410)+0x70

0027e738 00371aad mscorwks!JIT_RngChkFail(void)+0xb0

0027e780 003719fe WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(<HRESULT 0x80004001>)+0x55

00000000 7b062c9a WindowsApplication1!WindowsApplication1.Form1.Button6_Click(<HRESULT 0x80004001>)+0x86

0027e7e8 7b11cb29 System_Windows_Forms_ni!System.Windows.Forms.Control.OnClick(<HRESULT 0x80004001>)+0x6a

...

0027f854 77bba9bd KERNEL32!BaseThreadInitThunk(unsigned long RunProcessInit = 1, <function> * StartAddress = 0x00000000, void * Argument = 0x7ffda000)+0xe

0027f894 00000000 ntdll!_RtlUserThreadStart(<function> * StartAddress = 0x00d75ede, void * Argument = 0x7ffda000)+0x23

By default we can only see 0x14 frames. If the RetAddr of the bottom function in the call stack is not 00000000, we haven’t reached the end of the call stack yet and we need to show more frames.

With the standard k command we can see both managed and unmanaged calls at the same time. With kp we will only see unmanaged parameters (note the HRESULT we get with managed calls: 0x80004001 which means E_NOTIMPL - “Not implemented”).

Additionally, we can see managed and unmanaged calls with a specific SOS command:

0:000> !DumpStack

OS Thread Id: 0x1f3c (0)

Current frame: KERNEL32!RaiseException+0x58 [d:\vistartm\base\win32\client\thread.c:1953]

ChildEBP RetAddr Caller,Callee

0027e5b8 77a1b09e KERNEL32!RaiseException+0x58 [d:\vistartm\base\win32\client\thread.c:1953], calling ntdll!zzz_AsmCodeRange_Begin [d:\vistartm\base\ntos\rtl\i386\raise.asm:81]

0027e5c4 79e7b494 mscorwks!Binder::RawGetClass+0x23 [f:\redbits\ndp\clr\src\vm\binder.cpp:303], calling mscorwks!Module::LookupTypeDef [f:\redbits\ndp\clr\src\vm\ceeload.h:1438]

...

0027e738 00371aad (MethodDesc 0x116e28 +0x55 WindowsApplication1.Form1.PlayWithArray(Int32[])), calling mscorwks!JIT_RngChkFail [f:\redbits\ndp\clr\src\vm\jithelpers.cpp:4801]

0027e760 003719fe (MethodDesc 0x116e20 +0x86 WindowsApplication1.Form1.Button6_Click(System.Object, System.EventArgs)), calling 0011c3c4

0027e780 003719fe (MethodDesc 0x116e20 +0x86 WindowsApplication1.Form1.Button6_Click(System.Object, System.EventArgs)), calling 0011c3c4

0027e79c 7b062c9a (MethodDesc 0x7b4a6250 +0x6a System.Windows.Forms.Control.OnClick(System.EventArgs))

0027e7b4 7b11cb29 (MethodDesc 0x7b5b76d8 +0x49 System.Windows.Forms.Button.OnClick(System.EventArgs)), calling (MethodDesc 0x7b4a6250 +0 System.Windows.Forms.Control.OnClick(System.EventArgs))

...

0027f838 7900b1b3 mscoree!_CorExeMain+0x2c [f:\redbits\ndp\clr\src\dlls\shim\shim.cpp:5888]

0027f848 77a43833 KERNEL32!BaseThreadInitThunk+0xe [d:\vistartm\base\win32\client\baseinit.c:817]

0027f854 77bba9bd ntdll!_RtlUserThreadStart+0x23 [d:\vistartm\base\ntos\rtl\rtlexec.c:2695]

The output of this command is very noisy and potentially confusing. It shows a mixture of a managed and native callstack but it is a raw stack, meaning that it will pretty much just display any addresses on the stack that happen to be pointing to code. This means that it will not give a true stack trace and anything shown may or may not be correct. The command is good for viewing additional call stack information when k gets confused. It also returns the Method Descriptors of the methods, so we could use them to i.e. set breakpoints as we’ve already seen:

0:000> !BPMD -md 0x116e28

MethodDesc = 00116e28

Setting breakpoint: bp 00371A58 [WindowsApplication1.Form1.PlayWithArray(Int32[])]

If we don’t want to see unmanaged stuff, we may only focus on managed calls:

0:000> !CLRStack

OS Thread Id: 0x1f3c (0)

ESP EIP

0027e6f0 77a1b09e [HelperMethodFrame: 0027e6f0]

0027e740 00371aad WindowsApplication1.Form1.PlayWithArray(Int32[])

0027e788 003719fe WindowsApplication1.Form1.Button6_Click(System.Object, System.EventArgs)

0027e7a8 7b062c9a System.Windows.Forms.Control.OnClick(System.EventArgs)

...

0027ecdc 00370117 WindowsApplication1.My.MyApplication.Main(System.String[])

0027ef24 79e7c74b [GCFrame: 0027ef24]

0:000> !DumpStack -EE

OS Thread Id: 0x1f3c (0)

Current frame:

ChildEBP RetAddr Caller,Callee

0027e738 00371aad (MethodDesc 0x116e28 +0x55 WindowsApplication1.Form1.PlayWithArray(Int32[]))

0027e760 003719fe (MethodDesc 0x116e20 +0x86 WindowsApplication1.Form1.Button6_Click(System.Object, System.EventArgs))

...

0027ecd4 00370117 (MethodDesc 0x113d88 +0x5f WindowsApplication1.My.MyApplication.Main(System.String[]))

And we can also see their parameters ( -p), their locals ( -l) or all of them ( -a):

0:000> !CLRStack -a

OS Thread Id: 0x1f3c (0)

ESP EIP

0027e6f0 77a1b09e [HelperMethodFrame: 0027e6f0]

0027e740 00371aad WindowsApplication1.Form1.PlayWithArray(Int32[])

PARAMETERS:

this = 0x0192f338

array = 0x01966fb8

LOCALS:

0x0027e758 = 0x00000000

0x0027e754 = 0x00000006

0x0027e750 = 0x00000003

0x0027e744 = 0x00000000

0x0027e74c = 0x00000003

0027e788 003719fe WindowsApplication1.Form1.Button6_Click(System.Object, System.EventArgs)

PARAMETERS:

this = 0x0192f338

sender = 0x01930398

e = 0x0196520c

LOCALS:

0x0027e78c = 0x00000000

<CLR reg> = 0x01966fb8

<CLR reg> = 0x00000000

...

Note that we won’t be able to tell the names of the locals, only their memory addresses in the stack of the thread:

0:000> dds 0x0027e744

0027e744 00000000

0027e748 01966fb8

0027e74c 00000003

0027e750 00000003

0027e754 00000006

0027e758 00000000

...

· We can inspect the parameters and locals of a method in thecall stack:

Let’s inspect the parameters of WindowsApplication1.Form1.PlayWithArray in previous sample, starting with ‘this’ pointer:

0:000> !DumpObj 0x0192f338

...

0:000> !do 0x0192f338

Name: WindowsApplication1.Form1

MethodTable: 00116ecc

EEClass: 00380a30

Size: 352(0x160) bytes

(C:\__WORKSHOP\Demos\BuggyNETApp\bin\Debug\WindowsApplication1.exe)

Fields:

MT Field Offset Type VT Attr Value Name

790fd0f0 400018a 4 System.Object 0 instance 00000000 __identity

...

7b47f3dc 4000011 154 ...dows.Forms.Button 0 instance 01930398 _Button6

7912d7c0 4000013 158 System.Int32[] 0 instance 01966fb8 myarray

79104368 400000a 20 ...ections.ArrayList 0 static 0192d2d8 __ENCList

790fe704 4000012 24 ....Threading.Thread 0 static 00000000 MainThread

0:000> !DumpArray 01966fb8

...

0:000> !da 01966fb8

Name: System.Int32[]

MethodTable: 7912d7c0

EEClass: 7912d878

Size: 24(0x18) bytes

Array: Rank 1, Number of elements 3, Type Int32

Element Methodtable: 79102290

[0] 00000001

[1] 00000002

[2] 00000003

Note that ‘this.myarray’ is the same as the ‘array’ parameter.

In this sample, locals don’t contain references to objects, just value types. So there is no need to use additional commands to find out more about them. We already know their values.

Next post: MANAGED DEBUGGING with WINDBG. Call Stacks. Part 2.

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)