Retired Microsoft Blog disclaimer

This directory is a mirror of retired "Decrypt My World" MSDN blog and is provided as is. All posting authorship and copyrights belong to respective authors.

Posts on this page:

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/08/05/managed-debugging-with-windbg-call-stacks-part-3/
Post name: MANAGED DEBUGGING with WINDBG. Call Stacks. Part 3
Original author: Alejandro Campos Magencio
Posting date: 2009-08-05T02:47:00+00:00


Hi all,

This post is a continuation of MANAGED DEBUGGING with WINDBG. Call Stacks. Part 2.

CALL STACKS. Part 3

· Let's review previous commands:

Let’s see a sample where the debugger broke when “PlayWithArray” raised an exception. We can use the return address of the function it called, “mscorwks!JIT_RngChkFail”, to see where in “PlayWithArray” we were when the exception happened:

0:000> kL

ChildEBP RetAddr

0027e600 79f071ac KERNEL32!RaiseException+0x58

0027e660 79f9293a mscorwks!RaiseTheExceptionInternalOnly+0x2a8

0027e698 7a129a34 mscorwks!UnwindAndContinueRethrowHelperAfterCatch+0x70

0027e738 00371aad mscorwks!JIT_RngChkFail+0xb0

0027e780 003719fe WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[])+0x55

...

0:000> !u 00371aad

Normal JIT generated code

WindowsApplication1.Form1.PlayWithArray(Int32[])

Begin 00371a58, size c0

00371a58 55 push ebp

...

00371aa0 8b4dc8 mov ecx,dword ptr [ebp-38h]

00371aa3 3b5104 cmp edx,dword ptr [ecx+4]

00371aa6 7205 jb WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[])+0x55 (00371aad)

00371aa8 e8d67edb79 call mscorwks!JIT_RngChkFail (7a129983)

>>> 00371aad 03449108 add eax,dword ptr [ecx+edx*4+8]

00371ab1 7105 jno WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[])+0x60 (00371ab8)

00371ab3 e8837fdb79 call mscorwks!JIT_Overflow (7a129a3b)

...

00371b16 5d pop ebp

00371b17 c3 ret

0:000> u 00371aa8

WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[])+0x50 [C:\__WORKSHOP\Demos\BuggyNETApp\Form1.vb @ 135]:

...

Note how we used the unmanaged version of this command, u, to find out the source code line.

That line corresponds to the following VB.NET statement in “PlayWithArray”:

result += array(i)

It seems we got some exception when accessing the array... Can we tell why? Let’s give it a try:

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 = 0x01983d38

LOCALS:

0x0027e758 = 0x00000000

0x0027e754 = 0x00000006

0x0027e750 = 0x00000003

0x0027e744 = 0x00000000

0x0027e74c = 0x00000003

...

0:000> !da 0x01983d38

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

It seems we tried to access the element at index 3 of the array (the 4th element), but the array only has 3 elements.


How do I know which local corresponds to “i”? ILDASM may help here, because the locals it shows are in the same order as in !CLRStack. In our case, “i” is in the 3rd position:

// Code size 49 (0x31)

.maxstack 3

.locals init ([0] int32 PlayWithArray,

[1] int32 result,

[2] int32 i,

[3] class [mscorlib]System.Exception ex,

[4] int32 VB$CG$t_i4$S0)

· We can use registers to check parameters of a method in the call stack:

When we are at the beginning of a function, we must take into consideration that JIT compiler uses _fastcall calling convention. This means that the first two parameters will be passed in the ecx and edx registers. Ecx is used to hold the “this” pointer. We can use those registers to inspect the parameters:

0:000> g

Breakpoint 0 hit

eax=001a6e28 ebx=01ad8880 ecx=01a9f338 edx=01ada608 esi=01ada608 edi=01a9f338

eip=008f1a58 esp=002fe8a4 ebp=00000000 iopl=0 nv up ei ng nz na pe cy

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000287

WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[]):

008f1a58 55 push ebp

0:000> !CLRStack -p

OS Thread Id: 0x17fc (0)

ESP EIP

002fe8a4 008f1a58 WindowsApplication1.Form1.PlayWithArray(Int32[])

PARAMETERS:

this = 0x01a9f338

array = 0x01ada608

...

002ff044 79e7c74b [GCFrame: 002ff044]

0:000> !do @ecx

