With my few years of production debugging .NET code ,one thing that has really helped me a lot is Windbg. Lot us of know that using sos, sosex and Windbg we should be able to troubleshoot most of the .NET Code. But certain tips / tricks makes us productive in those crucial moments. I am assuming that you are aware of basic usage of sos and windbg.

We know by using !bpmd command we can stick in a break-point on a method. But the issue is we would want to break in to the debugger only on a certain condition, very similar to VS.NET break-point condition.

Here is the sample code that i am going to be using to set the conditional break-point. This is a simple Winforms app.

  1: using System;
  2: using System.Windows.Forms;
  3:
  4: namespace WindowsFormsApplication2
  5: {
  6:     public partial class Form1 : Form
  7:     {
  8:         public int Foo;
  9:
 10:         public Form1()
 11:         {
 12:             InitializeComponent();
 13:         }
 14:
 15:         private void Button1Click(object sender, EventArgs e)
 16:         {
 17:             Test(textBox1.Text);
 18:         }
 19:
 20:         void Test(string s)
 21:         {
 22:             Console.WriteLine(s);
 23:         }
 24:
 25:     }
 26: }
 27:

In this sample code I would like to put a conditional break-point on the Test method. After attaching the application to windbg ,look for the object Form1 in the heap.

0:000> !dumpheap -type WindowsFormsApplication2.Form1
Address MT Size
0000000002c92548 000007ff004b7b78 480
total 1 objects
Statistics:
MT Count TotalSize Class Name
000007ff004b7b78 1 480 WindowsFormsApplication2.Form1
Total 1 objects

The next step was to get the address of the function Test.

0:000> .shell -ci “!dumpmt -md 000007ff004b7b78” FIND “Test”
000007ff004a3508 000007ff004b7af0 JIT WindowsFormsApplication2.Form1.Test(System.String)
.shell: Process exited

The reason to get address of the function is to use the native “bp” command to set the break-point. FYI the sos also uses only the built in bp command for setting the break-point. The difference is condition that we can pass to the break-point. In the above I use the .shell command to look for Test function address instead of manually looking for the Test function. The .shell command comes in very handy.

In this exercise I would like to break into the debugger only if the argument “s” matches certain condition. I set the bp on the method test using the command “bp 000007ff004a3508”. And here is the result when the break-point hits.

0:000> g
Breakpoint 0 hit
rax=0000000002d9fcd8 rbx=0000000000000000 rcx=0000000002c92548
rdx=0000000002d9fcd8 rsi=0000000000000001 rdi=0000000000000000
rip=000007ff004a3508 rsp=000000000028d3e8 rbp=000000000028d590
r8=000000000028cde0 r9=000007feed0b14c0 r10=000007feff469f20
r11=000007ff00060120 r12=00000000003a9460 r13=0000000000000202
r14=000000001b3e23b8 r15=0000000000030672
iopl=0 nv up ei pl nz na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
000007ff`004a3508 e9f35a2c00 jmp 000007ff`00769000

FYI I am using a 64-bit machine and that’s the reason my pointers are much bigger than usual x86. We are interested on the argument “s”  that is passed to the method which is @rdx register “rdx=0000000002d7ed00”. To verify that we setting the break-point on the correct argument we can test it by using command

0:000> .printf “%mu”,@rdx+10
testa

Not many of them are aware of how to get just the string from string object , instead of the all additions from !dumpobj. The above command would get just the string . The command .printf contains “%mu” because it is null terminated unicode string and @rdx is the register which contains the argument “s”. The “@rdx+10” is the actual location of the string in memory and for the x86 it would be “@rdx+c” for the actual string. Now that we are sure the @rdx is the register we can build condition for the argument. Here it is

bp 000007ff004a3508 “.block {as /mu ${/v:cmp} @rdx+10; .if ( $spat( “${cmp}”, “*test*” ) ) { !clrstack; } .else { gc }}”

And here is the detailed explanation of the above condition within quotes. The .block command is used for alias evaluation. Alias is like variables within windbg. The “as /mu ${v:cmp} @rdx+10” command creates an string alias by name  of cmp which contains the value of argument “s”. This condition would be evaluated only when the functions first line of code is executed so @rdx will always have the value that is passed to the function. The next

“.if ( $spat( “${cmp}”, “*test*” )  ) { !clrstack; } .else { gc }}”

command is real crux where the code compares the alias cmp with “*test*”. Notice i am using a built-in function called $spat which is nothing but a string pattern function. So from the above condition i am instructing the break-point to give a callstack if the argument “s” has something like “*test*” . If not the command “gc” means go to the next conditional break-point,similar  F5 in VS.NET. So, for example if the function is called 40 times and of which we are interested only once when it is  something like “*test*” ,with the existing  !bpmd we would wasted our time 39 times.

The “” is a escape character in windbg ,i am using it because i would have to a string compare.