Palm Debugger

User's Guide

Last Updated: 18-Mar-1999

 

Contents

  1. About
  2. User Interface Overview
  3. The Console Window
  4. The Debugger Window
  5. The Source Window
  6. Debugging Hints

 

 

About PalmDebugger

PalmDebugger is a Windows and Macintosh desktop application for debugging PalmOS executables. It provides assembly and source level debugging as well as support for managing databases on the PalmOS device. It can communicate with a PalmOS device over serial or USB (soon), or with the PalmOSEmulator desktop application (which acts as a virtual PalmOS device) over TCP/IP.

All PalmOS devices have a debugger stub in their ROM that PalmDebugger communicates with. This debugger stub, although fairly small, provides enough basic support for all of PalmDebugger's functions including displaying and modifying memory, setting breakpoints, single-stepping, dumping memory heaps, modifying databases, etc.

Besides basic debugging support of PalmOS executables, PalmDebugger also provides a "console" window for system administration functions. The console window behaves like a unix shell or DOS command line interface to the device. There are console window commands for doing things like getting a directory of databases on the device, creating and deleting databases,  importing and exporting databases to and from the desktop computer, and many other system level functions.

PalmDebugger is a developer's tool and is very powerful but unfortunately is not a very "polished" application. In it's design, user-friendliness has taken a back seat to power and functionality. But, time spent learning how to use it will be time well spent and pay for itself many times over. With the help of this document, you should be able to learn at least the basics of how to use it within an hour or so.

 

User Interface Overview

The Windows

When launched, PalmDebugger displays 4 windows: a "Debugger" window, a "Console" window, a "CPU Registers" window, and a "Source" window (note: the source window and source level debugging support is only present in the Windows version). The debugger and CPU registers windows are used for assembly level debugging, the source window is used for source level debugging, and the console window is used as a command-line based shell for managing databases on the device.

Most of the powerful functions of the debugger are accessed by typing commands into either the debugger or console window. There are however some menus for performing basic source level debugging functions like setting breakpoints, single-stepping, etc. The source window is essentially an "output only" window that is used to display the source code and local variables for the executable currently being debugged. The CPU registers window is also an "output only" window that displays the values of each of the CPU registers while debugging.

Commands are entered into the debugger and console windows by typing the command and hitting the Enter key (note: on the Macintosh, the Enter key on the numeric keyboard must be used or Cmd-Return can be used instead). Both the debugger and console windows have a 'help' command that will display a list of possible commands. Help on any specific command can be displayed by entering 'help cmdName'.

The debugger and console windows behave like edit windows and support cut, copy, paste, and undo (undo is available in the Windows version only). Whenever you hit the Enter key, the window executes whatever text is currently selected, or the entire current line if the selection is empty. If more than 1 line is selected, then every selected line will be executed. If you simply want to create a new line without executing the current line, hit the  Return key if on a Macintosh, or Shift-Enter if on Windows.

The Windows version also supports the following special key sequences: hitting Shift-Backspace will delete all text from the current selection point to the end and the undo command (Ctrl-Z) immediately after executing a command will delete the output of the command from the window.

The Menus

Most of the menu items in the File menu are not implemented yet. In the future through, this menu will allow saving and printing of window contents.

The Edit menu provides the usual cut, copy, paste functions as well as undo and redo and a font command for changing the font used in all of the windows.

The Connection menu is used to setup how to communicate with the actual or virtual (in the case of PalmOSEmulator) device. The choices are through one of the serial ports (COM1 through COM4 on Windows, Modem or Printer on Macintosh), through USB (not implemented yet), or Emulator which means to communicate with the PalmOSEmulator application running on the desktop. When one of the serial ports are selected, the menu items for changing the baud rate are enabled.

The Souce menu provides basic source level debugging commands like breakpoints, single stepping, continuing, etc. as well as commands for setting up the symbol files for source level debugging.

The Window menu (Windows version only) provides the standard Windows commands for selecting various windows and re-arranging them.

 

The Console Window

The console window behaves like a unix shell or DOS command line interface to the device. Through the console window, you can get a directory of databases on the device, create and delete databases, transfer database to and from the desktop, display system information, etc.

In order to use the console window to communicate with the device, the device must be running a "console stub". The console stub runs as a background thread on the device and waits for commands over the serial or USB port, processes the commands, and then sends responses back. Because the console stub runs as a background thread, it does not affect the normal operation of the device and applications can be used normally while the thread is running. However, because it requires memory and system resources, it is not normally started up unless specifically activated by the user. Once started, the console stub continues to run, and prevents other applications (like HotSync, for example) from using the serial port until the device is soft-resetted.

To activate the console stub on a device, enter the Graffiti sequence Shortcut-.-2, that is: a shortcut stroke (a script lower case letter 'l'), followed by a period (two single taps), followed by the number 2 (entered in the right side of the Graffiti area). When the console stub first starts up, it sends out a "Ready..." message and if you have PalmDebugger running and connected with a cable to the device, this message will show up in the console window of PalmDebugger. If the console stub was already started or if PalmDebugger was not connected to the device you will not see the message.  Note that if you are debugging using the PalmOSEmulator instead of a real device, you do not have to manually start up the console stub because it will be started for you automatically. The console stub on the device always starts up at 57,600 baud, so check the baud rate setting of PalmDebugger if you are using serial to make sure it is set at 57,600.

Once the console stub is running on the device, you should be able to successfully enter commands in the console window of PalmDebugger and see results back from the device. A simple, quick command to try out is "hl 0" which will display a list of memory heaps on card #0 of the device. Here's an example output from the hl command:

hl 0
   index   heapID  heapPtr     size     free  maxFree    flags
------------------------------------------------------------
       0     0000 00001480 00016B80 00010A90 0001046C     8000
       1     0001 1001810E 001E7EF2 001E53C0 001E5342     8000
       2     0002 10C08212 00118DEE 0000A01C 0000A014     8001

If the console stub is not running on the device, or if the serial or USB connection is not correct, you will see the following error message after a few seconds:

### Error $00000404 occurred

If you see this message, double-check that you have the correct communication options set in the Connection menu of PalmDebugger. If you are using serial, make sure PalmDebugger is set to 57,600 baud, check that you have the correct handshaking mode, check your cable connection, and make sure the device is powered on and that you have started the console stub as described above. 

 

Commonly used Console Commands

import

By far, the most commonly used console command is the import command. This command will copy a PalmOS database from the desktop to the device. It will be used whenever you have built a new version of an application for example and want to load the application onto the device for testing. It basically performs the same function as the Installer tool provided with the PalmPilot HotSync application but is much more convenvient to use while debugging than the Installer tool is.

The basic form of the import command is:

import <cardNo> <filename>

Where <filename> is the name of a file on the desktop. By default, PalmDebugger looks in the "Device" sub-directory within the directory containing the PalmDebugger application itself for the named file. The filename can also be specified using a relative or absolute path if it's not in the "Device" directory. The <cardNo> is nearly always 0, which means import the database into the built-in RAM on the device.

Here's an example of the import command and its output:

import 0 Tex2HexApp.prc

Creating Database on card 0
name: Text to Hex
type appl, creator TxHx

Importing resource 'code'=0....
Importing resource 'data'=0....
Importing resource 'pref'=0....
Importing resource 'rloc'=0....
Importing resource 'code'=1....
Importing resource 'tFRM'=1000....
Importing resource 'tver'=1....
Importing resource 'tAIB'=1000....
Importing resource 'Tbmp'=1000....
Importing resource 'Tbmp'=1001....
Importing resource 'MBAR'=1000....
Importing resource 'Talt'=1000....
Importing resource 'Talt'=1001....
Success!!

Note that the actual name of the database once it's stored on the device is not the same as the name of the file on the desktop. The PalmOS database name is stored within the file itself and in the example above was "Text to Hex".

If the file you're trying to import already exists on the device, it will be deleted and replaced by the new file unless the current database is open on the device. If it is open when you try to import a new copy, you will get a dmErrAlreadyExists (0x0219) Data Manager error code back from the import command. For example:

import 0 Tex2HexApp.prc

Creating Database on card 0
name: Text to Hex
type appl, creator TxHx

### Error $00000219 occurred

Unfortunately, most of the error messages you will see in PalmDebugger are currently fairly cryptic hex codes like the one above. To determine the meaning of the error message, you will have to look up the name of the error code from the PalmOS header files. All PalmOS error codes are 16-bit values with the upper byte representing the manager and the lower byte representing a manager specific error code. In the example above, the manager code was 0x02 and the specific error code was 0x19. The <SystemMgr.h> header file contains the manager codes (0x02 is dmErrorClass) and the manager specific error code can be found in that manager's header file (0x19, decimal 25, is dmErrAlreadyExists in <DataMgr.h>).

 

export

The export command does the opposite of the import command above - it copies a database from the device to the desktop.

The basic form of the export command is:

export <cardNo> <filename>

Where <filename> is the name of the PalmOS database. To get a list of databases on the device to see what their names are, use the dir command described below. The database will be copied to the desktop in the the standard PRC/PDB file format (these two file formats are actually identical - PRC is normally used to indicate resource databases and PDB is normally used to represent record databases) and will be given a name of <filename> without any added extension. All exported files are placed into the "Device" sub-directory of PalmDebugger.

Here's an example of the export command and it's output. Note that you must use quotes if the name has spaces in it:

export 0 "Text to Hex"

Exporting resource 'code'=0....
Exporting resource 'data'=0....
Exporting resource 'pref'=0....
Exporting resource 'rloc'=0....
Exporting resource 'code'=1....
Exporting resource 'tFRM'=1000....
Exporting resource 'tver'=1....
Exporting resource 'tAIB'=1000....
Exporting resource 'Tbmp'=1000....
Exporting resource 'Tbmp'=1001....
Exporting resource 'MBAR'=1000....
Exporting resource 'Talt'=1000....
Exporting resource 'Talt'=1001....
Success!!

The above command will create a file called "Text to Hex" within the "Device" sub-directory of PalmDebugger.

 