Name: WindowsApplication1.Form1

...

0:000> !da @edx

Name: System.Int32[]

...

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

...

Note that later in the function, esi is typically used to hold the “this” pointer (gotten from ecx).

Next post: MANAGED DEBUGGING with WINDBG. Threads. Part 1.

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/08/05/managed-debugging-with-windbg-call-stacks-part-2/
Post name: MANAGED DEBUGGING with WINDBG. Call Stacks. Part 2
Original author: Alejandro Campos Magencio
Posting date: 2009-08-05T02:32:00+00:00


Hi all,

This post is a continuation of MANAGED DEBUGGING with WINDBG. Call Stacks. Part 1.

CALL STACKS. Part 2

· We can see the source code of a method in the call stack:

First of all, Source Code mode has to be enabled. We’ll need symbols and source code files correctly configured.

We can choose the frame of the method we want to see:

0:000> knL100

# ChildEBP RetAddr

00 0027e600 79f071ac KERNEL32!RaiseException+0x58

01 0027e660 79f9293a mscorwks!RaiseTheExceptionInternalOnly+0x2a8

02 0027e698 7a129a34 mscorwks!UnwindAndContinueRethrowHelperAfterCatch+0x70

03 0027e738 00371aad mscorwks!JIT_RngChkFail+0xb0

04 0027e780 003719fe WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[])+0x55

05 00000000 7b062c9a WindowsApplication1!WindowsApplication1.Form1.Button6_Click(System.Object, System.EventArgs)+0x86

06 0027e7e8 7b11cb29 System_Windows_Forms_ni!System.Windows.Forms.Control.OnClick(System.EventArgs)+0x6a

...

0:000> .frame 4

04 0027e780 003719fe WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[])+0x55 [C:\__WORKSHOP\Demos\BuggyNETApp\Form1.vb @ 135]

WinDbg will automatically open the source code file and locate where in the function we are in this call stack:

Private Function PlayWithArray(ByVal array As Integer()) As Integer

Dim result As Integer

Try

For i As Integer = 0 To 3

result += array(i)

Next

Catch ex As Exception

result = -1

End Try

Return result

End Function

· We can see the MSIL (Microsoft Intermediate Language) of a method in the call stack:

When compiling to managed code, the compiler translates our source code into Microsoft intermediate language (MSIL), which is a CPU-independent set of instructions that can be efficiently converted to native code. MSIL includes instructions for loading, storing, initializing, and calling methods on objects, as well as instructions for arithmetic and logical operations, control flow, direct memory access, exception handling, and other operations. Before code can be run, MSIL must be converted to CPU-specific code by a just-in-time (JIT) compiler.

To see the IL, we first need to get the method descriptor like this:

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)

...

0:000> !IP2MD 00371aad

MethodDesc: 00116e28

Method Name: WindowsApplication1.Form1.PlayWithArray(Int32[])

Class: 00380a30

MethodTable: 00116ecc

mdToken: 06000039

Module: 00112c3c

IsJitted: yes

m_CodeOrIL: 00371a58

Or directly with:

0:000> !DumpStack -EE

OS Thread Id: 0x1f3c (0)

Current frame:

ChildEBP RetAddr Caller,Callee

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

...

Additionally, if we just hit a breakpoint, we can also use the eip (Instruction Pointer) register for this purpose:

0:000> !IP2MD @eip

MethodDesc: 00116e28

Method Name: WindowsApplication1.Form1.PlayWithArray(Int32[])

...

Now we can take a look to IL code for that method:

0:000> !DumpIL 00116e28

ilAddr = 00d73298

IL_0000: nop

IL_0001: nop

.try

{

IL_0002: ldc.i4.0

IL_0003: stloc.2

IL_0004: ldloc.1

IL_0005: ldarg.1

IL_0006: ldloc.2

IL_0007: ldelem.i4

IL_0008: add.ovf

IL_0009: stloc.1

IL_000a: nop

IL_000b: ldloc.2

IL_000c: ldc.i4.1

IL_000d: add.ovf

IL_000e: stloc.2

IL_000f: ldloc.2

IL_0010: ldc.i4.3

IL_0011: stloc.s VAR OR ARG 4

IL_0013: ldloc.s VAR OR ARG 4

IL_0015: ble.s IL_0104

IL_0017: leave.s IL_002a

} // end .try

