Razor#

Because we all love bleeding edge technology

Month List

Search

Calendar

<<  May 2013  >>
MoTuWeThFrSaSu
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

View posts in large calendar

Managed (.NET) breakpoints using WinDBG

I often find myself needing to debug production applications and .NET applications I did not write myself or third party components in my own applications. Often, when doing so, we find ourselves thinking “Geez, I wonder if this component or this application is invoking this method at this point”, or “I’d like to be able to break on exception System.Foo, so I can see what is happening”. You can’t install Visual Studio on productions server, and even if you could, it wouldn’t help if you’re not debugging your own application. Do note this post only applies to .NET 2.0

Fortunately, WinDBG can help, and it isn’t as hard as one would think!

First, load up WinDBG, and attach to the process we want to debug:

image

 

For this exercise, I made a small application that makes an instance of a class, and invokes two methods within it, one that returns a Hello World, and one that throws an exception:

class Program
{
    static void Main(string[] args)
    {
        Console.ReadKey(true);
 
        BrokenClass broken = new BrokenClass();
        broken.HelloWorld();
        broken.BrokenWorld();
    }
}
namespace PhoenixMatrix.Windbg.Demo
{
    public class BrokenClass
    {
        public string HelloWorld()
        {
            return "Hello World!";
        }
 
        public void BrokenWorld()
        {
            throw new NotSupportedException("Hello World!");
        }
    }
}

 

 

 

Simple enough. All right, lets get started. So we have our process running, and it is waiting on a user action (Read key). Then, we have multiple options, depending on what we want to do.

Option A – Break on all Exceptions

Without doing anything else, we use the following commands:

sxe clr

!loadby sos mscorwks

What this does, is instructing WinDBG to break on all CLR exceptions, both first chance (the ones that didn’t bubble up yet) and second chance (the ones that did bubble up, usually because there was no catch blocks waiting for them). The second command, !loadby sos mscorwks, will load up a special set of extensions especially made to debug managed code. To be sure we’re loading the version that matches the .NET framework we are using, we load sos.dll at the same location mscorwks (part of the .NET runtime) was loaded. If we run:

g

our process now continues, we hit any key, and sure enough, WinDBG breaks on an exception. Now let see where it stopped (from now on I will be showing the output of WinDBG, bolding and underlining the command I executed)

0:000> !pe
Exception object: 01cd8988
Exception type: System.NotSupportedException
Message: Hello World!
InnerException: <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131515

!pe, shortcut for !PrintException, will give us the details of the exception. Now there’s no stacktrace because this is a first chance exception. Lets hit “g” to go a step further. The exception will enter second chance stage, and we can print it out for real this time:

0:000> !pe
Exception object: 01cd8988
Exception type: System.NotSupportedException
Message: Hello World!
InnerException: <none>
StackTrace (generated):
    SP       IP       Function
    002FEE38 001C01A6 PhoenixMatrix_Windbg_Demo!PhoenixMatrix.Windbg.Demo.BrokenClass.BrokenWorld()+0x46
    002FEE48 001C00CE PhoenixMatrix_Windbg_Demo!PhoenixMatrix.Windbg.Demo.Program.Main(System.String[])+0x5e
 
StackTraceString: <none>
HResult: 80131515

Now we have the stack trace, complete with method information. This is great if we want to break on any and all exception, but what if we wanted to break on a particular one?

Option B – Break on a specific exception

Instead of simply using sxe clr, we can create a breakpoint on a particular exception, as follow (Note that it is possible at this point WinDBG will give a big output complaining about lack of symbols. You can ignore that safely, i just removed them from my output below):

 

0:003> !soe -create System.NotSupportedException 1
Breakpoint set
 

The !soe (Stop On Exception) lets us create a breakpoint on a particular exception. Note that you must fully qualify the name of the exception, with the namespace. The number (1 in this case) after the exception is a “speudo register”, which can be used in more advanced WinDBG scripted commands to examine the result of the breakpoint. For now we don’t need that, but it is still needed to set a normal breakpoint. Now, lets allow our application to continue with “g”, hit a key, and see what we get.

0:000> !pe
Exception object: 01de8988
Exception type: System.NotSupportedException
Message: Hello World!
InnerException: <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131515

 

Great! same result as last time! However, if other exceptions had been thrown first (common in an ASP.NET application, for example), we would have ignored name, and only stopped on System.NotSupportedException. Now, this is great for exceptions. But what if we wanted to break on a method?

Option C – Break on a method name

It is possible to have a breakpoint on an actual method. First, we need to do the initial steps again (attack to the process, load sos), and run the following:

0:003> !bpmd PhoenixMatrix.Windbg.Demo.exe PhoenixMatrix.Windbg.Demo.BrokenClass.HelloWorld
Found 1 methods...
MethodDesc = 003e3060
Adding pending breakpoints...

 

That one is a mouthful. !bpmd (break point method description) lets us break on a method within a module. In C#, the module is virtually always the fully qualified name of the executable or the dll (so, the assembly) the method is in. So we type !bpmd, followed by the full module name, followed by the fully qualified method name, complete with namespace and parent class. We also see this added a “pending” breakpoint in our case: that is because the method has not been JITted, or compiled to native code, yet. Now lets allow our program to continue.

 