dir

The dir command will display a list of databases on the device. The basic form of this command is:

dir <cardNo>|<searchOptions> [<displayOptions>...]

The <displayOptions> are usually left blank, or the -a option is specified which means to display all information. For a complete list of options, type 'help dir' in the console window.

Here's an example of the command and it's (abbreviated) output:

dir 0
name                    ID      total       data  
--------------------------------------------------
*System            00D20A44  392.691 Kb  390.361 Kb  
*AMX               00D209C4   20.275 Kb   20.123 Kb  
*UIAppShell        00D20944    1.327 Kb    1.175 Kb  
*PADHTAL Library   00D208E2    7.772 Kb    7.674 Kb  
*IrDA Library      00D20876   39.518 Kb   39.402 Kb  
 ...
 MailDB            0001817F    1.033 Kb    0.929 Kb  
 NetworkDB         0001818B    0.986 Kb    0.722 Kb  
 System MIDI Sounds  000181B3    1.066 Kb    0.842 Kb  
 DatebookDB        000181FB    0.084 Kb    0.000 Kb  
----------------------------------------------------------------
Total: 41

 

del

The del command will delete a database from the device. The basic form of this command is:

del <cardNo> <filename>

Where <filename> is the name of the PalmOS database. To get a list of databases on the device to see what their names are, use the dir command described above. Keep in mind that you won't be able to delete a database if it is currently open and if you get an error from this command, like this: "##ERROR Deleting database", the most likely reason is that the database is currently open. If this is an application that is currently running, switch to a different application and try again.

 

Less Commonly used Console Commands

Entering 'help' in the console window will display a relatively large list of available console commands. The commands mentioned above will probably be used at least 90% of the time. The remaining console commands are used much less often and most users will probably never use most of them.

Rather than describe every one of the console commands in detail here, we will just provide a brief overview of the ones most likely to be used. Later on in this document, where we describe how to track down and solve typical application problems, we will describe specific features of the console commands that are useful for tracking down specific problems.

Of the remaining console commands, the ones dealing with memory heaps are probably used the most often. These include the following:

hl <cardNo> Display list of memory heaps
hd <hex heapID> Dump a specific heap

The hl command will display a list of memory heaps. For example:

hl 0
   index   heapID  heapPtr     size     free  maxFree    flags
------------------------------------------------------------
       0     0000 00001480 00016B80 00010A90 0001046C     8000
       1     0001 1001810E 001E7EF2 001E53C0 001E5342     8000
       2     0002 10C08212 00118DEE 0000A01C 0000A014     8001

The hd command will do a dump of a specific heap. The <heapID> argument can be determined by looking at the "heapID" column of the 'hl' command. Heap number 0 is always the dynamic heap and higher number heaps represent storage RAM or ROM. For example:

hd 0
Displaying Heap ID: 0000, mapped to 00001480
                             req    act                             resType/  #resID/
 start    handle   localID   size   size  lck own flags type  index attr ctg  uniqueID name
--------------------------------------------------------------------------------------------
-00001534 00001490 F0001491 00001E 000026  #0  #0    fM Alarm Table
-0000155A 00001494 F0001495 000456 00045E  #0  #0    fM Graffiti Private
-000019B8 00001498 F0001499 000012 00001A  #0  #0    fM DataMgr Protect List (DmProtectEntryPtr*)
...
-00017DC6 -------- F0017DC6 0001F4 0001FC  #0 #15    fM Handle Table: 'Graffiti ShortCuts'
-00017FC2 -------- F0017FC2 000024 00002C  #0 #15    fM DmOpenInfoPtr: 'Graffiti ShortCuts'
-00017FEE -------- F0017FEE 00000E 000016  #0 #15    fM DmOpenRef: 'Graffiti ShortCuts'
--------------------------------------------------------------------------------------------
Heap Summary: 
  flags:              8000
  size:               016B80
  numHandles:         #40  
  Free Chunks:        #14     (010A90 bytes)
  Movable Chunks:     #52     (006040 bytes)
  Non-Movable Chunks: #0      (000000 bytes)

 

The kinfo command will display a list of all system kernel information including tasks, semaphores, event groups, etc.:

kinfo <options>
  -all           : get all info
  -task <id>|all : get task info
  -sem <id>|all  : get semaphore info
  -tmr <id>|all  : get timer info
Display system kernel info

For example:

kinfo -all

Task Information:
  taskID     tag  priority  stackPtr   status
  ---------------------------------------------------------------------
  0001772C    AMX  #   0    00017598   Idle: Waiting for Trigger
  00017900   psys  #  30    000130B4   Running

Semaphore Information:
  semID      tag   type      initValue  curValue        nesting    ownerID 
  ---------------------------------------------------------------------------
  00017830   MemM  resource   #-1        #1  (free)      #0        00000000
  00017864   SlkM  counting   #1         #1  (avail.)    #0        00000000
  000178CC   SndM  counting   #1         #1  (avail.)    #0        00000000
  00017968   Sync  resource   #-1        #1  (free)      #0        00000000
  00017A38   SerM  counting   #0         #0  (unavail.)  #0        00000000

Timer Information:
  tmrID      tag   ticksLeft   period   procPtr
  ---------------------------------------------
  000177FC   psys    # 493    #   0    10C6C618


Finally, the mdebug command will put the device into various modes for helping to track down memory corruption problems. Note that turning these options on will slow down the device, often considerably:

mdebug [options..] 
  Shortcuts:
         -full     : Full checking (slowest)
         -partial  : Partial checking (faster)
         -off      : No checking (fastest)
  Fine Tuning:
    Which heaps are checked/scrambled:
         -a        : check/scramble ALL heaps each time
         -a-       : check/scramble affected heap only
    Heap Checking:
         -c        : check heap(s) on some Mem calls
         -ca       : check heap(s) on every Mem call
         -c-       : turn off heap checking
    Heap Scrambling:
         -s        : scramble heap(s) on some Mem calls
         -sa       : scramble heap(s) every Mem call
         -s-       : turn off heap scramble
    Free Chunk Checking:
         -f        : check free chunk contents
         -f-       : don't check free chunk contents
    Min Dynamic Heap free space recording:
    (Recorded in the global GMemMinDynHeapFree)
         -min      : record minimum free space in Dynamic heap
         -min-     : don't record minimum free space
Turn on/off various memory debugging options

 

 

The Debugger Window

The debugger window is used on conjunction with the CPU registers window for assembly level debugging. Commands are entered into this window for displaying and modifying memory, single stepping, setting and clearing breakpoints, dumping memory heaps, displaying database directories, automatically breaking on one or more PalmOS system calls, breaking when a memory location gets modified, etc. Besides applications, it can also debug system code, extensions, shared libraries, background threads and interrupt handlers.

The debugger window also supports custom defined command aliases, script files, and data structure templates, which are described in more detail in the Utility Commands section below. You can also automatically load these custom definitions every time you launch PalmDebugger by adding them to the "UserStartup-PalmDebugger" text file which is executed by PalmDebugger every time it starts up.

Attaching to the Device

Most Debugger window commands, except for help and some utility commands, will only execute when the device is connected to the desktop and halted in it's debugger stub. When the device is halted in the debugger stub, it will not respond to pen taps on the screen or key presses and you will see a tiny flashing square about 4 pixels across in the upper left corner of the display. There are two main ways to put the device into this mode (besides encountering a bug of course!):

  1. Enter the Shortcut-.-1 sequence using Graffiti.
  2. Compile a DbgBreak() call into your application and run the application until you encounter the DbgBreak() call.

To use method #1 above, enter the Graffiti sequence Shortcut-.-1, that is: a shortcut stroke (a script lower case letter 'l'), followed by a period (two single taps), followed by the number 1 (entered in the right side of the Graffiti area).

To use method #2 above, you must have previously entered the debugger at least once already using method 1. If the debugger has not been entered at least once already using method #1, then instead of entering the debugger, the DbgBreak() call will display a fatal error dialog. Alternatively, you can set the low memory global GDbgWasEntered to non-zero before the DbgBreak() call. This will effectively make the device "think" it has entered the debugger at least once already.  For example:

GDbgWasEntered = true;
DbgBreak();

If the PalmDebugger application is running and connected with the appropriate cable to the device when it halts into the debugger, then you will see a message similar to the following appear in the Debugger window:

EXCEPTION ID = $A0
'PrvHandleEvent' 
+$062C 10C0F2AA *MOVEQ.L #$01,D0 		| 7001 

Note that when the debugger stub on the device first starts up, it always starts talking at 57,600 baud so make sure PalmDebugger is set to talk at 57,600 baud (in the Connection menu) if you are using serial. Also check that you have a serial cable with handshaking lines, and if you don't, turn off the Handshaking option in the Connection menu. If the PalmDebugger was not running or connected correctly to the device when it halted, you can use the att command to attach PalmDebugger to the device. For example:

att
EXCEPTION ID = $A0
+$062C 10C0F2AA *MOVEQ.L #$01,D0 		| 7001 

The att command sends a request packet to the device and waits for a response from the device saying where it is currently halted. If no response is received within a couple seconds, the att command will timeout and and display a timeout error message. This means either that the device is not halted in the debugger or that there is a problem with the connection between desktop and the device.

If you try and execute a debugger command when the PalmDebugger thinks it is not attached to the device, it will display an error message "Error: not attached to remote". If this happens, insure that the device is indeed halted in the debugger then issue the att command to re-attach PalmDebugger to the device.

 

Commonly Used Debugger Commands

This section will illustrate the most commonly used debugger commands. It does not cover the entire set of available commands or options however. To see the entire set of available commands enter the 'help' command in the debugger window and to get help on a particular command, enter help cmdName.

Entering Commands

As mentioned previously, debugger commands are typed into the debugger window and executed by hitting the Enter key. For commands that take arguments, such as an address to operate on, the arguments can be entered in either decimal or hex or even as expressions. In addition, expressions can be written in terms of processor registers. By default, all numbers are assumed to be in hex unless preceded by a '#' sign (decimal) or '%' sign (binary).

