Setting a breakpoint in managed code using Windbg

One of the great features of managed code is getting call stacks and proper class and member function names without debug symbols. We can examine the methods of a class and set breakpoints based on name.

To see the methods on an object in Windbg we first need to find its method table. We can do this with the !name2ee command which will look up information about a class.

0:004> !Name2EE *!WindbgDemo.Form1
Module: 790c2000 (mscorlib.dll)
Module: 00912380 sortkey.nlp)
Module: 00912010 (sorttbls.nlp)
Module: 008f2d5c (WindbgDemo.exe)
Token: 0x02000004
MethodTable: 008f5b5c
EEClass: 008f15dc
Name: WindbgDemo.Form1
Module: 7b442000 (System.Windows.Forms.dll)
Module: 7a714000 (System.dll)
Module: 7ae72000 (System.Drawing.dll)

With the method table address we can examine all the methods defined on the object.

0:004> !DumpMT -MD 008f5b5c
EEClass: 008f15dc
Module: 008f2d5c
Name: WindbgDemo.Form1
mdToken: 02000004 (C:\Testing\WindbgDemo\WindbgDemo\bin\Release\WindbgDemo.exe)
BaseSize: 0x16c
ComponentSize: 0x0
Number of IFaces in IFaceMap: 15
Slots in VTable: 383
MethodDesc Table
Entry MethodDesc JIT Name
7b05c298 7b4a5518 PreJIT System.Windows.Forms.Form.ToString()
793539c0 7913bd50 PreJIT System.Object.Equals(System.Object)
793539b0 7913bd68 PreJIT System.Object.GetHashCode()
7a4a6510 7a75bf58 PreJIT System.ComponentModel.Component.Finalize()
79361e50 7913dbd8 PreJIT System.MarshalByRefObject.GetLifetimeService()
79360770 7913dbe0 PreJIT System.MarshalByRefObject.InitializeLifetimeService()
--------------- snip ----------------
7b06621c 7b4a5528 PreJIT System.Windows.Forms.Form.OnResizeBegin(System.EventArgs)
7b0662a4 7b4a5530 PreJIT System.Windows.Forms.Form.OnResizeEnd(System.EventArgs)
008f62a0 008f5ac8 JIT WindbgDemo.Form1.InitializeComponent()
008f627c 008f5ad0 JIT WindbgDemo.Form1..ctor()
008f62bc 008f5ad8 NONE WindbgDemo.Form1.btnLockBlock_Click(System.Object, System.EventArgs)
008f6249 008f5ae0 NONE WindbgDemo.Form1.LockBlock()
008f62d0 008f5ae8 NONE WindbgDemo.Form1.btnException_Click(System.Object, System.EventArgs)
008f6251 008f5af0 NONE WindbgDemo.Form1.CauseException()
008f630c 008f5af8 NONE WindbgDemo.Form1.btnOtherException_Click(System.Object, System.EventArgs)
008f6259 008f5b00 NONE WindbgDemo.Form1.CauseOtherException()
008f62e4 008f5b08 NONE WindbgDemo.Form1.btnMemoryGrowth_Click(System.Object, System.EventArgs)
008f62f8 008f5b10 NONE WindbgDemo.Form1.btnBreakpoint_Click(System.Object, System.EventArgs)
008f6265 008f5b18 NONE WindbgDemo.Form1.Foo()

For the curious we can see whether the function is ngen'ed code or if it has been JITted yet. PreJIT means ngen code, JIT and NONE is whether the function has been JITted or not. From here we can set a breakpoint.

0:004> !bpmd -md 008f5b10
MethodDesc = 008f5b10
Adding pending breakpoints...

It's a pending breakpoint because this code has not been jitted yet and thus has no place to put the debug breakpoint. You can also set breakpoints directly by name using !bpmd.