0:003> g
(16b8.14bc): CLR notification exception - code e0444143 (first chance)
JITTED PhoenixMatrix.Windbg.Demo!PhoenixMatrix.Windbg.Demo.BrokenClass.HelloWorld()
Setting breakpoint: bp 00990120 [PhoenixMatrix.Windbg.Demo.BrokenClass.HelloWorld()]
Breakpoint 0 hit
eax=003e3060 ebx=0038ecfc ecx=01c18950 edx=01c1895c esi=004b5308 edi=00000000
eip=00990120 esp=0038ecb4 ebp=0038ecd0 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
00990120 55              push    ebp

 

And here we see that the breakpoint worked, and stopped on the now JITted HelloWorld method. We can also pull the stacktrace, to see how that method was invoked:

 

0:000> !clrstack
OS Thread Id: 0x14bc (0)
ESP       EIP     
0038ecb4 00990120 PhoenixMatrix.Windbg.Demo.BrokenClass.HelloWorld()
0038ecb8 009900c2 PhoenixMatrix.Windbg.Demo.Program.Main(System.String[])
0038eeec 71cf1b4c [GCFrame: 0038eeec] 

 

While using !bpmd is great in cases where we are sure of the method’s name and module, do keep in mind that the compiler will happily mangle the method names when it suits its needs (such as public property definitions, which are compiled to get/set methods), and that it is possible to define custom modules in certain languages (such as VB.NET), so it is not always trivial.

Option D – Break directly on the method

With the following commands, we can fetch the actual pointer to the method, and break on it, as well as browse the methods in the class, letting us more easily select the one we want. First, we need to find our class within the available modules.

0:003> !name2ee * PhoenixMatrix.Windbg.Demo.BrokenClass
Module: 706f1000 (mscorlib.dll)
--------------------------------------
Module: 002c2c5c (PhoenixMatrix.Windbg.Demo.exe)
Token: 0x02000003
MethodTable: 002c3080
EEClass: 002c134c
Name: PhoenixMatrix.Windbg.Demo.BrokenClass

 

!name2ee will get us the class information within a module. Using the * as the second parameter will search all modules, but if we wanted, we could give it the fully qualified module name instead (in this case, PhoenixMatrix.Windbg.Demo.exe). The command will go through the modules, and if it finds the Class, outputs its details. The part we’re interested in is the MethodTable, so we can output all of the class’s methods.

0:003> !dumpmt -md 002c3080
EEClass: 002c134c
Module: 002c2c5c
Name: PhoenixMatrix.Windbg.Demo.BrokenClass
mdToken: 02000003  (C:\PhoenixMatrix.Windbg.Demo.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 7
--------------------------------------
MethodDesc Table
   Entry MethodDesc      JIT Name
708b6aa0   70734924   PreJIT System.Object.ToString()
708b6ac0   7073492c   PreJIT System.Object.Equals(System.Object)
708b6b30   7073495c   PreJIT System.Object.GetHashCode()
70927410   70734980   PreJIT System.Object.Finalize()
002cc045   002c3078     NONE PhoenixMatrix.Windbg.Demo.BrokenClass..ctor()
002cc03d   002c3060     NONE PhoenixMatrix.Windbg.Demo.BrokenClass.HelloWorld()
002cc041   002c306c     NONE PhoenixMatrix.Windbg.Demo.BrokenClass.BrokenWorld()

 

Now this is a lot of information, but !dumpmt simply dumps out the method table’s information. The last part is what we’re interested in, the list of method. Notice that we can see the JIT state of the methods (remember when I said HelloWorld was not JITted yet? We see it now, though that doesn’t stop us from setting a breakpoint on it). Lets take the MethodDesc code of the method we want to break on, HelloWorld (so the second code from the left)

 

0:003> !bpmd -md 002c3060
MethodDesc = 002c3060
Adding pending breakpoints...

 

 

The breakpoint is successfully set. If we run the application, we can see it will correctly break on HelloWorld, as per the clrstack again:

 

0:000> !clrstack
OS Thread Id: 0x1074 (0)
ESP       EIP     
002bf264 008e0120 PhoenixMatrix.Windbg.Demo.BrokenClass.HelloWorld()
002bf268 008e00c2 PhoenixMatrix.Windbg.Demo.Program.Main(System.String[])
002bf4a0 71cf1b4c [GCFrame: 002bf4a0] 

 

And there you have it, the primary ways of setting breakpoints using WinDBG on managed code. These little tricks have tons of applications, such as finding out the behavior of third party components, debugging third party applications (SharePoint comes to mind), as well as doing the diagnostic of nasty production issues that you simply cannot reproduce. And since WinDBG doesn’t need to load all symbols the way Visual Studio does before letting you use it, it can be a real timesaver, once you get the hang of it.

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Permalink | Comments (0) | Post RSSRSS comment feed

Comments