A number of commands allow you to intelligiently repeat the command simply by pressing Enter repeatedly. The 'dm' command is one of these - every time you hit Enter it will display the next 16 bytes of data. The 's' and 't' commands for single stepping will also repeat when you hit Enter.

One shortcut character is introduced below, which is the '.' character. This is a shorthand representation for the address value used for the last command. For a full description of expression evaluation and all the shorthand characters that are available, see the Debugger Expressions section below.

The following is an example that shows how to display and disassemble memory using various expressions. In all the examples in this section, commands entered by the user are shown in bold and comments are show in italics:

dm 0      	<=Display memory at address 0
00000000: FF FF FF FF 1A 34 3E 40 10 C0 92 D4 10 C0 92 F2 ".....4>@........"

dm 100		<=Display memory at address 0x100
00000100: 01 01 00 00 02 B0 00 01 78 30 00 00 00 01 47 EE "........x0....G."

dm #100		<=Display memory at address 100 decimal
00000064: 10 C6 BE 32 10 C6 BE 60 10 C6 BE 8E 10 C6 BE BC "...2...`........"

dm 100+20	<=Shows how to enter an expression for an address
00000120: 6F BC 00 00 07 22 00 00 00 06 00 01 7D 72 00 FD "o...."......}r.."

dm .+10		<=Illustrates using the '.' character to represent
		 the last address entered. 
00000130: 00 00 00 00 00 00 00 B6 3E C0 69 45 A4 0C 03 4A "........>.iE...J"

dm pc		<=Use the current program counter value 
10C0EEFE: 70 01 60 00 01 7E 4E 4F A0 BE 70 01 60 00 01 74 "p.`..~NO..p.`..t"

dm pc+20	<=An expression using the program counter
10C0EF1E: FF F4 4E 4F A0 AC 38 00 4A 44 50 4F 66 2A 48 6E "..NO..8.JDPOf*Hn"

il pc		<=Disassemble code at current program counter
'SysHandleEvent 10C0E9EC'
+$0512 10C0EEFE *MOVEQ.L #$01,D0 			| 7001 
+$0514 10C0EF00 BRA.W SysHandleEvent+$0694 ; 10C0F080 	| 6000 017E 
+$0518 10C0EF04 _SysLaunchConsole ; $10C0E30C 		| 4E4F A0BE 
+$051C 10C0EF08 MOVEQ.L #$01,D0 			| 7001 
+$051E 10C0EF0A BRA.W SysHandleEvent+$0694 ; 10C0F080 	| 6000 0174 
+$0522 10C0EF0E MOVEQ.L #$00,D0 			| 7000 
+$0524 10C0EF10 BRA.W SysHandleEvent+$0694 ; 10C0F080 	| 6000 016E 
+$0528 10C0EF14 CLR.L -$0010(A6) 			| 42AE FFF0 
+$052C 10C0EF18 PEA -$0006(A6) 				| 486E FFFA 
+$0530 10C0EF1C PEA -$000C(A6) 				| 486E FFF4 

il pc-10	<=Display code at program counter - 0x10
'SysHandleEvent 10C0E9EC'
+$0502 10C0EEEE ORI.B #$01,(A5)+ ; '.' 			| 001D 7001 
+$0506 10C0EEF2 BRA.W SysHandleEvent+$0694 ; 10C0F080 	| 6000 018C 
+$050A 10C0EEF6 MOVE.B #$01,$00000101 ; '.' 		| 11FC 0001 0101 
+$0510 10C0EEFC _DbgBreak 				| 4E48 
+$0512 10C0EEFE *MOVEQ.L #$01,D0 			| 7001 
+$0514 10C0EF00 BRA.W SysHandleEvent+$0694 ; 10C0F080 	| 6000 017E 
+$0518 10C0EF04 _SysLaunchConsole ; $10C0E30C 		| 4E4F A0BE 
+$051C 10C0EF08 MOVEQ.L #$01,D0 			| 7001 
+$051E 10C0EF0A BRA.W SysHandleEvent+$0694 ; 10C0F080 	| 6000 0174 
+$0522 10C0EF0E MOVEQ.L #$00,D0 			| 7000 

You can even enter an expression by itself on the command line as a form of hex calculator:

20*4+3
$83 #131 #-125 '.'

This has been just a very brief introduction to expressions in order to lead into the commonly used commands. For a full discussion of expressions and the available operators, see the Debugger Expressions section below.

 

Displaying Registers, Memory and Instructions

Usually the first thing one does after breaking into the debugger is to disassemble code around the current program counter, display variables, etc. The following commands perform these functions as well as allow you to change memory.

One fairly advanced feature of the debugger is the ability to dump memory according to a specific structure definition. This is the <template> option to the dm command. The section below on Utility Commands describes how to define templates for various structures.

dm <address> [<count>]  [<template>] Display memory at <address> for <count> bytes. If count is omitted then assume a count of 16. If <template> is included, then display memory according to that template.
db <address> Display the byte (8 bits) at given address
dw <address> Display the word (16 bits) at given address
dl <address> Display the long word (32 bits) at given address
sb <address> <value...> Set Byte(s) at given address to <value>. Entering multiple <values> will set multiple bytes starting at <address>
sw <address> <value...> Set Word(s) (16 bits) at given address to <value>. Entering multiple <values> will set multiple words starting at <address>
sl <address> <value...> Set Long(s) (32 bits) at given address to <value>. Entering multiple <values> will set multiple longs starting at <address>
il <address> [<lineCount>] Instruction List. Disassemble the instructions at the given address.
reg Display current values of all the processor's registers. Note that the current values of all the registers also appear in the CPU Registers window.
sc Stack Crawl. Display a list of routines on the stack using information stored in the frame pointer register (A6).
sc7 Stack Crawl using the stack pointer (A7) instead of the frame pointer (A6). This will display information about routines on the stack that don't set up frame pointers but will also sometimes show bogus routines as well.

The following example shows how some of these commands can be used:

reg		<= Display all processor registers
D0 = 00000102 A0 = 10C0EEF6 USP = 420024FD
D1 = 00000013 A1 = 10C0EF0E SSP = 00013412
D2 = 0000001A A2 = 000134F0 
D3 = 00000000 A3 = 00015404 
D4 = 0001008E A4 = 10CD884E 
D5 = 00000000 A5 = 00014A06 
D6 = 00D1F334 A6 = 000134DA PC = 10C0EEFE
D7 = 0001515E A7 = 00013412 SR = tSxnzvc Int = 0

10C0EEFE *MOVEQ.L #$01,D0 | 7001 

sc		<= Display stack crawl. The routines are listed
                   in order from oldest (at top) to newest (at bottom).
		   In this example, the current routine was called from 
                   EventLoop+0016. 
Calling chain using A6 Links:
 A6 Frame   Caller
 00000000  10C68982  cjtkend+0000
 00015086  10C6CA26  __Startup__+0060
 00015066  10C6CCCE  PilotMain+0250
 00014FC2  10C0F808  SysAppLaunch+0458
 00014F6E  10C10258  PrvCallWithNewStack+0016
 00013418  10CD88B2  __Startup__+0060
 000133F8  10CDB504  PilotMain+0036
 000133DE  10CDB47C  EventLoop+0016

dm pc		<= Display memory at the program counter
10C0EEFE: 70 01 60 00 01 7E 4E 4F A0 BE 70 01 60 00 01 74 "p.`..~NO..p.`..t"

il pc		<= Disassemble code at program counter
'SysHandleEvent 10C0E9EC'
+$0512 10C0EEFE *MOVEQ.L #$01,D0 			| 7001 
+$0514 10C0EF00 BRA.W SysHandleEvent+$0694 ; 10C0F080 	| 6000 017E 
+$0518 10C0EF04 _SysLaunchConsole ; $10C0E30C 		| 4E4F A0BE 
+$051C 10C0EF08 MOVEQ.L #$01,D0 			| 7001 
+$051E 10C0EF0A BRA.W SysHandleEvent+$0694 ; 10C0F080 	| 6000 0174 
+$0522 10C0EF0E MOVEQ.L #$00,D0 			| 7000 
+$0524 10C0EF10 BRA.W SysHandleEvent+$0694 ; 10C0F080 	| 6000 016E 
+$0528 10C0EF14 CLR.L -$0010(A6) 			| 42AE FFF0 
+$052C 10C0EF18 PEA -$0006(A6) 				| 486E FFFA 
+$0530 10C0EF1C PEA -$000C(A6) 				| 486E FFF4 

dm 0		<= Display memory at address 0
00000000: FF FF FF FF 1A 34 3E 40 10 C0 92 D4 10 C0 92 F2 ".....4>@........"

sb 0 1 2	<= Set the two bytes at address 0 to 1 & 2
Memory set starting at 00000000

dm 0 20		<= Display 0x20 bytes at address 0
00000000: 01 02 FF FF 1A 34 3E 40 10 C0 92 D4 10 C0 92 F2 ".....4>@........"
00000010: 10 C0 93 10 10 C0 93 18 10 C0 93 20 10 C0 93 28 "........... ...("

dw 0		<= Display the word at address 0
Word at 00000000 = $0102 #258 #258 '..'
dm 0 RectangleType <= Display memory at address 0 as a RectangleType
                      structure
00000000  struct    RectangleType
            {
00000000    PointType topLeft
              {
00000000      SWord     x         = $0102
00000002      SWord     y         = $-1
              }
00000004    PointType extent
              {
00000004      SWord     x         = $1A34
00000006      SWord     y         = $3E40
              }
            }

Flow Control

Probably the most commonly used debugger commands are those for flow control and include single-stepping, breakpoints, and continuing. These are summarized below along with their most commonly used options.

