Debugging NetCF apps with cordbg - Part V: Breakpoints, stepping and setip

Debugging NetCF apps with cordbg - Part V: Breakpoints, stepping and setip

  • Comments 2

In the first four parts of this series, we have covered the background data needed to debug .NET Compact Framework applications with cordbg.exe (getting connected, handy modes, unsupported commands and v2 beta 1).  For the remainder of this series, I will be talking about how to use some of the common cordbg commands.  Unless otherwise specified, what I talk about will apply equally to debugging any of the Microsoft .NET frameworks.

And on to part V -- breakpoints and stepping.

Commands used / referenced in this post:
-- breakpoints --
b[reak]             Set or display breakpoints
stop                Set or display breakpoints
del[ete]            Remove one or more breakpoints
rem[ove]            Remove one or more breakpoints
-- process control --
cont                Continue the current process
g[o]                Continue the current process
setip               Set the next statement to a new line
k[ill]              Kill the current process
ex[it]              Kill the current process and exit the debugger
q[uit]              Kill the current process and exit the debugger
-- stepping --
n[ext]              Step over the next source line
so                  Step over the next source line
i[n]                Step into the next source line
si                  Step into the next source line
s[tep]              Step into the next source line
o[ut]               Step out of the current function
-- data manipulation --
p[rint]             Print variables (locals, args, statics, etc.)
set                 Modify the value of a variable (locals, statics, etc.)

Setting and removing breakpoints
Note: The b[reak] and stop commands are equivalent.  I will be using b[reak] in my examples.  Cordbg allows you to set breakpoints by source line number or by name (I'm ignoring by address since that is only
supported in Win32 mode).  The syntax for b[reak] can be viewed by issuing the following in cordbg: "? b".  Please be aware that breakpoints in cordbg are case-sensitive.

Below are some examples breakpoints from a simple debugging session:
(cordbg) b 59
Breakpoint #1 has bound to <path>\TestDebuggee.exe.
#1      <path>\TestDebuggee.cs:59       Main+0xd5(il) [active]
(cordbg) b SecondaryClasses.cs:28
Breakpoint #2 has bound to <unknown>.
#2      <path>\TestDebuggee.exe!<path>
\SecondaryClasses.cs:28    ReturnMagicNumber+0x7(il) [active]

You might have noticed that I didn't provide any examples of setting breakpoints by method name.  Unfortunately, as of NetCF v2 beta 1, this feature is not supported when debugging .NET Compact Framework projects.  If you try, you will see something like this:
(cordbg) b TestDebuggee!NetCF.Test.TestDebuggee::PrivateStaticMethod
Error: hr=0x80004001, Not implemented
#1      <path>\TestDebuggee!NetCF.Test.TestDebuggee::PrivateStaticMethod:0       [unbound]

When viewing your current breakpoints, you may have noticed "[active]" or "[unbound]" notation.  Active indicates that the required module as been loaded and that the debugger has successfully set (bound) the breakpoint.  Unbound indicates that the debugger has not been able to bind the breakpoint.  Bind failures can occur for a number of reasons: unable to locate the requested location (case-sensitivity problem, requested method doesn't exist), the target module / class has not yet been loaded, etc.  Unbound breakpoints will not be hit.

As with b[reak] and stop, del[ete] and rem[ove] perform the same task.  The syntax for rem[ove] can be viewed using "? rem".

Process control
Note: The cont and g[o] commands are equivalent -- I like g[o] best because it can be shortened to just "g".  Once
you have your breakpoint(s) set, you typically want to let your process run until it gets to where you want it to be.  To do so, use g[o]Once you are finished debugging, you can either use g[o] to let the application gracefully exit -or- you can forcably terminate the process using the k[ill], ex[it] or q[uit] commands.  Like many other commands ex[it] and q[uit] are aliases for each other, and both cause the debuggee to terminate and then exit cordbg.  The k[ill] command terminates your debuggee and keeps cordbg running (with all breakpoints switching from active to unbound).  Re-connecting to the debuggee will restore your existing breakpoints.

If you are running NetCF v2 beta 1 (or the full .NET Framework), you can set the next statement using the setip command (use "? setip" for command syntax).  A couple of things to watch for when using setip:
1) Changing the instruction pointer can be "dangerous" -- it's real easy to skip over required initialization statements and crash your app
2) You cannot set the instruction pointer outside of the current function
3) You cannot set the instruction pointer into a catch block
Keeping the above in mind, setip is real handy for retrying operations with different values (see example
below -- blank lines removed for space considerations).

