Debugging base class method with conditional break point in .NET using Windbg
In this post I am going to be demonstrating how to have a conditional break-point on the base class method where it has been used by multiple derived classes. A classic example is Winform UI.
The Control base class has got methods like set_Enabled , set_Visible which could be consumed by multiple derived controls. The goal is to debug only the control instance that we are interested in. Here is the sample code.
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button3.Click += (s, b) => button1.Enabled = button1.Enabled ? false : true;
button4.Click += (s, b) => button2.Enabled = button2.Enabled ? false : true;
}
}
}
This app that has four buttons. On the click of button3 it toggles button1’s enabled property and the button 4 does the same for button2. The goal is to break only when button1′s enabled property is set.
Launched the app and attached it to windbg and loaded sosex, issued the command
!mbm *Control.set_Enabled
to set a break-point on enabled method. FYI set_Enabled is not available in the button class because it is derived from the base class. The goal is to break only when the button1’s enabled property is changed. With the above command it will break every time and here is the output when the break-point hits
rax=000007fede335c40 rbx=0000000000060b28 rcx=000000000242ed18
rdx=0000000000000000 rsi=0000000000000000 rdi=000000000242ed18
rip=000007fede335c4d rsp=000000000015dde0 rbp=000000000015df80
r8=0000000002464500 r9=0000000000000000 r10=000007fffff10018
r11=000000000015dd20 r12=00000000002f5980 r13=000000000015dea8
r14=000000000015e380 r15=0000000000100000
iopl=0 nv up ei pl nz na pe nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
System_Windows_Forms_ni+0x2b5c4d:
000007fe`de335c4d 488bcf mov rcx,rdi
The rcx register contains the reference of the instance of the class , example button1 / button2. And here is the command to get the offset of the button text
.shell -ci "!do @rcx" findstr /E text
and the output of the command is
0:000> .shell -ci”!do @ecx” findstr /E text
000007fef6db6960 40001c2 40 System.String 0 instance 000000000242ec50 text
000007fef6db5ab8 400016d 280 System.Object 0 static 0000000002403d80 EventBindingContext
.shell: Process exited
The reason behind getting the button text offset is to use the text as the property for conditional break-point. The text member is available in the 40 offset of the button class. Now that we have the offset of the text property lets try and reset the break-point with a condition. To do this get the address of the break-point using the bl command and the output is
0:000> bl
0 e 000007fe`de335c4d 0001 (0001) 0:**** System_Windows_Forms_ni+0x2b5c4d
The address of the set_Enabled function is 000007fe`de335c4d. Here is the conditional break-point for button1
bp 000007fe`de335c4d "as /mu ${/v:name} (poi(@rcx+40)+c);.block{ .if (0== $scmp( \"${name}\", \"button1\") ) { .echo 'in button1';gc } .else { gc}}"
The “as /mu ${/v:name} (poi(@rcx+40)+c)” will set the value of text property to the variable “name”. The .block command is used to evaluate the variable “name” and the rest is a simple .if .else command. Every time the button1 is clicked it would output “in button1” and continue. Now we have managed to break-in only on the button1 click.
Awesome article, thanks for this!
Marc
Marc Sherman
June 17, 2010 at 2:31 pm
Thanks Marc.
Naveen
June 17, 2010 at 2:49 pm