g Go
s Step Into. Will step into subroutine calls and system traps.
t Step Over. Will step over subroutine calls and system traps
gt <address> Go Till address. Sets a temporary breakpoint at <address> and continues execution.
br <address> Set breakpoint at <address>. PalmDebugger currently supports up to 5 breakpoints at any one time. Also note that although breakpoints are supported on ROM addresses, these types of breakpoints will force the device to run very slow - it will essentially single step until the program counter reaches a set breakpoint address. Breakpoints set in RAM do not incur any performance penalty.
cl [<address>] Clear breakpoint at <address> or, if no address, clear all breakpoints
brd Display all breakpoints
atb <"systemTrapName"> A-Trap break. Set a breakpoint at the given system call.
atc <"systemTrapName"> A-Trap clear. Clear breakpoint at the given system call.
atd Display all current A-Trap breaks.
ss <address> Step Spy. Execute instructions until the DWord at <address> changes value. Warning: this routine essentially makes the processor single step through instructions until the data at <address> changes and thus makes the device run very slow.
reset Perform a soft reset of the device

The following example shows how to use most of these commands. You can follow along by first putting the device into the debugger stub by typing Shortcut-.-1. The commands entered by the user into the debugger window of PalmDebugger are shown in bold. One of the shortcuts you'll see illustrated below is using the ':' character to represent the starting address of the current routine when entering an address. This is a very handy shortcut to use when setting breakpoints or disassembling code.

att		<= Attach to device
EXCEPTION ID = $A0
+$0512 10C0EEFE *MOVEQ.L #$01,D0 			| 7001 

il pc		<= Disassemble at current program counter
'SysHandleEvent 10C0E9EC'
+$0512 10C0EEFE *MOVEQ.L #$01,D0 			| 7001 
+$0514 10C0EF00 BRA.W SysHandleEvent+$0694 ; 10C0F080 	| 6000 017E 
+$0518 10C0EF04 _SysLaunchConsole ; $10C0E30C 		| 4E4F A0BE 
+$051C 10C0EF08 MOVEQ.L #$01,D0 			| 7001 
+$051E 10C0EF0A BRA.W SysHandleEvent+$0694 ; 10C0F080 	| 6000 0174 
+$0522 10C0EF0E MOVEQ.L #$00,D0 			| 7000 
+$0524 10C0EF10 BRA.W SysHandleEvent+$0694 ; 10C0F080 	| 6000 016E 
+$0528 10C0EF14 CLR.L -$0010(A6) 			| 42AE FFF0 
+$052C 10C0EF18 PEA -$0006(A6) 				| 486E FFFA 
+$0530 10C0EF1C PEA -$000C(A6) 				| 486E FFF4 

sc		<= Display stack crawl. The routines are listed
                   in order from oldest (at top) to newest (at bottom).
		   In this example, the current routine was called from 
                   EventLoop+0016. 
Calling chain using A6 Links:
 A6 Frame   Caller
 00000000  10C68982  cjtkend+0000
 00015086  10C6CA26  __Startup__+0060
 00015066  10C6CCCE  PilotMain+0250
 00014FC2  10C0F808  SysAppLaunch+0458
 00014F6E  10C10258  PrvCallWithNewStack+0016
 00013418  10CD88B2  __Startup__+0060
 000133F8  10CDB504  PilotMain+0036
 000133DE  10CDB47C  EventLoop+0016

s		<= Single-Step one instruction
'SysHandleEvent' Will Branch
+$0514 10C0EF00 *BRA.W SysHandleEvent+$0694 ; 10C0F080 	| 6000 017E 
 		<= Just hit Enter to repeat the Single-Step 
+$0694 10C0F080 *MOVEM.L (A7)+,D3-D5/A2-A4 		| 4CDF 1C38 
 		<= Just hit Enter to repeat the Single-Step 
+$0698 10C0F084 *UNLK A6 				| 4E5E 
 		<= Just hit Enter to repeat the Single-Step 
+$069A 10C0F086 *RTS 					| 4E75 8E53 7973 4861 
 		<= Just hit Enter to repeat the Single-Step 
+$0018 10CDB47E *TST.B D0 				| 4A00 

il		<= Disassemble at current program counter
'EventLoop 10CDB466'
+$0018 10CDB47E *TST.B D0 				| 4A00 
+$001A 10CDB480 LEA $000C(A7),A7 			| 4FEF 000C 
+$001E 10CDB484 BNE.S EventLoop+$0050 ; 10CDB4B6 	| 6630 
+$0020 10CDB486 PEA -$001A(A6) 				| 486E FFE6 
+$0024 10CDB48A PEA -$0018(A6) 				| 486E FFE8 
+$0028 10CDB48E MOVE.L -$0024(A5),-(A7) 		| 2F2D FFDC 
+$002C 10CDB492 _MenuHandleEvent ; $10C4B768 		| 4E4F A1BF 
+$0030 10CDB496 TST.B D0 				| 4A00 
+$0032 10CDB498 LEA $000C(A7),A7 			| 4FEF 000C 
+$0036 10CDB49C BNE.S EventLoop+$0050 ; 10CDB4B6 	| 6618 

gt 10cdb484	<= Go-Till address 0x10CDB484. Alternatively, we could
			have used :+1E to represent this address
'EventLoop' Will Branch
+$001E 10CDB484 *BNE.S EventLoop+$0050 ; 10CDB4B6 	| 6630 

br :+50		<= Set a breakpoint at current routine+0x50
Breakpoint set at 10CDB4B6 (EventLoop+0050)

g		<= Go
+$0050 10CDB4B6 *CMPI.W #$0016,-$0018(A6) ; '..' 	| 0C6E 0016 FFE8 

brd		<= Display all currently set breakpoints
10CDB4B6 (EventLoop+0050)

cl		<= Clear all breakpoints
All breakpoints cleared

atb "EvtGetEvent" <= Break whenever the EvtGetEvent system trap is called
A-trap set on 011d (EvtGetEvent)

g		<= Go. The unit will break as soon as EvtGetEvent is
			called. 
Remote stopped due to: A-TRAP BREAK EXCEPTION
'EvtGetEvent' 
+$0000 10C3B1E2 *LINK A6,$0000 				| 4E56 0000 

atd		<= Display list of A-Traps
Displaying A-Traps:
Function Name Trap # Library Address
===========================================================
EvtGetEvent $011D none 10C3B1E2

atc		<= Clear all A-Traps
All A-Traps cleared

ss a2		<= Step-Spy until the DWord at address 0x15404 (which is the
		   current value of register A2) changes. 
Step Spying on address: 00015404
'EvtGetSysEvent' 
  +$00E8  10C1E980  *CLR.B     $0008(A4)               | 422C 0008 

 

Heap and Database Commands

There is a set of commands that can be entered in the debugger window that mirror commands that the console window supports. These include commands for displaying information about databases and memory heaps. These are especially useful because often a bug involves a corrupted memory heap or database and you'll want to dump information about a heap or database while single-stepping through your program.

The following commands have the same options as their equivalents in the console window and if you do a help cmdName on them, you will see the same help information that the console window prints out. However, because the '-' symbol means to perform subtraction in the debugger window it can not be used when entering command options. Instead, use the backslash symbol (\). For example:

dir 0 \a
hl <cardNo> Heap List. Display a list of heaps for this card
hd <heapID> Heap Dump. Dump the contents of the given heap. The heapID can be obtained by first getting a heap list using the hl command
ht <heapID> Heap Total.  Display just the summary information about the heap - this is the same information printed out at the end of a heap dump.
hchk <heapID> Heap Check. Validate that the given heap is not corrupted. Both the HD and HT commands also verify a heap as part of their operation as well.
dir <cardNo> Directory. Dump a directory of databases for the given card.

 

Utility Commands

Finally, the last set of commands to cover here are the utility commands. These include commands for loading memory from a file image or vice-versa, for flashing new data into the FLASH ROM of a device, for running debugger scripts,  for defining templates for use with the 'dm' command, and for defining debugger command aliases.

For a good example of how to define structure templates and aliases, see the "SystemTypes" text file which appears in the "Scripts" sub-directory of PalmDebugger. This text file contains definitions of a large number of PalmOS system structures.

In all cases below, the default directory for the <"filename"> parameter is the "Device" sub-directory of PalmDebugger except as noted.

load <"filename"> <addr> Load a file into memory at the given address.
save <"filename"> <addr> <bytes> Create a file in the "Device" sub-directory of PalmDebugger which is an image of the memory at the given address
flash <"filename"> <addr> Program FLASH ROM with the contents of <filename>
run <"filename"> Execute the debugger commands contained in the text file "filename". The default directory is the not the "Device" sub-directory like the load, save and flash commands but rather the root PalmDebugger directory.
alias <"name"> [<"text">] Define or view (if no <text> parameter) a new alias command
aliases Display list of all defined aliases
typedef <template> [@]<"name"> Define a new template as the same as an existing template or as a pointer to an existing template
typedef struct <"name">
  > <template> [@]<"name">[[count]] [\-] [\%]