(cordbg) setip 85
IP set successfully.
(cordbg) n
086:             Int32 startVal  = -7041776;
(cordbg) n
087:             Console.WriteLine(i.ToString());
(cordbg) n
088:             Console.WriteLine("Calling CallProtectedMethod");
(cordbg) n
089:             i = Math.Abs(startVal) % 27;
(cordbg) n
090:             s = tdc.CallProtectedMethod(i);
(cordbg) p i
i=14
(cordbg) set startVal 72
startVal=72
(cordbg) setip 89
IP set successfully.
(cordbg) n
090:             s = tdc.CallProtectedMethod(i);
(cordbg) p i
i=18
(cordbg)

In the above example, I set the instruction pointer and stepped through (more on this below) the code as-is.  I then changed the value of startVal  (using the set command) and reset the instruction pointer to let me retry the calculation.  I used the p[rint] command to display the results of the math.

Stepping
Stepping lets you slow down your application to a single line of source (or IL statement, but that's for another post) at a time.  For those new to debugging, this lets you check variable state, etc every step of the way, so you can find where the bad stuff (data corruption, incorrect calculation, etc) is happening.  There are three types of steps (remember, we're talking high-level source here, not IL): step over, step in, step out.

Step over is the most common stepping method used when debugging (it's what I used in my example above).  For those familiar with Visual Studio's debugger, this is the same as the F10 key.  Cordbg gives you two commands to step over source: n[ext] and so.

Step in -- Visual Studio F11 key -- lets you enter the function your code is calling.  I step into functions when I have found that the returned data is not what I expected.  There are two commands to step into a function: i[n] and si.  Here's an example (empty lines removed):

(cordbg) setip 85
IP set successfully.
(cordbg) n 5
086:             Int32 startVal  = -7041776;
087:             Console.WriteLine(i.ToString());
088:             Console.WriteLine("Calling CallProtectedMethod");
089:             i = Math.Abs(startVal) % 27;
090:             s = tdc.CallProtectedMethod(i);
(cordbg) i
089:             return this.TestMethod1(i);
(cordbg) i
106:             Console.WriteLine("Derived: TestMethod1");
(cordbg) sh
101:             this.m_AString = "private to derived class";
102:         }
103:
104:         protected override String TestMethod1(Int32 i)
105:         {
106:*            Console.WriteLine("Derived: TestMethod1");
107:
108:             String s = "";
109:             for(Int32 count = 0; count < i; count++)
110:             {
111:                 s += this.m_AString;
(cordbg)

The line with the * next to it is the position of the instruction pointer.  The first i[n] command stepped me into CallProtectedMethod(), the second into TestMethod1().  From the position of the instruction pointer, I can see that using the i[n] command one more time would take me into the Console.WriteLine method (part of the .NET BCL) -- since I'm not using mscorlib symbols, I would see IL code instead of source (again, IL is for a future post).

Step out (o[ut]) will complete whatever function you are currently debugging and return you to the caller.  This is handy when you are finished debugging a function you stepped into, but want to continue debugging the calling function.

Each of these commands support providing a line count (the default is 1).  You can "speed step" through an number of source lines using the count argument (ex: “n[ext] 10“ to step over the next 10 lines) -- I used this in the "step in" example, above .  Keep in mind, when specifying the line count for the stepping commands, whatever you specify becomes the new "default" (ex: "n[ext] 5" followed by "n[ext]" will step over 10 lines, collectively).  To return to the initial default count setting for the stepping commands, use a count of 1 (ex: “n[ext] 1“).

Have fun!
-- DK

Disclaimers:
This posting is provided "AS IS" with no warranties, and confers no rights.
Some of the information contained within this post may be in relation to beta software.  Any and all details are
subject to change.

Page 1 of 1 (2 items)