Advert

Managed Debugging and inspecting Jitted code With WinDbg

Managed Debugging and inspecting Jitted code With WinDbg

  • Comments 3

WinDbg is a powerful tool to debug applications. Lately, its use has been extended to managed debugging as well through an extension called sos.dll (Son of Strike). Sos.dll is shipped with both .NET Framework 2.0 and .NET 1.1 Framework and is a WinDbg extension which allows WinDbg to read managed data structures such as Method Tables, Method Descriptors etc. The power of WinDbg is ultimate in detecting memory leaks, deadlocks and unexpected application crashes where Visual Studio debugger cannot reach.

In this post, we will try to debug and inspect the Jitted code for a very simple .NET console application. Note that WinDbg is new to me as well, so if there is any better or shorter way to do this, kindly leave a comment to me.

namespace ConsoleApplication1
{
    class Test
    {
        public int i;
        public int ri()
        {
            i++;
            return i;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Test t = new Test();
 
            int y = t.ri();
 
            Console.Write(y);
        }
    }
}

Upon loading this ConsoleApplication1.exe into debugger, WinDbg breaks at the default debug breakpoint. At this point .NET runtime is not loaded into memory, so SOS cannot be loaded at this moment. We will set a breakpoint on Module load of mscorlib.dll, so that we can load SOS.

Entering the command sxe ld:mscorlib sets this breakpoint. Once it is set, we will go ahead with F5 to run the application. WinDbg will break upon loading on mscorlib.dll :

ModLoad: 790c0000 79bf6000   C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\mscorlib\32e6f703c114f3a971cbe706586e3655\mscorlib.ni.dll
eax=00000001 ebx=00000000 ecx=00000001 edx=00000000 esi=7ffdf000 edi=20000000
eip=7c82ed54 esp=0012e9d0 ebp=0012ea14 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCallRet:
7c82ed54 c3              ret

At this point, SOS can be loaded into memory, with the command .loadby sos mscorwks

Once the SOS is loaded, we can inspect the running threads using !threads

!threads

PDB symbol for mscorwks.dll not loaded ThreadCount: 2 UnstartedThread: 1 BackgroundThread: 1 PendingThread: 1 DeadThread: 0 Hosted Runtime: no PreEmptive GC Alloc Lock ID OSID ThreadOBJ State GC Context Domain Count APT Exception 0 1 1c4 00181fd0 220 Enabled 00000000:00000000 0014cf28 1 Ukn XXXX 2 9d4 001533b8 1400 Enabled 00000000:00000000 0014cf28 0 Ukn (Finalizer)

We can see from the output above that there is one AppDomain at 0014cf28 loaded for the current running process, which makes sense since it is a very simple application.

Lets dump the domain information using !dumpdomain 014cf28

!dumpdomain 014cf28

Domain 1: 0014cf28 LowFrequencyHeap: 0014cf4c HighFrequencyHeap: 0014cfa4 StubHeap: 0014cffc Stage: OPEN SecurityDescriptor: 0014e258 Name: None

Dumping the domain gives us information about the locations of heaps. Note that it also gives the managed loaded modules in the process, but there is none listed since no managed module has been loaded yet (mscorlib is about to be loaded). We can set another breakpoint in WinDbg till the jitting engine (mscorjit.dll) is loaded using sxe ld:mscorjit

The breakpoint is hit as soon as mscorjit.dll is loaded:

ModLoad: 79060000 790b6000   C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorjit.dll
eax=00000001 ebx=00000000 ecx=00000001 edx=00000000 esi=7ffdf000 edi=20000000
eip=7c82ed54 esp=0012e554 ebp=0012e598 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCallRet:
7c82ed54 c3              ret

Now, if we dump the domain again,