.catch

{

IL_0019: dup

IL_001a: call Microsoft.VisualBasic.CompilerServices.ProjectDat::SetProjectError

IL_001f: stloc.3

IL_0020: nop

IL_0021: ldc.i4.m1

IL_0022: stloc.1

IL_0023: call Microsoft.VisualBasic.CompilerServices.ProjectDat::ClearProjectError

IL_0028: leave.s IL_002a

} // end .catch

IL_002a: nop

IL_002b: ldloc.1

IL_002c: stloc.0

IL_002d: br.s IL_002f

IL_002f: ldloc.0

IL_0030: ret

Tip for Reading IL: The CLR is stack-based. A two-operand operation such as Add pops the two top values from the stack and adds them.

ILDASM.exe (Visual Studio.NET) will show the same IL code that we’ve just seen, plus the following declarations just before it:

// Code size 49 (0x31)

.maxstack 3

.locals init ([0] int32 PlayWithArray,

[1] int32 result,

[2] int32 i,

[3] class [mscorlib]System.Exception ex,

[4] int32 VB$CG$t_i4$S0)

And Reflector.exe will show our function like this:

Private Function PlayWithArray(ByVal array As Integer()) As Integer

Dim result As Integer

Try

Dim VB$CG$t_i4$S0 As Integer

Dim i As Integer = 0

Do

result = (result + array(i))

i += 1

VB$CG$t_i4$S0 = 3

Loop While (i <= VB$CG$t_i4$S0)
Catch exception1 As Exception

ProjectData.SetProjectError(exception1)

Dim ex As Exception = exception1

result = -1

ProjectData.ClearProjectError

End Try

Return result

End Function

All this may help us to understand the correspondence between our code and its IL version.

· We can write an image loaded in memory to a file:

You can save a module after patching it on the fly when live debugging, or get the original DLLs or EXEs when debugging a dump. If we save a managed binary, we can use the file with ILDASM.exe or Reflector.exe, for instance. We save a module like this:

0:004> lm

start end module name

00a20000 00a2c000 WindowsApplication1 (deferred)

79e70000 7a3ff000 mscorwks (private pdb symbols) c:\symbols\mscorwks.pdb\62286AFFFC674D5198883B98518936FF2\mscorwks.pdb

...

7afd0000 7bc6c000 System_Windows_Forms_ni (deferred)

0:004> !SaveModule 79e70000 c:\mscorwks.dll

5 sections in file

section 0 - VA=1000, VASize=53abd2, FileAddr=400, FileSize=53ac00

section 1 - VA=53c000, VASize=9, FileAddr=53b000, FileSize=200

section 2 - VA=53d000, VASize=189d0, FileAddr=53b200, FileSize=16000

section 3 - VA=556000, VASize=670, FileAddr=551200, FileSize=800

section 4 - VA=557000, VASize=37bc4, FileAddr=551a00, FileSize=37c00

· We can see the assembly code of a method in the call stack:

If method is jitted, we can see its assembly code as we will do it with any other unmanaged method:

0:000> !DumpStack -EE

OS Thread Id: 0x1f3c (0)

Current frame:

ChildEBP RetAddr Caller,Callee

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

...

0:000> !DumpMD 0x116e28

Method Name: WindowsApplication1.Form1.PlayWithArray(Int32[])

Class: 00380a30

MethodTable: 00116ecc

mdToken: 06000039

Module: 00112c3c

IsJitted: yes

m_CodeOrIL: 00371a58

0:000> uf 00371a58

WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[]) [C:\__WORKSHOP\Demos\BuggyNETApp\Form1.vb @ 129]:

129 00371a58 55 push ebp

129 00371a59 8bec mov ebp,esp

129 00371a5b 57 push edi

129 00371a5c 56 push esi

129 00371a5d 53 push ebx

129 00371a5e 83ec34 sub esp,34h

129 00371a61 33c0 xor eax,eax

129 00371a63 8945e8 mov dword ptr [ebp-18h],eax

...

...

WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[])+0xb5 [C:\__WORKSHOP\Demos\BuggyNETApp\Form1.vb @ 144]:

144 00371b0d 8b45d8 mov eax,dword ptr [ebp-28h]

144 00371b10 8d65f4 lea esp,[ebp-0Ch]

144 00371b13 5b pop ebx

144 00371b14 5e pop esi

