Debugging NetCF Applications using cordbg.exe - Part II

In Part I of NetCF debugging with cordbg, I showed you how to get the debugger connected to your process. In this part, I'm going to talk a little about cordbg's mode command.

But before we get to that, I had a question from a reader as to why the "launch me debuggable" command line argument (//i) starts with "//" instead of the more traditional "/" (or "-"). Over the course of it's development, NetCF has had a number of execution engine options exposed on the command line, all using the "//" semantic. We chose this format because we felt is was highly unlikely that an actual application would have an argument that started with "//". This was chosen back when all we had was a command line interface in NetCF (that was a very long time ago) and has stuck. While we could have switched over to using the device's registry, the lack of a registry editor on our target platforms made for a difficult user experience, so the command line options remained.

Back to the topic at hand... As you saw in part I, the first thing you did after starting cordbg was to issue the "mode embeddedclr 1" command. What this did was to instruct the debugger to use the NetCF provided implementation of the managed debugger interfaces. Without this step (which you only need to do once, unless you toggle it back to 0), cordbg uses the .NET Framework's implementation of these interfaces and cannot debug your process.

But this is not the only cool thing that the mode command can do for you. If you type "? mode" at the (cordbg) prompt, you will see something resembling the following.

AppDomainLoads=1 AppDomain and Assembly load events are not displayed
ClassLoads=1 Class load events are not displayed
DumpMemoryInBytes=0 Memory is dumped in DWORDS
EnhancedDiag=0 Suppress display of diagnostic information
HexDisplay=1 Numbers are displayed in hexadecimal
ILNatPrint=0 Offsets will be IL xor native-relative
ISAll=0 All interceptors are skipped
ISClinit=0 Class initializers are skipped
ISExceptF=0 Exception filters are skipped
ISInt=0 User interceptors are skipped
ISPolicy=0 Context policies are skipped
ISSec=0 Security interceptors are skipped
JitOptimizations=0 JIT's will produce debuggable (non-optimized) code
LoggingMessages=0 Managed log messages are suppressed
ModuleLoads=1 Module load events are displayed
SeparateConsole=0 Debuggees share cordbg's console
ShowArgs=1 Arguments will be shown in stack trace
ShowModules=1 Module names will be included in stack trace
ShowStaticsOnPrint=0 Static fields are not included when displaying objects
ShowSuperClassOnPrint=0 Super class names are not included when displaying objects
UnmanagedTrace=0 Unmanaged debug events are not displayed
USAll=0 Unmapped stop locations are skipped
USEpi=0 Epilogs are skipped, returning to calling method
USPro=0 Prologs are skipped
USUnmanaged=0 Unmanaged code is skipped
Win32Debugger=0 CorDbg is not the Win32 debugger for all processes
EmbeddedCLR=1 Embedded CLR applications are debugged

These are the settings that I use when I am debugging (this display is from my home machine, in fact). We'll talk a little more about most of these a little later. But first...

As with most things in NetCF, we have factored the functionality of the debugger interfaces. Of the cordbg modes shown in the listing above, the following are not supported when debugging device projects.

ISAll Step through all interceptors
ISInt Step through user interceptors
ISPolicy Step through context policies
ISSec Step through security interceptors
JitOptimizations JIT compilation generates debuggable code
LoggingMessages Display managed code log messages
UnmanagedTrace Display unmanaged debug events
USAll Step through all unmapped stop locations
USEpi Step through method epilogs
USPro Step through method prologs
USUnmanaged Step through unmanaged code
Win32Debugger Specify Win32 debugger (UNSUPPORTED: use at your own risk)

And now... "My Favorite Modes".

AppDomainLoads
If your application starts a new appdomain or loads assemblies, this mode will help you to keep track of exactly when these events are occuring. They also help you to see if there are any loads you were not expecting -- perhaps a 3rd party assembly you are using is loading something large.

ClassLoads
This tells the debugger to display a message whenever a new class is loaded. Again, helpful to see exactly what is going on in your code.

HexDisplay
Maybe this is due to my early days working on Win95 (we used wdeb386 then). I got accustomed to seeing values as hex and found it was nice when looking at flags values.

ModuleLoads
Similar to AppDomainLoads and ClassLoads, this lets you see what your application is loading into memory.

ShowArgs
ShowModules

These give you much more complete data when looking at stack traces. ShowArgs provides you with argument names. ShowModules adds module names. ShowModules is particularly helpful when figuring out how to set breakpoints (cordbg can be a bit touchy wrt breakpoint formatting -- this will be covered in a future part of this series).

EmbeddedCLR
Of course, without this, debugging NetCF applications would not be possible.

How about a few examples? I have been working on a simple triva game off|on for quite awhile. The examples below are from a basic debugging session using this applicaiton as the debuggee.

For space reasons, I will not show an example of AppDomainLoads, ClassLoads or HexDisplay. AppDomainLoads and ClassLoads are similar to what you get with ModuleLoads. HexDisplay is pretty obvious.

With ModuleLoads enabled, the results of the connect command look something like this:
Process 1934213950/0x7349c33e created.
Warning: couldn't load symbols for <path>\mscorlib.dll
Module 0x002683f4, <path>\mscorlib.dll
-- Loaded
in assembly 0x0026830c, mscorlib.dll
in appdomain #463396, TrivialThings.exe
Module 0x00a35e84, <path>\TrivialThings.exe -- Loaded
in assembly 0x00a34634, \Program Files\TrivialThings\TrivialThings.exe
in appdomain #463396, TrivialThings.exe
Warning: couldn't load symbols for <path>\System.Drawing.dll
Module 0x00a34dd4, <path>\System.Drawing.dll -- Loaded
in assembly 0x00a38fd4, System.Drawing.dll
in appdomain #463396, TrivialThings.exe
Warning: couldn't load symbols for <path>\System.dll
Module 0x00a49e2c, <path>\System.dll --
Loaded
in assembly 0x0026cb54, System.dll
in appdomain #463396, TrivialThings.exe
Warning: couldn't load symbols for <path>\System.Windows.Forms.dll
Module 0x00a3554c, <path>\System.Windows.Forms.dll -- Loaded
in assembly 0x0026cb94, System.Windows.Forms.dll
in appdomain #463396, TrivialThings.exe
[thread 0x71784] Thread created.

As you can see from the example, ModuleLoads not only tells you what was loaded but which assembly was loaded into what appdomain.

When you set ShowArgs and ShowModules, your stack traces (where command) become very nice.

Thread 0x71784 Current State:Normal
0)* TrivialThings!TrivialThings.GameEngine::.ctor +0000[IL] in <path>\GameEngine.cs:33
board=(0x00097360) <TrivialThings.RectangularGameBoard>
die=(0x002e1b80) <TrivialThings.Die>
serverAddress=<null>
1) TrivialThings!TrivialThings.MainForm::.ctor +00a9[IL] in <path>\mainform.cs:71
2) TrivialThings!TrivialThings.MainForm::Main +000a[IL] in <path>\mainform.cs:141

I, for one, find it very useful to see the names of the arguments (sometimes I have a hard time keeping track of which argument does what) and besides, .NET assemblies are self describing, so why not take advantage of it.

Also, as mentioned above, sometimes it's hard to figure out just what format cordbg wants for breakpoints. Here's how ShowModules helps you here (you can just copy from the stack, and modify as needed).

Well, that's all for this part... Stay tuned for future installments, including (in no particular order):
* Breakpoints and stepping
* Working with source
* Stack walking
* Threads and exceptions
* IL debugging
* Cordbg and NetCF v2

Until next time.
-- DK

Disclaimer:
This posting is provided "AS IS" with no warranties, and confers no rights.