typeend
Define a new template structure called "name". The elements of the structure are each defined on a new line starting with '>'. The '\-' option means don't print that field. The '\%' option means print as a binary field. The [count] represents an array.
wh [<"funcName"> | <A-trap #> | \a <address>] Where command. Find the address of a system trap by name or number or identify the memory chunk, database name, and resource type and ID that contains a particular address (the \a form).
load "myfile.bin" 10000		<= Load a file image into memory
100%
#24576 bytes loaded at $00010000.
save "tmpfile.bin" 10000 1000  	<= Save memory contents to a file
100% 
#4096 bytes saved from address $00010000 to file "tmpfile.bin"
run "SystemTypes" 		<= Execute the debugger commands
				contained in the text file "SystemTypes"
alias "LowMem" 	"dm 0 LowMemType"
				<= Define a new command called "LowMem"
                             	that is an alias for the text "dm 0 LowMemType"

typedef struct "PointType"	<= Define two templates called PointType
                                   and RectangleType 
	> SWord		"x"
	> SWord		"y"
	typeend
	
typedef struct "RectangleType"
	> PointType	"topLeft"
	> PointType	"extent"
	typeend
	
dm 0 RectangleType		<= Display memory at address 0 according to
                                   the RectangleType template. 
00000000  struct    RectangleType
            {
00000000    PointType topLeft
              {
00000000      SWord     x         = $-1
00000002      SWord     y         = $-1
              }
00000004    PointType extent
              {
00000004      SWord     x         = $1A34
00000006      SWord     y         = $3E40
              }
            }
wh "memhandlenew"	<= Find the address of the given system trap 
Function Name                     Trap #  Library Address
===========================================================
memhandlenew                      $001E   none    10C11F4A
wh \a pc		<= Determine the database name, resource type
			  and resource ID that contains the current
			  program counter. 
pointer 1001B204 is 322 bytes into chunk 1001AEE2 in: 
 card: 0, heapID: 1
 database: "Text to Hex", resType: 'code', resID: 1 

Debugger Expressions   

Any commands entered in the debugger window can be written using expressions for 1 or more of the command arguments. Expressions were briefly introduced above in the command examples. This section describes the complete expression evaluation rules for reference.

All expressions must be entered without white space. White space is used to delimite parameters to commands.

The following operators are supported and shown from highest to lowest precedence:

.a .b .w .l .s unary left to right
~ + -  @ unary right to left
* / binary left to right
+ - binary left to right
& binary left to right
^ binary left to right
| binary left to right
= binary left to right

 

Cast Operators:
These are entered following a value, register name, address, or parenthesized expression. The byte, word, and dword casts truncate the value at the specified size, resulting in an unsigned integral value. The sign extension case performs a sign extension at the operands present size.

.a   address cast
.b   byte cast
.w word cast
.l dword cast
.s sign extension cast
Examples:
45.b
$45 #69 #69 'E'
45.w
$0045 #69 #69 '.E'
45.l
$00000045 #69 #69 '...E'

Unary Operators:

~ bitwise NOT
+ no operation
- change sign
Examples:
~1
$fe  #254  #-2  '.'

2*-1
$fe  #254  #-2  '.'

 

Dereference Operator:

This is similar to the 'C' language dereference operator '*'. The operand is either an address or an integral value

@ Retrieves 4 bytes as an unsigned integral value
@.a Retrieves 4 bytes as an address
@.b Retrieves 1 byte as an unsigned integral value
@.w Retrieves 2 bytes as an unsigned integral value
@.l Retrieves 4 bytes as an unsigned integral value
Examples:
@.l(A2)
$00040000  #262144  #262144  '....'

@.b(PC)
$70  #112  #112  'p'

@A7
$00000000  #0  #0  '....'

 

Binary Arithmetic Operators:

* Multiplication
/ Division
+ Addition
- Subtraction

 

Binary bitwise operators:

& Bitwise AND
^ Bitwise XOR
| Bitwise OR

 

Assignment operator::

= Assignment. This operator is used to change the value of any one of the processor's registers

Example:

reg
D0 = 00000102 A0 = 10C0EEF6 USP = 420024FD
D1 = 00000013 A1 = 10C0EF0E SSP = 000148F4
D2 = 0000000D A2 = 000149D0 
D3 = 00000000 A3 = 00015404 
D4 = 00014B06 A4 = 10CF26B0 
D5 = 00000000 A5 = 00013A16 
D6 = 00D1F876 A6 = 000149BC PC = 10C0EEFE
D7 = 0001515E A7 = 000148F4 SR = tSxnzvc Int = 0
'SysHandleEvent' 
+$0512 10C0EEFE *MOVEQ.L #$01,D0 	| 7001 

d0=45
$00000045 #69 #69 '...E'

Constants

Numbers may be entered as character constants, binary, hexadecimal, and decimal values.

A character constant is a string of one or more characters within single quotes. C-style escape sequences are supported. The result is an unsigned integral value of size determined as follows:

> 2 chars   dword
> 1 char word
1 char byte

Examples:

'xyz1'
$78797a31  #2021227057  #2021227057  'xyz1'

'a\'Y\''
$61275927  #1629968679  #1629968679  'a'Y''

'\123'
$53  #83  #83  'S'

A binary number is entered as a % (percent sign) followed by one or more binary digits. The size is determined as follows:

> 16 digits dword
> 8 digits word
1-8 digits byte

Example

%00111000
$38 #56 #56 '8'

A hexadecimal number is entered as 1 or more hexadecimal digits, or optionally a $ (dollar sign) followed by 1 or more hexadecimal digits. The size of the result is determined as follows:

> 4 digits dword
> 2 digits word
1-2 digits byte

Example

c123
$c123  #49443  #-16093  '.#'

$c123
$c123  #49443  #-16093  '.#'

Special Variables

The processor registers can be used as variables in any expression. The data registers are named d0 thru d7, the address registers are a0 thru a7, the program counter is pc, the status register is sr, and the stack pointer a7, can also be referenced using it's alias name sp.

By default, any string that can represent a register name is interpreted as a register name and not a hexadecimal value. To force the string to be interpreted as a hexadecimal value, either prepend it with a 0 or a '$' character:

For example:

dm a0		<= display memory at address stored in register A0
10C0EEF6: 11 FC 00 01 01 01 4E 48 70 01 60 00 01 7E 4E 4F "......NHp.`..~NO"

dm a0+d0	<= Add contents of register A0 to register D0 and
			use that as the address
10C0EFF8: 60 04 38 3C 02 07 4A 44 66 26 48 6E FF FA 48 6E "`.8<..JDf&Hn..Hn"

dm a0+0d0	<= This shows how to add the hex value 0xD0 instead of
                   the contents of register D0
10C0EFC6: 26 3C 73 79 6E 63 7A 09 60 2E 26 3C 73 79 6E 63 "&<syncz.`.&<sync"

Special Characters:

The following special characters are recognized in any expression:

. The last address entered
: The starting address of the current routine

Examples:

reg
D0 = 00000001 A0 = 10C0EEF6 USP = 420024FD
D1 = 00000013 A1 = 10C0EF0E SSP = 00014854
D2 = 0000001B A2 = 0000201C 
D3 = 000020AA A3 = 0000201C 
D4 = 00000000 A4 = 00001A04 
D5 = 00000002 A5 = 00013A16 
D6 = 00000000 A6 = 00014890 PC = 10C47284
D7 = 0001515E A7 = 00014854 SR = tSxnzvc Int = 0
'FrmDoDialog' 
+$006A 10C47284 *TST.B D0 				| 4A00 

dm sp
00014854: 00 01 48 78 00 00 20 AA 00 01 49 30 55 00 00 00 "..Hx.. ...I0U..."

dm .
00014854: 00 01 48 78 00 00 20 AA 00 01 49 30 55 00 00 00 "..Hx.. ...I0U..."

dm .+10
00014864: 1A 04 00 00 00 00 00 00 00 00 00 00 20 1C 00 00 "............ ..."

il pc
'FrmDoDialog 10C4721A'
+$006A 10C47284 *TST.B D0 				| 4A00 
+$006C 10C47286 ADDQ.W #$04,A7 				| 584F 
+$006E 10C47288 BNE.S FrmDoDialog+$0092 ; 10C472AC 	| 6622 
+$0070 10C4728A PEA -$001A(A6) 				| 486E FFE6 
+$0074 10C4728E PEA -$0018(A6) 				| 486E FFE8 
+$0078 10C47292 CLR.L -(A7) 				| 42A7 
+$007A 10C47294 _MenuHandleEvent ; $10C4B768 		| 4E4F A1BF 
+$007E 10C47298 TST.B D0 				| 4A00 
+$0080 10C4729A LEA $000C(A7),A7 			| 4FEF 000C 
+$0084 10C4729E BNE.S FrmDoDialog+$0092 ; 10C472AC 	| 660C 

il :
'FrmDoDialog 10C4721A'
+$0000 10C4721A LINK A6,-$0030 				| 4E56 FFD0 
+$0004 10C4721E MOVEM.L D3/A2,-(A7) 			| 48E7 1020 
+$0008 10C47222 MOVE.L $0008(A6),A2 			| 246E 0008 
+$000C 10C47226 MOVE.B #$01,-(A7) ; '.' 		| 1F3C 0001 
+$0010 10C4722A PEA -$0030(A6) 				| 486E FFD0 
+$0014 10C4722E _FrmActiveState ; $10C48380 		| 4E4F A33B 
+$0018 10C47232 MOVE.L A2,-(A7) 			| 2F0A 
+$001A 10C47234 _FrmSetActiveForm ; $10C45CC8 		| 4E4F A174 
+$001E 10C47238 BTST #$0005,$002A(A2) 			| 082A 0005 002A 
+$0024 10C4723E LEA $000A(A7),A7 			| 4FEF 000A 

il :+60
'FrmDoDialog 10C4721A'
+$0060 10C4727A BNE.S FrmDoDialog+$003E ; 10C47258 	| 66DC 
+$0062 10C4727C PEA -$0018(A6) 				| 486E FFE8 
+$0066 10C47280 _SysHandleEvent ; $10C0E9EC 		| 4E4F A0A9 
+$006A 10C47284 *TST.B D0 				| 4A00 
+$006C 10C47286 ADDQ.W #$04,A7 				| 584F 
+$006E 10C47288 BNE.S FrmDoDialog+$0092 ; 10C472AC 	| 6622 
+$0070 10C4728A PEA -$001A(A6) 				| 486E FFE6 
+$0074 10C4728E PEA -$0018(A6) 				| 486E FFE8 
+$0078 10C47292 CLR.L -(A7) 				| 42A7 
+$007A 10C47294 _MenuHandleEvent ; $10C4B768 		| 4E4F A1BF 

 

The Source Window

The source window is an output only window used for source level debugging. As you single step for example, the source window follows along and displays the source code and line number corresponding to the current program counter. You can also select a line in the source window for setting or clearing a breakpoint or for disassembling code. Currently, the source level debugging support only works with code built using the GNU gcc compiler for PalmOS, although other symbol file formats may be supported in the future.

The source window works in conjunction with the debugger window and the CPU registers window. For example, when you single step by entering commands in the debugger window, the source window will automatically track along and any breakpoints set or cleared from the debugger window are displayed in the source window as well. There are also dedicated menu commands and key equivalents for source level debugging that don't require you to enter commands in the debugger window.

The source window is split into two panes. Between the two panes is a thick horizontal line that is colored red when the device is halted in the debugger and green when the device is running code. The upper pane  is used to display the values of the local variables for the current routine and the lower pane is used to display the actual source code. By default, the source pane is automatically updated every time you single step in order to show the current source file and line number corresponding to the program counter. You can also scroll the source pane or even change to a different source file altogether for purposes of setting a breakpoint or just for viewing. The left margin of the lower pane is used to display indicators for breakpoints and the current program counter. Breakpoints will show up with a solid red circle next to the line and the current program location will be indicated by a green arrow.

The source level debugging is designed to support debugging of any type of executable code including applications, shared libraries, system extensions, or even interrupt handlers. In addition, any number of executables can be source level debugged simultaneously by loading multiple symbol files into the debugger. You can, for example, source level debug an application and a shared library that it uses at the same time.

In order to source level debug an executable, you must first "associate" a symbol file with the code for the executable that's loaded onto the device. In a nutshell, this simply means telling the debugger the starting address of the code on the device and the name of the symbol file on the desktop that contains the symbol information for that code (there are simple menu commands for doing this). Any number of symbol files can be loaded into the debugger at once and whenever the device stops in the debugger stub for any reason, PalmDebugger will automatically determine which symbol file corresponds to the current program counter and display the appropriate source file and line number if one was found.

A quick example will help to illustrate how this works. In order to source level debug an application, you could do the following:

  1. Insure that the console stub is launched on the device by entering the Shortcut-.-2 sequence.
  2. Select the "Install Database and Load Symbols" menu command from the "Source" menu.
  3. From the open file dialog, select the .PRC file to load onto the device.
  4. PalmDebugger will now "import" that .PRC file onto the device and also look in the same directory for an associated symbol file. It will then automatically associate that symbol file with the address in memory of where the application was just installed.
  5. At this point, the symbol file for that application is loaded into the debugger and anytime the debugger breaks in code belonging to that application, the source window will display the associated source file and line number. Alternatively, you can break into the debugger manually (using either Shortcut-.-1 or the "Break" command from the source menu") and set a breakpoint on a certain source line of the application using the "Toggle Breakpoint" menu command from the "Source Menu" or from the right-mouse button context menu of the source window.

If the executable you wish to debug is already loaded onto the device or is in ROM, then steps 2 thru 4 above can be replaced by selecting the "Load Symbols..." command from the "Source" menu and selecting the symbol file for that executable.

 

How Symbol Files are Used

This section briefly describes what's contained in a symbol file and how PalmDebugger uses that information. It is recommended reading so that you can better understand and utilize the source level debugging support and its various features.

A symbol file is created by the linker and represents a single code resource. Most applications for PalmOS contain only a single code resource that has a resource type of 'code' and a resource ID of 1. More complex applications may have more than 1 code resource and consequently more than 1 symbol file. Within the symbol file are names of each of the source files that were linked together to create the code resource along with the offset from the start of the code resource to the object code for each source file and each line within the source file.  Also within the symbol file are descriptions of the various data structures used and the names, types, and locations of each of the local variables for each routine as well as the global variables.

Besides the symbol file, the only other information required by PalmDebugger is the address of the code resource on the device that corresponds to that symbol file. The "Load Symbols..." and the "Install Database and Load Symbols" menu commands perform the task of lining up a symbol file with the address of the associated code resource on the device.

 

The Load Symbols Menu Commands

There are two menu commands for loading source level symbols. The "Load Symbols for current Program Counter..." command, which is only enabled when the device is halted in the debugger, and the "Load Symbols..." command, which is only enabled when the device is not halted in the debugger.

If you select the "Load Symbols for current Program Counter..." command, the debugger first attempts to identify which code resource and database the program counter is currently in (this same information can be obtained manually using the "wh \a <address>" command in the debugger window where <address> is the current program counter). Once it identifies the database and code resource, it presents an open file dialog and asks the user to choose the corresponding symbol file named "<DatabaseName>.<resType>.<resID>.sym". If, for example, the program counter was found in the 'code' #1 resource of a database named "Text to Hex", it will ask the user to choose the symbol file "Text to Hex.code.1.sym"

If you select the "Load Symbols..." command, then you will be immediately presented with an open file dialog asking you to first select a symbol file. The symbol file you select must have a name of the form: "<DatabaseName>.<resType>.<resID>.sym" like, for example, "Text to Hex.code.1.sym". After you select a symbol file, the debugger will look up the address of the associated database code resource on the device and "line up" the symbol file with that address. Note that the "<DatabaseName>" portion of the symbol file name most correspond exactly to the PalmOS name of the database, which is the name that shows up when doing a directory listing of databases on the device (using the "dir" command of the console window) and is not always the same as the name of the .PRC file for that database.

Finally, the "Install Database and Load Symbols..." command is a sort of macro command that does two things: it first imports a PRC file into the device and then automatically loads the associated symbol file for that PRC file. The same results can be obtained by first manually importing the PRC file using the "import" command on the console window and then loading the symbols using the "Load Symbols..." menu command. When you choose the "Install Database and Load Symbols..." menu command, you will be presented with an open file dialog asking you to pick a PRC file. After importing this PRC file into the device, PalmDebugger will automatically look in the same directory for a file named "<DatabaseName>.code.1.sym" and associate this symbol file with the newly imported database.

 

The Source Menu

The "Source" menu contains commands for source level debugging and for loading and releasing symbol files. The "Load Symbols...", "Load Symbols for current Program Counter...", and "Install Database and Load Symbols..." commands were already covered in the previous section. The "Remove all Symbols" command will unload all symbol files which are currently loaded. Once at least one symbol file is loaded, the remaining commands in the menu can be used for setting source level breakpoints, stopping, continuing, etc.

The "Break" command is only enabled when the device is not already halted in the debugger. This command simulates entering the "Shortcut-.-1" sequence on the device and is usually more convenient to use than the equivalent Graffiti sequence. This command only works whe the console stub is running on the device however since it relies on the console communication in order to send a key event to the device.

The "Step Into" and "Step Over" commands work at the source level. Both commands single step one source line at a time - the difference being that "Step Into" command will  step into a subroutine if one is about to be called, whereas "Step Over" won't stop till it reaches the source line after the subroutine call.

The "Go" command continues execution and is the same as entering the "g" command in the debugger window. The "Go Till" command will set a temporary breakpoint at the currently selected line in the source window and then continue execution.

The "Toggle Breakpoint" command will toggle a breakpoint at the currently selected line in the source window. The "Disassemble at Cursor" command will disassemble code at the currently selected line in the source window. The output of this command will appear in the debugger window.

Finally, the "Show Current Location" command will automatically scroll the source window to show the current source file and line number. This is useful if you've previously scrolled the source window to set or clear a breakpoint or temporarily changed it to view a different source file (using the context menu, described below).

 

The Source Window Context Menu

The source window has a context menu which can be activated by right-clicking with the mouse. This menu has many of the same commands which also appear in the "Source" menu of the menu bar including "Break", "Go Till", "Toggle Breakpoint", "Disassemble at Cursor", and "Show Current Location".

Also present in the context menu are pop-up items for selecting which source file to view. Every symbol file that is loaded presents a pop-up that lists the source files for that symbol file. Using these menus, you can change the current source file for viewing purposes or for setting and clearing breakpoints.

 

Limitations

Unfortunately, the source level support in PalmDebugger is still quite limited compared to most modern source level debuggers. It provides just the bare essentials for source level debugging and you'll find that there are still numerous occasions where you will have to switch to the assembly level debugger window and enter commands there for certain functions:

  1. The source window does not display a current stack crawl. To get a stack crawl, you'll have to enter the "sc" command in the debugger window.
  2. You can not view the contents of structures in the local variables pane of the source window. Local variables that are structures, or pointers to structures, will simply be displayed as hexadecimal addresses. In order to view the contents of the structure, you'll have to switch to the debugger window and use the 'dm' command.
  3. Global variables are not displayed.
  4. Local variables are only displayed in hexadecimal format.
  5. The values of local variables can not be changed from the source window. The only way to change them is to use the sb, sw, or sl commands from the debugger window and you must enter the address of the variable manually.

 

Debugging Hints

This section describes a few hypothetical debugging situations. These sessions are included to help familiarize you with the debugging commands and how they can be used together to help track down certain types of problems.

 

Entering the Debugger

The most common way to enter the debugger is to enter the Graffiti sequence "Shortcut-.-1", that is, the shortcut stroke followed by two taps to generate a period, followed by the number 1 ( in the right side of the Graffiti area).

If you've started the console stub on the device already (using the "Shortcut-.-2" sequence), you can use the "Break" command in the "Source" menu. This command sends a key event to the device using the console stub communications and this key event is identical to entering the Shortcut-.-1 Graffiti sequence by hand.

You can also rebuild your executable with a compiled breakpoint in it by making a call to DbgBreak(). Remember though that unless you've entered the debugger at least once already using the Shortcut-.-1 sequence, a DbgBreak() call will display a fatal error dialog instead of placing you in the debugger.

Finally, you can enter the debugger immediately after reset by holding the down button and pressing the reset button in the back of the device with a paper clip. This will put you into the "SmallROM" debugger which is bootstrap code placed into the very front of the ROM that contains just enough code to initialize the hardware and startup the debugger stub. If you enter the "g" command at this point, the system will jump to the "BigROM". The "BigROM" contains the same code as the "SmallROM" as well as the rest of the system code. If you press the down button on the device while executing the "g" command, you will end up in the "BigROM's" debugger. You can now set a-trap breaks, or single step through the boot-up sequence.

 

Finding Code

A common problem is finding the location in memory where code resides - the ultimate goal being able to single step through the problem code to find out what exactly is going wrong. Depending on the circumstances, you may use any one of a number of different methods.

Method 1:

If you're debugging an application and are able to rebuild it, it is sometimes easiest just to re-build the application with a DbgBreak() call in the problem routine. This is a "compiled" breakpoint and will cause your program to break into the debugger at that line.

Method 2:

A second method is to use PalmDebugger to set an a-trap break on a system call that the problem routine makes. Ideally, it would be a system call that only that routine makes so that you won't get false triggers from other routines making the same call. As an example, if you wanted to find your application's main event loop, you could set an a-trap break on EvtGetEvent():

atb "evtgetevent"
A-trap set on 011d (evtgetevent)
g
Remote stopped due to: A-TRAP BREAK EXCEPTION
'EvtGetEvent' 
+$0000 10C3B1E2 *LINK A6,$0000 | 4E56 0000 

When you break due to an a-trap break, you will end up at the beginning of the system call. At this point, the return address on the stack is the routine that actually made the system call which in this case is your application's main event loop. To get back to this routine, you can either single-step through the EvtGetEvent() call until it returns (not recommended!) or set a temporary breakpoint at the return address on the stack. To do this, use the following command:

gt @sp
EXCEPTION ID = $80
'EventLoop' 
+$0016 1001B2E6 *MOVE.L A2,-(A7) | 2F0A 

The '@' operator fetches the long word at the given address (the value stored in the stack pointer) and thus the expression '@sp' yields the return address on the stack.

You are now at the instruction in your main event loop immediately after the EvtGetEvent() call. It this point, you may want to disassemble this routine from the beginning. This is where the ':' symbol, which represents the starting address of the current routine, is handy:

il :
'EventLoop 1001B2D0'
+$0000 1001B2D0  LINK A6,-$001C 		| 4E56 FFE4 
+$0004 1001B2D4  MOVEM.L D3-D4/A2,-(A7) 	| 48E7 1820 
+$0008 1001B2D8  LEA -$0018(A6),A2 		| 45EE FFE8 
+$000C 1001B2DC  PEA $00000032 ; 00000032 	| 4878 0032 
+$0010 1001B2E0  MOVE.L A2,-(A7) 		| 2F0A 
+$0012 1001B2E2  _EvtGetEvent ; $10C3B1E2 	| 4E4F A11D 
+$0016 1001B2E6 *MOVE.L A2,-(A7) 		| 2F0A 
+$0018 1001B2E8  _SysHandleEvent ; $10C0E9EC 	| 4E4F A0A9 
+$001C 1001B2EC  ADD.W #$000C,A7 		| DEFC 000C 
+$0020 1001B2F0  TST.B D0 			| 4A00 

Another way to double check that you are where you want to be, is to perform a stack crawl. The routines are displayed from "oldest" at the top to "newest" at the bottom.

sc
Calling chain using A6 Links:
A6 Frame Caller
00000000 10C68982 cjtkend+0000
00015086 10C6CA26 __Startup__+0060
00015066 10C6CCCE PilotMain+0250
00014FC2 10C0F808 SysAppLaunch+0458
00014F6E 10C10258 PrvCallWithNewStack+0016
0001491E 1001CC7E start+006E
000148E6 1001CF44 PilotMain+001C

Yet another check is to get a list of the opened databases. If you are in the application you expect to be in, it should appear as one of the opened databases. Note that the "System" and "Graffiti ShortCuts" databases are always opened by the system and will always appear at the bottom of this list.

opened
name 	              resDB cardNum accessP  ID      openCnt mode
---------------------------------------------------------------------------
Tex2HexDB 		no  0 	    00015146 0001825B 1      0003
*Text to Hex 		yes 0       00016DD2 0001821B 1      0001
*Graffiti ShortCuts 	yes 0       00017D5C 0001812B 1      0007
*System 		yes 0       00017FEE 00D20A44 1      0005
----------------------------------------------
Total: 4 databases opened

Note the '*' to the left of the instruction after the _EvtGetEvent call - this marks the current location of the program counter. The other thing to notice is that PalmDebugger was able to tell the name of the routine without us having to load a symbol file - this is possible because the name of the routine is included in the code itself by the compiler. If you display memory immediately after the return instruction of a routine, you can see the name of the routine imbedded in the code there:

il :+6c
'EventLoop 1001B2D0'
+$006C 1001B33C _FrmDispatchEvent ; $10C4769A 	| 4E4F A1A0 
+$0070 1001B340 ADDQ.W #$04,A7 			| 584F 
+$0072 1001B342 CMPI.W #$0016,-$0018(A6) ; '..' | 0C6E 0016 FFE8 
+$0078 1001B348 BNE.S EventLoop+$000C ; 1001B2DC | 6692 
+$007A 1001B34A MOVEM.L -$0028(A6),D3-D4/A2 	| 4CEE 0418 FFD8 
+$0080 1001B350 UNLK A6 			| 4E5E 
+$0082 1001B352 RTS 				| 4E75 8945 7665 6E74 
dm 1001b354
1001B354: 89 45 76 65 6E 74 4C 6F 6F 70 00 00 4E 56 00 00 ".EventLoop..NV.."

Method 3:

This brings us to the third way to find your code - by the name of the routine. The 'ft' command in the debugger window will find text, and if your routine name is fairly unique is an easy way to find your routine. The 'ft' command takes 3 arguments: the text to find, a starting address, and the number of bytes to search. To search the first megabyte of RAM on a Palm III for example, use this command:

ft "EventLoop" 10000000 100000
dm 100005C4 ;100005C4: 45 76 65 6E 74 4C 6F 6F 70 63 61 74 69 6F 6E 00 "EventLoopcation."
After the above line is printed, hit Enter again without parameters to repeat the find
dm 1001B355 ;1001B355: 45 76 65 6E 74 4C 6F 6F 70 00 00 4E 56 00 00 2F "EventLoop..NV../"

The 'ft' command will stop at the first successful find of the text and will continue the search from where it left off if you hit Enter again without any parameters. All existing PalmPilots except for the PalmV haveRAM starting at address 0x10000000. The PalmV RAM starts at address 0. To search ROM instead of RAM, use address 0x10C00000. Note that the first occurance of the text was found at 0x100005C4 - this is actually an alias of the debugger globals that are stored in low memory at address 0x05C4 and is a copy of the string you asked the debugger to search for and not the actual routine. To insure that the address is of the routine you want, disassemble code before that address:

il 1001b355-23
'EventLoop 1001B2D0'
+$0062 1001B332 _FrmSetEventHandler ; $10C47672 	| 4E4F A19F 
+$0066 1001B336 ADDQ.W #$08,A7 				| 504F 
+$0068 1001B338 PEA -$0018(A6) 				| 486E FFE8 
+$006C 1001B33C _FrmDispatchEvent ; $10C4769A 		| 4E4F A1A0 
+$0070 1001B340 ADDQ.W #$04,A7 				| 584F 
+$0072 1001B342 CMPI.W #$0016,-$0018(A6) ; '..' 	| 0C6E 0016 FFE8 
+$0078 1001B348 BNE.S EventLoop+$000C ; 1001B2DC 	| 6692 
+$007A 1001B34A MOVEM.L -$0028(A6),D3-D4/A2 		| 4CEE 0418 FFD8 
+$0080 1001B350 UNLK A6 				| 4E5E 
+$0082 1001B352 RTS 					| 4E75 8945 7665 6E74 

Note that we took the address of the found text (0x1001b355) and subtracted 23 bytes. We choose an odd number because all instructions must be on word boundaries and 0x1001b355 is an odd address.

Method 4:

Finally, the last (and sometimes the easiest) method to find your code is to take advantage of the source level debugging support. Assuming you've built your application with the gcc compiler and have generated a symbol file, load the symbol file using the "Load Symbols..." menu command. Remember to launch the console stub on the device first or the "Load Symbols..." command will not work. Once the symbol file is loaded, break into the debugger stub on the device using the "Break" command of the Source menu, select the source line in the source window of PalmDebugger, and set a breakpoint there using the "Toggle Breakpoint" command from the "Source" menu or from the source window's context menu.

 

Finding Memory Trashing Bugs

Memory trashing bugs are often the hardest kind of bugs to track down. A bug in the code could trash low memory globals used by the operating system, the dynamic memory heap, or an application variable. A memory corruption in any one of these areas could cause very unpredictable behavior.

The first line of attack on these kinds of bugs is typically to divide and conquer: using form of binary search, you try and narrow down what portion of the code is corrupting memory.

 

Heap Corruptions

If a bug trashes a memory heap, you might get a fatal error message put up by the Memory or Data Managers or you may simply crash on some unrelated manager due to a side effect. In any case, if you ever do suspect a corrupted heap, use the 'hd 0' command to dump the dynamic heap. If the heap is in a valid state, you'll see the heap dump complete and print out totals at the bottom of the heap for amount of memory used and free and other statistics:

hd 0
Displaying Heap ID: 0000, mapped to 00001480
                             req    act                             resType/  #resID/
 start    handle   localID   size   size  lck own flags type  index attr ctg  uniqueID name
--------------------------------------------------------------------------------------------
-00001534 00001490 F0001491 00001E 000026  #0  #0    fM Alarm Table
-0000155A 00001494 F0001495 000456 00045E  #0  #0    fM Graffiti Private
-000019B8 00001498 F0001499 000012 00001A  #0  #0    fM DataMgr Protect List (DmProtectEntryPtr*)
...
-00017DC6 -------- F0017DC6 0001F4 0001FC  #0 #15    fM Handle Table: 'Graffiti ShortCuts'
-00017FC2 -------- F0017FC2 000024 00002C  #0 #15    fM DmOpenInfoPtr: 'Graffiti ShortCuts'
-00017FEE -------- F0017FEE 00000E 000016  #0 #15    fM DmOpenRef: 'Graffiti ShortCuts'
--------------------------------------------------------------------------------------------
Heap Summary: 
  flags:              8000
  size:               016B80
  numHandles:         #40  
  Free Chunks:        #14     (010A90 bytes)
  Movable Chunks:     #52     (006040 bytes)
  Non-Movable Chunks: #0      (000000 bytes)

For a faster check of the heap without a heap dump, use the 'hchk' command which simply checks the validity of the heap:

hchk 0
Heap OK

By breaking into the debugger at various portions of execution and checking the heap using either hd or hchk, as shown above, you can narrow down the heap corruption.

Another method for tracking down heap corruption is to use the 'mdebug' command in the console window. This command is one of the console commands that can be executed even when the debugger is attached (see "Using the Console Window When the Debugger is Attached" below for more info). This command puts the device into any one of a set of various heap checking modes. Basically, when the device is in this mode, it will perform an automatic heap check and verification on every memory manager call. If it detects a heap corruption during any one of these checks, it will automatically break into the debugger. Type 'help mdebug' to get a full list of options for this command. By turning various memory checking features on and off, you can usually strike an acceptable balance between performance (various modes can slow down performance considerably) and coverage.

mdebug -partial
Current mode = 001A
Only Affected heap checked/scrambled per call
Heap(s) checked on EVERY Mem call
Heap(s) scrambled on EVERY Mem call
Free chunk contents filled & checked

Minimum dynamic heap free space recording OFF

 

Global Variable Corruptions

Some bugs might trash a global - either a low memory system global or an application global. The effects of this type of corruption are generially unpredictable and the process of tracking them down is usually quite difficult and hard to generalize.

Once you have managed to determine which address in memory is being corrupted however, you can usually use the 'ss' (Step-Spy) command to help determine where the bug in the code is. The 'ss' command will put the processor in a single step mode where it will automatically check the contents of a given address after every instruction and automatically break into the debugger if the contents ever change. It takes one parameter which is the address of a DWord in memory to check.

ss 100
Step Spying on address: 00000100

Because the 'ss' command makes the processor single-step through instructions, it will make the device run considerably slower so you will usually want to narrow the bug down to a certain area before using this command.

 

 

Viewing Local Variables and Function Parameters

If you are debugging using the source level window, the local variables and parameters for functions are displayed in the upper pane of the window. If you don't have access to symbol information however, you will have to manually look up the variable values using commands in the debugger window. This section walks through how to do this with a typical function

To illustrate the process, we will use the following example routine:

static Boolean 
MainFrmEventHandler (EventPtr eventP)
{
  FormPtr 	formP;
  Boolean 	handled = false;
  Err        	err;
  char        	buffer[64];
  DWord        	bytes=0;
  SWord        	i;
  static    	char prevChar = 0;

  // See if StdIO can handle it
  if (StdHandleEvent (eventP)) return true;
    
  // body of function omitted for clarity
  ...

  return false;
}

If we were to break into the debugger at the beginning of the routine, right before it calls StdHandleEvent(), we would see the following disassembly:

il :
'MainFrmEventHandler 1001E296'
+$0000 1001E296  LINK A6,-$0048 			| 4E56 FFB8 
+$0004 1001E29A  MOVEM.L D3-D5/A2,-(A7) 		| 48E7 1C20 
+$0008 1001E29E  MOVE.L $0008(A6),A2 			| 246E 0008 
+$000C 1001E2A2  CLR.B D5 				| 4205 
+$000E 1001E2A4  CLR.L -$0044(A6) 			| 42AE FFBC 
+$0012 1001E2A8 *MOVE.L A2,-(A7) 			| 2F0A 
+$0014 1001E2AA  BSR.W StdHandleEvent ; 1001F214 	| 6100 0F68 
+$0018 1001E2AE  ADDQ.W #$04,A7 			| 584F 
+$001A 1001E2B0  TST.B D0 				| 4A00 
+$001C 1001E2B2  BEQ.S MainFrmEventHandler+$0024 ; 1001E2BA | 6706 

When a routine enters, the first DWord on the stack is the return address and after that are the parameters, from left to right. If for example we were to display memory that the stack pointer points to on the first instruction of the routine (the LINK instruction), we would see the following:

dm sp
00014A2A: 10 C4 77 00 00 01 4A 4E 00 01 4A 4E 00 01 51 0E "..w...JN..JN..Q."

The first DWord (0x10C47700) is the return address of the routine. The second DWord (0x00014A4E) is the 'eventP' parameter to the routine. If there were another parameter to the routine, it would follow the 'eventP' parameter on the stack.

After the LINK instruction executes however, the stack pointer register is changed. What happens is the stack pointer is decremented to make room for local variables (in this example, to make room for 0x48 bytes of local variables) and room for a saved value of the A6 register. Also, the A6 register is changed to point the beginning of the function's stack frame. The A6 register then acts like a stack frame pointer and is used by the rest of the routine to access function parameters and local variables. Here's a picture of what the stack looks like after the LINK instruction:

      Address : Contents     
  -------------------------------------------------------
  A7 => 149CE                   <= new "top" of stack
              : ...             <= 0x48 bytes of local variables
  A6 => 14A26 : 00 01 4A 3A     <= saved value of A6
   	14A2A : 10 C4 77 00    	<= return address
	14A2E : 00 01 4A 4E	<= eventP parameter

Thus, if you display memory at register A6, you will see the following:

dm a6
00014A26: 00 01 4A 3A 10 C4 77 00 00 01 4A 4E 00 01 4A 4E "..J:..w...JN..JN"

The first DWord that A6 points to will be the old value of A6, the next DWord is the return address of the routine, and after that are the parameters to the function. Thus, the first parameter to the function can always be found at 8(A6) and indeed if you look at the function disassembly above, you can see that one of the first things it does is after the LINK instruction is copy the value of the 'eventP' parameter from 8(A6) to register A2.

The function local variables are placed at memory locations before A6. For example, the 'bytes' local variable which is a DWord, is found at -$0044(A6) and is cleared to 0 by the instruction at address 0x1001E2A4. It is not always easy to tell where each local variable is on the stack except by disassembling the code and looking for places in the code where that variable is accessed. Once you know the offset of the variable, you can view by using an expression with A6. To view the value of the 'bytes' parameter, for example, you could use the following command:

dm -44+a6
000149E2: 00 00 00 00 00 00 1A 0C 20 00 20 04 00 01 4A 08 "........ . ...J."

 

Using the Console Window When the Debugger is Attached

When the device is in the debugger stub and PalmDebugger is "attached", you normally will only use the debugger window to enter commands. There are however a subset of console commands that work even when the debugger is attached. The subset of console commands that work when the debugger is attached are ones that simply display information and don't change memory contents at all. These include 'dir', 'hl', 'hd', 'hchk', 'mdebug', and others.

When the debugger is attached, commands that you enter in the console window do not go through the normal channels and talk to the console stub on the device - they instead talk directly to the debugger stub so that can be executed even when the console stub has not been started.

By leveraging the use of these commands in the console window, you now have two windows at your disposal for displaying debugging information. This comes in handy, for example, for displaying a heap dump in the console window that you can view while single-stepping in the debugger window.

 

Changing PalmDebugger's baud rate

Whenever the debugger stub or console stub on the device starts up, they start out communicating at 57,600 baud. It is often desirable however to switch to a higher baud rate in order to download large applications or other files to the device or to a lower baud rate if you are using a serial cable without hardware handshaking lines.

If the device is in the debugger stub, and PalmDebugger is attached to the device, you can change the baud rate of both PalmDebugger and the device at the same time by choosing a new baud rate setting from the Connection menu of PalmDebugger. When PalmDebugger thinks the device is attached, it sends a request packet to the device's debugger stub telling it the new baud rate to use and then it switches over to the new baud rate itself. Keep in mind that this new baud rate setting will only remain in effect until the device is soft-resetted, or until you launch an application on the device that opens the serial port and changes the baud rate again.

You can also change the baud rate of both PalmDebugger and the device through the console stub. If you change PalmDebugger's baud rate when it is not attached to the debugger stub, it sends the new baud rate request to the console stub on the device instead of the debugger stub. Whether you change the baud rate through the console stub or the debugger stub, the other stub will use the new baud rate as well.

 

Debugging Applications that use the Serial Port

Debugging applications that use the serial port when using that same serial port for the debugger is tricky, but not impossible. As long as the application itself and the debugger stub are using the same baud rate, you can actually perform limited debugging functions. When the debugger stub starts up, it always initializes the serial port on the device to 57,600 baud and assumes it will stay at that baud rate unless it gets a message from PalmDebugger while attached telling it to change (as a result of the user picking a new baud rate from the Connection menu).

If the debugger stub on the device has been entered at least once already, and you later launch an application that opens the serial port, that application may change the baud rate. If it does, the debugger stub on the device will end up using that new baud rate the next time it enters. If this is the case, you will have to manually change the baud rate setting on the PalmDebugger application's Connection menu in order to communicate again with the device.

Of course, when you do enter the debugger stub on the device while debugging a serial application, the debugger stub will send data over the serial port and most likely disrupt serial communications with whatever host your PalmOS application was originally talking to. You can however, switch the serial cable back over to PalmDebugger, double-check your baud rate setting, and issue an 'att' command to attach to the device and perform "post-mordem" analysis at that point.

What you cannot do when using a serial port application on the device however is use the console stub. When the console stub starts up, it opens up the serial port just like a normal application would, and thus prevents any other application from successfully opening up the port at the same time. Likewise, if you are in an application that has the serial port open, you will not be able to start up the console stub. Remember though that a number of console commands can still be used even when the device is in the debugger stub. This subset of console commands (ones that merely display information about heaps and databases) are smart enough to know when the device is currently in the debugger stub, and can communicate with that stub instead of the console stub.

 

Importing System Extensions and Libraries

The console's 'import' command will import a new database or replace an existing database on the device. It can only replace an existing database however if that database is currently not open or protected. System extension databases and shared libraries present a problem because they are normally either kept open or marked protected so that they won't be deleted accidentally while they are in use by the system.

If you are developing a system extension or shared library and need to import a newer version to the device, you will have to make sure the old database is closed and unprotected first. If it is not, you will get the following error from the import command:

###Error $00000219 occurred

To get around this, soft-reset the device while pressing the "up" button on the device. The "up" button tells the system that it should not automatically load system extensions or shared libraries. You can then import your database and soft-reset again to make the system use the new version.