144 00371b15 5f pop edi

144 00371b16 5d pop ebp

144 00371b17 c3 ret

Note that if we have symbols we can tell the line number in the source code file.

We have a .NET version of uf command which understands managed code:

0:000> !u 00371a58

Normal JIT generated code

WindowsApplication1.Form1.PlayWithArray(Int32[])

Begin 00371a58, size c0

>>> 00371a58 55 push ebp

00371a59 8bec mov ebp,esp

00371a5b 57 push edi

00371a5c 56 push esi

00371a5d 53 push ebx

00371a5e 83ec34 sub esp,34h

00371a61 33c0 xor eax,eax

00371a63 8945e8 mov dword ptr [ebp-18h],eax

...

00371b16 5d pop ebp

00371b17 c3 ret

You can pass to this command any address within that method and it will show the same assembly and where we are in it (>>>).

Note that we may run this command with an address within an unmanaged method and if we have private symbols and source code, it shows all the assembly in that method along with the source code itself!

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

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/07/24/managed-debugging-with-windbg-call-stacks-part-1/
Post name: MANAGED DEBUGGING with WINDBG. Call Stacks. Part 1
Original author: Alejandro Campos Magencio
Posting date: 2009-07-24T07:37:00+00:00


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 the call 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)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/07/20/managed-debugging-with-windbg-setting-a-breakpoint-part-3/
Post name: MANAGED DEBUGGING with WINDBG. Setting a Breakpoint. Part 3
Original author: Alejandro Campos Magencio
Posting date: 2009-07-20T05:51:00+00:00


Hi all,

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

SETTING A BREAKPOINT. Part 3

· We can step through the code and do some live debugging:

When we reach a breakpoint, for instance, we can go step by step through the code as with any unmanaged application with F10 key or the following command which steps over a function:

0:000> p

Each step executes a single assembly instruction or a single source line, depending on whether the debugger is in assembly mode or source mode. We can change through the modes like this:

0:000> l-t

Source options are 0:

None

0:000> l+t

Source options are 1:

1/t - Step/trace by source line

If Source Mode is On and we have configured the path to the source code of the assemblies we are debugging, WinDbg will automatically open the source code file of the function where debugger just broke.

We can also step into a function with F11 or the following command:

0:000> t

But be careful, as this command won’t always immediately step into the function we want to. Let’s see this with a sample:

Source Mode is On and we are here:

0:000> kL

ChildEBP RetAddr

00000000 7b062c9a WindowsApplication1!WindowsApplication1.Form1.Button6_Click(System.Object, System.EventArgs)+0x77

0028ead8 7b11cb29 System_Windows_Forms_ni!System.Windows.Forms.Control.OnClick(System.EventArgs)+0x6a

...

We are about to call this function:

0:000> u

WindowsApplication1!WindowsApplication1.Form1.Button6_Click(System.Object, System.EventArgs)+0x77 [C:\__WORKSHOP\Demos\BuggyNETApp\Form1.vb @ 120]:

009619ef 8b9758010000 mov edx,dword ptr [edi+158h]

009619f5 8bcf mov ecx,edi

009619f7 3909 cmp dword ptr [ecx],ecx

009619f9 e8c6a983ff call WindowsApplication1.Form1.PlayWithArray(Int32[]) (0019c3c4)

And we want to step into it:

0:000> t

WindowsApplication1.Form1.PlayWithArray(Int32[]):

0019c3c4 b8286e1900 mov eax,196E28h

But wait, we didn’t reach the real code of that function yet! Actually, we are moving the Method Descriptor of our method to eax registry:

0:000> !Name2EE *!WindowsApplication1.Form1.PlayWithArray

...

--------------------------------------

Module: 00192c3c (WindowsApplication1.exe)

Token: 0x06000039

MethodDesc: 00196e28

Name: WindowsApplication1.Form1.PlayWithArray(Int32[])

JITTED Code Address: 00961a58

--------------------------------------

...

Note we may have to turn Source Mode Off before using “t” to go into the function, as sometimes it will step over the function instead (like the “p” command would do). I don’t know why, don’t ask ??

We have to continue a bit more:

0:000> p

WindowsApplication1.Form1.PlayWithArray(Int32[])+0x5:

0019c3c9 90 nop

0:000> p

WindowsApplication1.Form1.PlayWithArray(Int32[])+0x6:

0019c3ca e82197cd79 call mscorwks!PrecodeRemotingThunk (79e75af0)

0:000> p

0019c3cf e984567c00 jmp WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[]) (00961a58)

0:000> p

WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[]):

00961a58 55 push ebp

We finally got where we wanted!

0:000> kL

ChildEBP RetAddr

00000000 009619fe WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[])

00000000 7b062c9a WindowsApplication1!WindowsApplication1.Form1.Button6_Click(System.Object, System.EventArgs)+0x86

But first we had to go through some code in charge of jit compiling our method. In this case, it was already jitted so we just jumped to it.

If the method is not jitted when we step into it, we will run the following asm instead:

0:000> u

WindowsApplication1.Form1.PlayWithArray(Int32[]):

0019c3c4 b8286e1900 mov eax,196E28h

0019c3c9 90 nop

0019c3ca e82197cd79 call mscorwks!PrecodeRemotingThunk (79e75af0)

0019c3cf e930442e00 jmp CLRStub[ThePreStub]@490804 (00490804)

We go into CLRStub[ThePreStub] method and when we reach its ret instruction, our method will be jitted and we will automatically go back to run the same asm as before (at the same memory address), but with just a “little” change:

0:000> u

WindowsApplication1.Form1.PlayWithArray(Int32[]):

0019c3c4 b8286e1900 mov eax,196E28h

0019c3c9 90 nop

0019c3ca e82197cd79 call mscorwks!PrecodeRemotingThunk (79e75af0)

0019c3cf e984567c00 jmp WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[]) (00961a58)

Now we finally jump to our method.

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

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/07/20/managed-debugging-with-windbg-setting-a-breakpoint-part-2/
Post name: MANAGED DEBUGGING with WINDBG. Setting a Breakpoint. Part 2
Original author: Alejandro Campos Magencio
Posting date: 2009-07-20T05:40:00+00:00


Hi all,

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

SETTING A BREAKPOINT. Part 2

· We set the breakpoint:

Regardless if method is jitted, we can set a breakpoint on a method by using its Method Descriptor:

0:004> !BPMD -md 00766d60

MethodDesc = 00766d60

Setting breakpoint: bp 00850A08 [WindowsApplication1.Form1.InitializeComponent()]

If it’s jitted, we can use its jitted code address:

0:004> bp 00850a08

And if we know the name of the method and in which module we can find it, we can set the breakpoint directly like this:

0:004> !BPMD WindowsApplication1 WindowsApplication1.Form1.InitializeComponent

Found 1 methods...

MethodDesc = 00766d60

Setting breakpoint: bp 00850A08 [WindowsApplication1.Form1.InitializeComponent()]

We can check that the breakpoint has been correctly set:

0:004> bl

0 e 00850a08 0001 (0001) 0:**** WindowsApplication1!WindowsApplication1.Form1.InitializeComponent()

If the method is not jitted, the breakpoint will be set whenever it gets jitted, so it won’t appear in the list of breakpoints until then:

0:004> !BPMD WindowsApplication1 WindowsApplication1.Form1.Button1_Click

Found 1 methods...

MethodDesc = 00766db8

Adding pending breakpoints...

0:004> !BPMD -md 00766db8

MethodDesc = 00766db8

Adding pending breakpoints...

· We can enable, disable and remove the breakpoints:

0:004> bl

0 e 00391940 0001 (0001) 0:**** WindowsApplication1!WindowsApplication1.Form1.get_Button6()

1 e 00390a08 0001 (0001) 0:**** WindowsApplication1!WindowsApplication1.Form1.InitializeComponent()

0:004> bd *

0:004> bl

0 d 00391940 0001 (0001) 0:**** WindowsApplication1!WindowsApplication1.Form1.get_Button6()

1 d 00390a08 0001 (0001) 0:**** WindowsApplication1!WindowsApplication1.Form1.InitializeComponent()

0:004> bc 0

0:004> bl

1 d 00390a08 0001 (0001) 0:**** WindowsApplication1!WindowsApplication1.Form1.InitializeComponent()

0:004> be 1

0:004> bl

1 e 00390a08 0001 (0001) 0:**** WindowsApplication1!WindowsApplication1.Form1.InitializeComponent()

Next post: MANAGED DEBUGGING with WINDBG. Setting a Breakpoint. Part 3.

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)