MANAGED DEBUGGING with WINDBG. Breaking on an Exception. Part 2

Hi all,

This post is a continuation of MANAGED DEBUGGING with WINDBG. Breaking on an Exception. Part 1.

BREAKING ON AN EXCEPTION. Part 2

What happens if !PrintException doesn’t return anything, or when we break (i.e. with a breakpoint or manually – Ctrl+Break) we can’t see mscorwks!RaiseTheExceptionInternalOnly in the call stack? What if we already missed the exception when we break?

We could still find the exception near the top of the managed stack of the current thread:

0:000> !DumpStackObjects

...

0:000> !dso

OS Thread Id: 0x15e4 (0)

ESP/REG Object Name

0020eb50 0195fe94 System.IndexOutOfRangeException

0020eb98 0195fe94 System.IndexOutOfRangeException

0020ebdc 01929a04 WindowsApplication1.Form1

0020ebe0 01941958 System.Int32[]

...

0:000> !pe 0x0195fe94

...

If not, we can try to find significant exceptions in the managed heap, as all recent exceptions are stored there:

0:000> !DumpHeap -type Exception

Address MT Size

01991024 790fe044 72

0199106c 790fe0e0 72

019910b4 790fe17c 72

019910fc 790fe284 72

01991144 790fe284 72

0199f848 79103c58 12

0199f860 79103ca4 12

019df490 79117f8c 72

019fc5c8 79117f8c 72

total 9 objects

Statistics:

MT Count TotalSize Class Name

79103ca4 1 12 System.Text.DecoderExceptionFallback

79103c58 1 12 System.Text.EncoderExceptionFallback

790fe17c 1 72 System.ExecutionEngineException

790fe0e0 1 72 System.StackOverflowException

790fe044 1 72 System.OutOfMemoryException

79117f8c 2 144 System.IndexOutOfRangeException

790fe284 2 144 System.Threading.ThreadAbortException

Total 9 objects

0:000> !DumpHeap -mt 79117f8c

Address MT Size

019df490 79117f8c 72

019fc5c8 79117f8c 72

total 2 objects

Statistics:

MT Count TotalSize Class Name

79117f8c 2 144 System.IndexOutOfRangeException

Total 2 objects

0:000> !pe 019df490

...

0:000> !pe 019fc5c8

...

We can also do the following to find all exceptions of a given type in the managed heap:

0:000> .foreach(ex {!dumpheap -type System.IndexOutOfRangeException -short}){!pe ex;.echo *************}

Exception object: 019df490

Exception type: System.IndexOutOfRangeException

Message: Index was outside the bounds of the array.

InnerException: <none>

StackTrace (generated):

SP IP Function

0017EE80 00861AAD WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[])+0x55

StackTraceString: <none>

HResult: 80131508

*************

Exception object: 019fc5c8

Exception type: System.IndexOutOfRangeException

Message: Index was outside the bounds of the array.

InnerException: <none>

StackTrace (generated):

<none>

StackTraceString: <none>

HResult: 80131508

*************

Note that exception objects are normally created when they are needed. But there are a few exception objects which are created when process starts as they can’t be created when they need to be raised: OutOfMemoryException, ExecutionEngineException, StackOverflowException. So we will always see these exceptions in the heap. That doesn’t mean that they were ever raised.

· We can inspect the method where we got the exception:

We can get i.e. the source code line which failed:

0:000> !pe 019df490

Exception object: 019df490

Exception type: System.IndexOutOfRangeException

Message: Index was outside the bounds of the array.

InnerException: <none>

StackTrace (generated):

SP IP Function

0017EE80 00861AAD WindowsApplication1!WindowsApplication1.Form1.PlayWithArray(Int32[])+0x55

StackTraceString: <none>

HResult: 80131508

0:000> !u 00861AAD

Normal JIT generated code

WindowsApplication1.Form1.PlayWithArray(Int32[])

Begin 00861a58, size c0

00861a58 55 push ebp

...

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

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

00861aa8 e8d67e8c79 call mscorwks!JIT_RngChkFail (7a129983)

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

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

00861ab3 e8837f8c79 call mscorwks!JIT_Overflow (7a129a3b)

...

00861b16 5d pop ebp

00861b17 c3 ret

0:000> u 00861aa8

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

00861aa8 e8d67e8c79 call mscorwks!JIT_RngChkFail (7a129983)

...

Note that not all exceptions we saw have the StackTrace field set. I’ve seen that when the debugger just breaks on an exception, the field is empty. Once we go beyond that point and we i.e. enter the “catch” section of the “try{}catch(){}” statement in the code, that field is set.

But if we just broke on an exception, we can still see the call stack with k, !CLRStack, !DumpStack and related commands as we saw in the Call Stack section before, and get the instruction pointer (IP) we need from there.

· We can stop breaking on CLR exceptions:

If we don’t want to break on any other CLR exception we do the following:

0:000> sxn clr

If we were breaking on specific exception types ( !StopOnException) before, and we stop breaking on all CLR exceptions, we will still see those exceptions in the debugger:

0:000> g

(1728.1f58): C++ EH exception - code e06d7363 (first chance)

(1728.1f58): CLR exception - code e0434f4d (first chance)

'System.IndexOutOfRangeException hit'

Next post: MANAGED DEBUGGING with WINDBG. Locks.

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)