Tips for using Breakpoints

Posted by: Sue Loh

I've been working with one of our customers the last couple of days, and found that there were a few handy tips for using the Platform Builder debugger with Windows CE that I knew and they didn't.  So I am sharing them here.

Uninstantiated Breakpoints

If you've ever tried to set a breakpoint, but gotten the pink-color uninstantiated circle (indicating that the breakpoint is not yet set) instead of the maroon-color circle (indicating that the breakpoint is set), the problem might be something like this:

  • Wrong source file:   The code you set a breakpoint in isn't actually running.
  • Bad source path mapping:   The binaries and PDBs you use contain strings indicating where the source code came from.  But those strings are inserted when they are compiled.  If you are trying to set a breakpoint inside of code that Microsoft compiled, then the strings will indicate a location that does not exist on your machine.  The "source path mapping" is a mapping from the locations that are compiled-in to the code (eg. c:\SomeMicrosoftBuildMachine\foo\...) to the corresponding locations on your machine (eg. D:\WINCE500\bar\...)  If you have ever had Platform Builder ask you for a source location when you tried to set a breakpoint, it's probably a source path mapping question.  If your source path mapping is wrong for the file you set the breakpoint in, or if PB gets confused about it somehow, then it may not understand properly that the code you're looking at is the code that's running on the device.
  • Bad symbols:   The PDB files in your release directory don't match the EXEs or DLLs that are running on the device.  Usually this results in the breakpoint being set in some random other location in the code.
    • Common issue #1: you built the .exe or .dll more
      recently than the .pdb.  This can happen if you rebuild while debugging;
      Platform Builder will hold the .pdb open and the compiler won't be able to
      overwrite the .pdb.  Fix by rebuilding while you are not debugging the
      module.
    • Common issue #2: you built the .pdb more recently than the .exe or
      .dll.  Usually this happens if it's a file in ROM and you forgot to re-run
      makeimg.  Or if you rebuilt the .exe/.dll but the old version is still
      running on the device.
    • At worst, all of these problems can be fixed by
      disconnecting from the device, rebuilding, re-running makeimg, and
      reconnecting.  But as you get familiar with how the tools work you should be
      able to figure out quicker ways of working.
  • Optimizations: The code is optimized (= release build) and the source line with the pink breakpoint doesn't
    map to code the debugger understands.  In this case you might have to switch to a disassembly window, examine the disassembly and place your breakpoint using that window.  Or build a debug version of your code, so that the
    optimizations don't confuse the debugger.
  • Module not loaded:   The breakpoint cannot be instantiated until the code is actually loaded.  So if the DLL or EXE is not loaded, the breakpoint will stay pink.  When the DLL or EXE is loaded it should become maroon.
  • Breakpoint is pending:   The debugger only instantiates breakpoints while the device is stopped for debugging.  Not while the device is running.  Our debugger doesn't stop the device from running in order to add the breakpoint when you set it.  Instead it waits until you break into the debugger before setting the breakpoint.  PB also breaks into the debugger automatically for a very brief time whenever a DLL or EXE is loaded or unloaded.  So you may find that the breakpoint becomes instantiated when that happens.
  • Debugger is not connected:   Try running "s loaddbg" in the target control window.  That will load debugger DLLs and connect the PB debugger.  To run that, you will need: loaddbg.exe and kd.dll, and if present in your release directory you'll need hd.dll, osaxst0.dll and osaxst1.dll.  You can also get the debugger to start automatically during boot if you make sure that the IMGNODEBUGGER flag is not set before you make a new image.
  • Flash ROM: You can't debug ROM.  By that I mean, if your image is running directly from flash ROM, the debugger can't edit the ROM to insert your breakpoint.  A breakpoint is just a replacement of the existing code with a call to stop in the debugger, so if the debugger can't edit the code it can't insert the breakpoint.  You can debug if your image is running from RAM, or if your .dll or .exe is running from RAM (which is generally true if it's not in the MODULES section of ROM).
  • If the modules you are trying to debug are built with VS2005, there is a QFE to Platform Builder 5.0 to support VS2005 symbols.  The update is in the 2005 update package at https://www.microsoft.com/downloads/details.aspx?FamilyID=6c69461e-89fa-40b0-8953-b4cc1adbc8d8&DisplayLang=en.  You can also just copy msobj71.dll into “Program Files\Windows CE Platform Builder\5.00\CEPB\BIN”.

Breakpoints without source

You can set a breakpoint even if you don't have the source code to set it on.  If you know the exact function name and module name you can use the breakpoint window to break on function entry.  In the View menu, go to Debug Windows / Breakpoint List.  You can also get there by clicking the "hand" icon on the debug toolbar or by hitting ALT + 9.  In the breakpoint window, create a new breakpoint using this syntax:

    {,,modulename} function_name

That's an open curly-brace, two commas, module name, close curly-brace, function name.  You can use this for EXEs or DLLs.  For example:

    {,,nk.exe} SC_QueryPerformanceCounter
{,,pm.dll} PlatformSetSystemPowerState

This syntax is not actually specific to breakpoints.  It's just a way to tell the debugger that a symbol is specific to a module.  For example if you want to look at a global variable in the Watch window, you can use this syntax there too.  For example you could type {,,nk.exe}CurMSec in the Watch window to see that global variable inside the kernel.

By setting a breakpoint on a symbol instead of a source file line, you use only the PDBs to set the breakpoint.  There is no dependency on having the right source file or source path mapping.

Don't just guess random function names when using this method though.  OS APIs often have prefixes and postfixes that differ from their official names (eg. the implementation of CreateFile is FS_CreateFileW) and may be completely different (eg. for some reason the implementation of DeleteAndRenameFile is called FS_PrestoChangoFileName...  I like that one :-) ).  You can search the .map files in your release directory to get clues about what the API names are.

The other useful thing about the breakpoint window is that you can see if your breakpoint changes are pending.  Like I mention above, if you set (or unset) a breakpoint while the target device is still running, the change will not occur until you stop in the debugger.

Other tips

If you have a bunch of breakpoints set you may notice that your device is running slower.  Every time a DLL or EXE is loaded the debugger does work to figure out if it needs to instantiate a breakpoint inside the module.  Unfortunately from my experience, deactivating the breakpoints does not help.  I don't know why.  You would have to completely delete the breakpoints in order to get back to normal speed.  My advice is not to do that though.  Delete breakpoints when you're totally done with them, but don't let debugger speed put you into the habit of setting & deleting breakpoints left and right.  Slower speed is better than having to redo some debugging because you forgot to set something.

Happy debugging...

UPDATE Dec 9, 05: I added the "debugger is not connected" case above.
UPDATE Dec 4, 06: I added some more details to the "Bad Symbols" case and added the "Optimizations" and "Flash ROM" cases.
UPDATE Jan 5, 07: Added bullet about the QFE to support VS2005 symbols.
UPDATE Feb 14, 07: See also https://blogs.msdn.com/hopperx/archive/2007/02/14/map-file-breakpoints.aspx