Domain 1: 0014cf28
LowFrequencyHeap: 0014cf4c
HighFrequencyHeap: 0014cfa4
StubHeap: 0014cffc
Stage: OPEN
SecurityDescriptor: 0014e258
Name: ConsoleApplication1.exe
Assembly: 00190558 [C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 001905f0
SecurityDescriptor: 0018cdc8
  Module Name
790c2000 C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll

Assembly: 00194388 [C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\ConsoleApplication1\ConsoleApplication1\bin\Release\ConsoleApplication1.exe]
ClassLoader: 00193720
SecurityDescriptor: 001942f0
  Module Name
008f2c3c C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\ConsoleApplication1\ConsoleApplication1\bin\Release\ConsoleApplication1.exe

We can see that it lists, the mscorlib and our application(ConsoleApplication1.exe) as one of the loaded modules. Lets proceed onto setting the breakpoint on the Main() function. First we will need to get the address of method table of class Program by first dumping our module using !dumpmodule command. Once we get the method table, we can dump it using !dumpmt command to get the method descriptor for the Main() function.

!dumpmodule -mt 008f2c3c

!dumpmodule -mt 008f2c3c

Name: C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\ConsoleApplication1\ConsoleApplication1\bin\Release\ConsoleApplication1.exe Attributes: PEFile Assembly: 00194388 LoaderHeap: 00000000 TypeDefToMethodTableMap: 008f00c0 TypeRefToMethodTableMap: 008f00cc MethodDefToDescMap: 008f0118 FieldDefToDescMap: 008f012c MemberRefToDescMap: 008f0134 FileReferencesMap: 008f017c AssemblyReferencesMap: 008f0180 MetaData start address: 00402098 (1548 bytes) Types defined in this module MT TypeDef Name ------------------------------------------------------------------------------ 008f3038 0x02000003 ConsoleApplication1.Program Types referenced in this module MT TypeRef Name ------------------------------------------------------------------------------ 790fd0f0 0x01000001 System.Object

We can dump the method table of ConsoleApplication1.Program using !dumpmt -md 008f3038. The parameter -md is for dumping MethodDesc table.

!dumpmt -md 008f3038

EEClass: 008f1278 Module: 008f2c3c Name: ConsoleApplication1.Program mdToken: 02000003 (C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\ConsoleApplication1\ConsoleApplication1\bin\Release\ConsoleApplication1.exe) BaseSize: 0xc ComponentSize: 0x0 Number of IFaces in IFaceMap: 0 Slots in VTable: 6 -------------------------------------- MethodDesc Table Entry MethodDesc JIT Name 79371278 7914b928 PreJIT System.Object.ToString() 7936b3b0 7914b930 PreJIT System.Object.Equals(System.Object) 7936b3d0 7914b948 PreJIT System.Object.GetHashCode() 793624d0 7914b950 PreJIT System.Object.Finalize() 008fc010 008f3028 NONE ConsoleApplication1.Program.Main(System.String[]) 008fc01c 008f3030 NONE ConsoleApplication1.Program..ctor()

We can see that method descriptor for Main resides at 008f3028.

We can set the breakpoint on Program.Main() method using !bpmd -md 008f3028

MethodDesc = 008f3028
Adding pending breakpoints...

As we run the application, the breakpoint is hit and instruction pointer is placed on the first instruction of the jitted Main() method.

(a0c.1c4): CLR notification exception - code e0444143 (first chance)
JITTED ConsoleApplication1!ConsoleApplication1.Program.Main(System.String[])
Setting breakpoint: bp 00C20070 [ConsoleApplication1.Program.Main(System.String[])]
Breakpoint 0 hit
eax=008f3028 ebx=0012f4ac ecx=01241ec0 edx=00000000 esi=00181fd0 edi=00000000
eip=00c20070 esp=0012f484 ebp=0012f490 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
00c20070 56              push    esi

Finally, to see the generated machine code, use the Unassemble instruction to unassemble the instructions starting from current.

!u eip

   1: Normal JIT generated code
   2: ConsoleApplication1.Program.Main(System.String[])
   3: Begin 00c20070, size 35
   4: >>> 00c20070 56              push    esi
   5: 00c20071 b9b0308f00      mov     ecx,8F30B0h (MT: ConsoleApplication1.Test)
   6: 00c20076 e8a11f7dff      call    003f201c (JitHelp: CORINFO_HELP_NEWSFAST)
   7: 00c2007b 8bd0            mov     edx,eax
   8: 00c2007d ff4204          inc     dword ptr [edx+4]
   9: 00c20080 8b7204          mov     esi,dword ptr [edx+4]
  10: 00c20083 833d8c10240200  cmp     dword ptr ds:[224108Ch],0
  11: 00c2008a 750a            jne     00c20096
  12: 00c2008c b901000000      mov     ecx,1
  13: *** WARNING: Unable to verify checksum for C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\mscorlib\32e6f703c114f3a971cbe706586e3655\mscorlib.ni.dll
  14: 00c20091 e8ce587478      call    mscorlib_ni+0x2a5964 (79365964) (System.Console.InitializeStdOutError(Boolean), mdToken: 06000770)
  15: 00c20096 8b0d8c102402    mov     ecx,dword ptr ds:[224108Ch]
  16: 00c2009c 8bd6            mov     edx,esi
  17: 00c2009e 8b01            mov     eax,dword ptr [ecx]
  18: 00c200a0 ff5074          call    dword ptr [eax+74h]
  19: 00c200a3 5e              pop     esi
  20: 00c200a4 c3              ret

Here are some interesting observations of this jitted code:

  • The call at line number 6 (call 003f201c (JitHelp: CORINFO_HELP_NEWSFAST) is a call to create the instance of any class. The parameter to this method is the method table address which we see being passed in previous line number in ecx. If we dump out that method table using !dumpmt , we will see that it points to class Test.

  • One might be wondering, once the Test Class is instantiated, where is the call to ri()? Interestingly, there is no call and the instructions are inlined as we see in line numbers 7 and 8. inc instruction is equivalent to i++. I do not exactly know why it has been inlined , perhaps may be because compiler optimizations were enabled in Visual Studio. Or if there are any rules governing the inlining, I do not know them yet.

  • Lines number 14 - 16 are simply initialization of Console to prepare it for emitting text using Write() method. This Write() method is finally called at line 18. If you set the breakpoint on line 18 and step through it, you will see 1 being outputted on your screen :)
Leave a Comment
  • Please add 4 and 8 and type the answer here:
  • Post
Page 1 of 1 (3 items)