Order the book from O'Reilly

Previous PageTable Of ContentsIndexNext Page

In this chapter:

 10.  Debugging Palm Applications

There are a variety of useful tools to help you debug your Palm application. The best by far is the Palm OS Emulator (POSE). With it you can code, build, and test your handheld application, without ever leaving the comfort of your desktop. Another useful tool is the strategic use of the reset buttons. There are a couple of different forms that we discuss. There are also a number of hidden Graffiti shortcut characters that offer you debugging aids and shortcuts.

Source-level debugging is available for both CodeWarrior and GNU PalmPilot SDK. This goes a long way toward making your debugging job easier. Using the Simulator on Mac OS is also worth a brief discussion for those of you who will work on that platform.

Last, we discuss Gremlins-the useful testing creatures that bear not the slightest resemblance to fanciful beings. Gremlins in the Palm world are little monkeys who bash about randomly on your code looking for problems. You may not like them, but you will find them very helpful for catching bugs you might otherwise have missed.

Using POSE

Top Of Page

POSE emulates, at the hardware level, a Palm handheld. It emulates a Motorola Dragonball processor, a display, and so on. Actual Palm OS handhelds also contain ROM-the emulator requires ROM, as well (actually, a file containing a ROM image). POSE can emulate a 1.0, 2.0, or 3.0 OS device, depending on the ROM you provide.

POSE is based on Copilot, an application written by Greg Hewgill. POSE is supported by Palm Computing for both Windows and Mac (XPilot, a port of Copilot running under X Windows on Unix/Linux, also exists). Better yet, source code is provided. You are free to make changes, but if you do, please contribute them to Palm Computing. Your enhancements may be incorporated in the main code base, making life better for everybody.

NOTE:

POSE can be downloaded from http://www.palm.com/devzone. You should always check Palm's web site for the most recent version, as this tool evolves rapidly. It also comes with the Metrowerks CodeWarrior for Palm OS. Versions of Copilot are available, though not officially supported by Palm, for Unix and Linux. The Linux port is available at Red Hat Software (http://www.redhat.com).

Debug versions of 2.0 and 3.0 ROMs can be downloaded from Palm's web site (http://www.palm.com/devzone). These versions do extra sanity checking on calls; each can catch some problems that would cause a crash on a nondebug ROM (or problems that don't cause an error today but are still wrong).

Major Advantages to POSE

In our programming, we use POSE almost exclusively. Every once in a while, we download to an actual device for testing, but all the following reasons should make it clear why this is a less attractive alternative:

POSE provides source-level debugging

If you have ever tried to work on a platform that did not have tools for source-level debugging (we have!), you know how useful this is.

POSE doesn't need batteries

We don't have to buy AAA batteries nearly as often.

POSE doesn't need cables to download an application

It can download an application directly from your desktop machine without a cable. (If you want to HotSync with POSE, however, you need a cable.)

POSE can use the keyboard

You can use the keyboard as an alternative to Graffiti.

POSE on a laptop is a self-contained environment

We've done development and testing at the beach, in the car, poolside, and in many other places where it would have been very inconvenient to also have had a Palm OS device and associated cabling.

POSE detects bad programming practices

Though we personally don't need to worry about this, since there are never bugs in our code, POSE is great for finding all sorts of violations. It lets you know if you are trying to access low memory, system globals, screen memory, hardware registers, and unimplemented functions (okay, this one gets us occasionally).

Screenshots are a snap

It's easy to take screenshots (for product manuals or books!).

You can use POSE to demonstrate a Palm OS application

With an LCD projector, hundreds of people can see your application being demonstrated. If you don't know that this is an advantage, try displaying a Palm application running on an actual handheld to even two people.

Future POSE Features

POSE is a heavily revised application, and you should always check Palm's web site for a current version. This also means new features are in the works for POSE if they haven't already been added. Here are some of the forthcoming features that will be even more helpful for debugging:

Profiling

You'll be able to profile your code to determine where it is spending its time; almost a necessity for effective optimizing.

Illegal access checking

You can check for an application's accessing of any of the following memory locations:

Stack space

The emulator will check to make sure that the stack pointer doesn't exceed the space allocated for the stack.

Logging events

You will be able to keep a log of events, whether they are events in the event queue, a log of system or application functions that are called, or CPU opcodes that get executed.

Memory block checking

Soon POSE should be able to do all the following:

Minor Disadvantages to POSE

Though we hate to admit it, POSE is bad for certain things:

The speed isn't the same as on an actual device

POSE can be faster or much slower than an actual device, depending on the particulars of the desktop machine on which it is running. (This makes optimizing wickedly difficult.)

This isn't the ROM on which your final application will run

As a result, you still need to test with the nondebug version of the ROM from an actual handheld unit. While nondebug versions of the ROMs aren't available from Palm's web site, you can use POSE to copy the (nondebug) ROM from your handheld. POSE comes with a handheld application that lets you upload a ROM copy from your handheld to POSE. Even after testing with a nondebug ROM under POSE, you still want to test on an actual handheld device.

Graffiti is harder to use

It's much harder to use Graffiti with a mouse (or touchpad) than it is with a stylus. (Though, as we said, you should see some improvement in future revisions.)

POSE doesn't contain support for infrared

This means that all beaming functionality must be tested with actual handheld devices.

Some current versions of POSE can't reliably do serial communication

We've had problems with 2.0b3 on both Windows and Mac OS on all machine configurations. A good test for serial communication is to hook up a modem and attempt a dial-up TCP/IP connection; if POSE cannot do this on your machine, it will have problems with the serial communication code that you write. If you happen to be a serial communication wizard, please check to see if this has been fixed; if not, consider fixing it and giving back the changes!

Cross-Platform Capabilities

POSE is available for both Macintosh and Windows and both platforms share quite a range of capabilities. POSE on Mac and Windows can:

There are settings that control the following:

RAM size

Emulate the 128K of the original PalmPilot, or be the first on your block to run with 8MB of RAM!

Screen doubling

You can run with one pixel stretched to two pixels in either direction. This can make it easier to see little bitty controls and edges of things that aren't refreshing properly.

Communications port settings

This controls the emulator's connection to the available desktop ports.

Mac OS-Specific Commands

The Mac version has all commands in a menubar (see Figure 10-1).

-Figure 10- 1. Palm OS Emulator on Mac OS with commands in menu

Windows-Specific Commands

The Windows version has all commands in a pop-up menu. Right-click on the POSE window to pop-up the menu (see Figure 10-2).

Figure 10- 2. Palm OS Emulator on Windows with commands in pop-up menu

Once you have used POSE for a while, we think you will find it hard to imagine how you could have done handheld development without it. It is a very useful development and debugging tool.

Device Reset

Top Of Page

Now it is time to move back to a discussion of things on the handheld. There are a couple of different kinds of resets that can be done to your Palm device:

Soft reset

This is done by pressing the reset button with a blunt instrument, like an unfolded paper clip. This resets the dynamic heap but not the storage heaps, so no data is lost. Each installed application receives the sysAppLaunchCmdSystemReset launch code.

Hard reset

You do this by pressing the reset button while holding down the power key. You are provided with the option to erase everything in RAM. If you choose it, everything in RAM is erased, including all your data.

Debug reset

By pressing the reset button while pushing the down-arrow key, you get a debug reset. This puts the Palm device into debug mode. You see a flashing box in the upper left.

No-notify reset

This happens we you press the reset button while holding down the up-arrow key. The OS boots without sending reset launch codes to each application. This is essential to use if you've got a bug in your PilotMain (like trying to access globals without checking the launch code).

NOTE:

It's not uncommon to accidentally access globals when you shouldn't in your PilotMain (typically by not checking the launch code, for example). You can get into a vicious cycle in such cases. After a reset, your application is sent the sysAppLaunchCmdSystemReset launch code, at which point you access globals, at which point you crash and cause a reset, and so on, and so on, and so on.

NOTE:

The solution to this vexing problem is to use the no-notify reset, which allows the device to successfully boot. Now you can delete your application, fix the PilotMain, and download a new version. Of course, a hard reset would also solve the problem, but the cure would be worse than the disease.

Graffiti Shortcut Characters

Top Of Page

There are a number of hidden debugging aids that you can access using the Graffiti shortcut mechanism.

NOTE:

These debugging mechanisms can drain your battery quickly or cause the loss of all your data. Use them judiciously.

The Graffiti shortcuts are accessed by writing the Graffiti shortcut character (a cursive lowercase L) followed by two taps (the two taps generate a dot, or period), followed by a specific character or number. It's common to open the Find dialog before writing them. (Find has a text field that's available in all applications, and it's nice to have the feedback of seeing the characters as you write them.) Here is a complete list of these shortcuts:

Enters debugger mode. The device opens the serial port and listens for a low-level debugger to connect to it (for example, the unsupported Palm Debugger application). Do a soft reset to exit this mode.

Enters console mode. The device opens the serial port and listens for a high-level debugger like CodeWarrior to connect to it. Do a soft reset to exit this mode.

Turns off the power auto-off feature. The device does not power off after idle time (although the power key still works). Do a soft reset to exit this mode.

Displays the user's name and random number.

Erases the user's name and random number. On the next HotSync, this device appears to be a never-before-synced device. Syncing to an existing user recognizes all the records on the device as new; thus, they are all duplicated for the existing user on the desktop and handheld.

Displays the ROM build date and time.

Switches battery profiles from alkaline to NiCad (theoretically to adjust when the battery warning alerts appear). We didn't find this to be very effective.

Toggles loopback mode on and off for the Exchange Manager. This allows loopback mode testing even for applications that haven't set the localMode field to true in the ExgSocketType structure (like the built-in applications, for instance). See "Send an entry" on page 239 for information on initializing the ExgSocketType structure.

Toggles between serial and IR modes on the device. In serial mode, information that would normally be sent via infrared is sent via the serial port. This works on a Palm III device with a built-in IR port, but may or may not work on an OS 3.0 upgraded unit that has an IR port on the memory card.

Source-Level Debugging with CodeWarrior

Top Of Page

CodeWarrior can do source-level debugging either with a handheld (attached via a serial cable) or with POSE.

In either case, you've got to enable source-level debugging with Enable Debugger from the Project menu. (This is a toggle menu item, so if it says Disable Debugger, debugging is on.)

Choosing a Target

You need to tell CodeWarrior whether you are using POSE or the handheld; then it needs to acquire its target.

Using POSE

To use POSE, select Palm OS Emulator from the Target pop-up menu in the Preferences dialog box (see Figure 10-3).

Figure 10- 3. Selecting options for debugging using POSE

In order to debug, POSE has to be running. When you choose Debug from the Project menu, CodeWarrior automatically downloads the PRC file to the Emulator and stops at the first line of the program.

Using a handheld

To use the handheld, specify the target as Palm OS Device in the Preferences dialog box (see Figure 10-4). When you choose Debug from the Project menu, CodeWarrior prompts you to enter console mode (see Figure 10-5). At that point, use shortcut .2 on the handheld, and click OK in the CodeWarrior dialog box. CodeWarrior then automatically downloads the PRC file to the device and stops at the first line of the program.

Figure 10- 4. Specifying the device as the target in the Preferences dialog box

Figure 10- 5. CodeWarrior prompting to enter console mode

Debugging Commands

Figure 10-6 shows CodeWarrior source-level debugging in action. With it you can do all of the following.

Figure 10- 6. Debugging in CodeWarrior

Console Window

While you are debugging, a console window is available to you. From the Palm OS menu, choose the Open Debug Console menu item to open this window. In this command-line-oriented window, you can issue commands to the device (or emulator) in order to obtain information about memory and databases and to export databases.

NOTE:

To execute lines in the console window, use the Enter key on the numeric keypad, not the Enter key on the main keyboard area.

Common debugger commands are:

help

Displays a list of all commands.

help command

Displays help for the specified command.

dir 0

Lists all the databases on card 0. This is useful to see whether your application's database or databases exist.

ht heapNumber

Displays a summary of the given heap. A heap number of 0 specifies the dynamic heap. Here's example output (note that it shows the amount of free space available):

Displaying Heap ID: 0000, mapped to 00001480

-----------------------------------------------------------------------

Heap Summary: 

  flags:              8000

  size:               016B80

  numHandles:         #40  

  Free Chunks:        #7      (01204A bytes)

  Movable Chunks:     #4      (0004E4 bytes)

  Non-Movable Chunks: #39     (0045A2 bytes)

hd heapNumber

Displays not just a summary, but all the chunks in a heap. A heap number of 0 specifies the dynamic heap. This allows you to see which chunks are where, which are locked, which are unlocked, etc. It's not necessary to see the heap in such detail very often, however.

Source-Level Debugging with GNU PalmPilot SDK

Top Of Page

The GNU tools can't debug an application running on the handheld. You only have POSE available to you. To debug:

1. Compile and link your application with the -g flag.

2. Run POSE and load your application.

3. Run an intermediary application called gdbplug, which communicates via TCP/IP to GDB. It communicates with POSE using a POSE debugging protocol. See http://www.tiac.net/users/thomas/pilot-gdbplug.html for documentation and the latest version.

In a separate DOS window, run:

gdbplug -port 2000 -enable

4. Run GDB. Pass as a command-line argument your linked file, not the PRC (if your application is foo, pass foo as the parameter, not foo.prc):

m68k-palmos-coff-gdb your_linked_app

5. Within GDB, specify the PalmPilot as a target by executing:

target pilot localhost:2000

6. Within POSE, start your application. GDB stops at the first line.

Here are the most important commands that GDB supports:

print expressionToPrint1, ..., expressionToPrintN

Use the print command to look at the values of variables. Here's an example:

print *myStructPtr, theString[5], myOtherStruct.x

backtrace

Prints a stack crawl, showing each function in the stack, including parameter names and values.

step

Single-steps, stepping into functions.

next

Single-steps, stepping over functions.

cont

Continues running the program until it reaches a breakpoint, causes an error, or exits.

break funcNameOrLineNumber

Sets a breakpoint. You can break at a function:

break MyFunction

Or you can set a breakpoint at a specific line number in a file:

break MyFile.c:16

quit

Quits the program. If the program is still running, you are prompted for GDB to automatically quit it (by resetting POSE).

help

There are, of course, many other functions. Use help to find out more about them all.

GDB is a text-oriented debugger, where commands and responses to commands are interleaved (Figure 10-7 shows an example of GDB running). GNU PalmPilot SDK comes with the Emacs text editor. Emacs can be used as an Integrated Development Environment (IDE) that can control the debugging process. As you debug, Emacs makes sure that the source file with the current line is always displayed, and it provides some menu commands that can be used instead of typing into GDB.

Figure 10- 7. GDB debugging an application

Using Simulator on Mac OS

Top Of Page

CodeWarrior running on the Mac OS has a feature not found on the Windows version: the Simulator. The Simulator consists of some libraries that contain a subset of the Palm OS. These libraries are hosted so that they run on the Mac OS. When you create a Simulator version of your application, you actually build a Mac OS application that simulates a Palm OS application. It does not simulate the entire Palm OS, only your application-no other applications are present. Figure 10-8 shows a Simulator application running.

Figure 10- 8. Datebook running as a Simulator application on Mac OS

Before POSE was available, the Simulator was an almost indispensable tool. Like POSE, the Simulator doesn't require a Palm device to be connected. It also allows debugging applications that use serial communications (tough to do if you're debugging with the device itself and have the one-and-only serial port connected to the debugger).

Now that POSE is available, however, the Simulator is much less useful. In fact, we don't use it anymore. We can think of only one advantage that the Simulator has compared to POSE-it is faster. On a reasonably fast Mac OS machine, POSE is quick enough, so the speed isn't much of an issue.

Gremlins

Top Of Page

There are two approaches to testing software, which can often be used in a complementary fashion:

Gremlins does the second sort of testing. Imagine, if you will, a very inquisitive monkey given a Palm OS device with your application on it. The monkey grabs the stylus and starts tapping away. Let's look at some characteristics of the monkey:

You start a Gremlin from the Gremlins dialog of POSE by selecting New from the Gremlins menu. In this dialog you specify which Gremlin you want to use and on what application (see Figure 10-9). You get to choose from 1,000 of them, each of which acts slightly differently in terms of the events it generates. Looking at Figure 10-9, you see that we've specified our own Sales application to test. You can also specify the entire device to check for problems between applications.

Figure 10- 9. Gremlins dialog box, where you select the application and Gremlin number

Gremlins goes to work by generating events and causing your application to respond to them. It generates pen-downs (mostly on active areas), inputs keys, and does everything a user could do. You'll find, however, that it will end up exercising parts of your program you'd never tested: fields with more characters than you'd anticipated or more records than you'd planned for (to the extent that the entire data heap will probably be filled).

Here are the various things you can specify for Gremlins:

Figure 10-10 shows the dialog that occurs after the Gremlins run is over. You have to request it, however, in the initial-run dialog box by selecting "Display elapsed time."

Figure 10- 10. Dialog shown after a Gremlins run with "Displays elapsed time" chosen

If you encounter an error while running Gremlins (as is often the case), the dialog box shown in Figure 10-11 tells you about the problem.

Figure 10- 11. Gremlin error dialog failing on the 971st event; clicking the Debug button drops you into the debugger

If you choose to "Log posted/retrieved events" (see Figure 10-9), POSE creates a file (named Event Log #n.txt, where n increments on every run). This is useful if you want to know how far Gremlins got before an error occurred, or to find out events that happened before the error. Here's an example output:

Gremlin #2 started, 10 steps.
>>> EvtEnqueueKey: ascii = 0x007C, keycode = 0x0000, modifiers = 0x0000.
<<< 0: keyDownEvent    Key:'|' 0x7c,  Modifiers: 0x0000
>>> EvtEnqueuePenPoint: pen->x=99, pen->y=150.
<<< 1: penDownEvent    X:97   Y:136
<<< 1: ctlEnterEvent   ID: 10307
>>> EvtEnqueuePenPoint: pen->x=91, pen->y=157.
<<< 2: EvtGetPen: screenX=89, screenY=143, penDown=1.
>>> EvtEnqueuePenPoint: pen->x=156, pen->y=87.
<<< 3: EvtGetPen: screenX=154, screenY=73, penDown=1.
>>> EvtEnqueuePenPoint: pen->x=-1, pen->y=-1.
<<< 4: EvtGetPen: screenX=154, screenY=73, penDown=0.
<<< 4: ctlExitEvent
<<< 4: penUpEvent      X:154   Y:73
>>> KeyHandleInterrupt: periodic=0, status=0x00000020.
>>> EvtEnqueueKey: ascii = 0x0069, keycode = 0x0000, modifiers = 0x0000.
<<< 6: keyDownEvent    Key:'i' 0x69,  Modifiers: 0x0000
>>> EvtEnqueueKey: ascii = 0x0079, keycode = 0x0000, modifiers = 0x0000.
<<< 7: keyDownEvent    Key:'y' 0x79,  Modifiers: 0x0000
>>> EvtEnqueueKey: ascii = 0x0044, keycode = 0x0000, modifiers = 0x0000.
<<< 8: keyDownEvent    Key:'D' 0x44,  Modifiers: 0x0000
>>> EvtEnqueueKey: ascii = 0x0065, keycode = 0x0000, modifiers = 0x0000.
<<< 9: keyDownEvent    Key:'e' 0x65,  Modifiers: 0x0000
>>> EvtEnqueueKey: ascii = 0x0020, keycode = 0x0000, modifiers = 0x0000.
<<< 10: keyDownEvent    Key:' ' 0x20,  Modifiers: 0x0000
Gremlin #2 stopped at 11 of 10 after 378 msecs.

If you request "Log system calls," the system calls that are executed are output. Here's a portion of output where logging was requested (some of the lines were removed for brevity):

Gremlin #2 started, 5 steps.
--- System Call 0xA2C9: SysEvGroupSignal.
--- System Call 0xA08D: SysDoze.
--- System Call 0xA23F: HwrDoze.
...
--- System Call 0xA2C9: SysEvGroupSignal.
--- System Call 0xA0A5: SysDisableInts.
--- System Call 0xA0A6: SysRestoreStatus.
--- System Call 0xA12E: EvtDequeueKeyEvent.
--- System Call 0xA23A: AlmDisplayAlarm.
--- System Call 0xA2CB: SysEvGroupWait.
>>> EvtEnqueueKey: ascii = 0x007C, keycode = 0x0000, modifiers = 0x0000.
--- System Call 0xA2C9: SysEvGroupSignal.
--- System Call 0xA0A5: SysDisableInts.
--- System Call 0xA0A6: SysRestoreStatus.
--- System Call 0xA12E: EvtDequeueKeyEvent.
--- System Call 0xA272: PenRawToScreen.
--- System Call 0xA20D: WinDisplayToWindowPt.

Logs are verbose; it's not uncommon to have an average of 10K per event when both types of logging are enabled. Rather than growing the log without bounds, POSE reuses log files that reach 128K. Thus, only the last 128K of log information is saved. A nice refinement, we think.

Gremlins and Gadgets

Gremlins generate pen taps almost exclusively on active areas of the screen: buttons, fields, lists, places where there are form objects. This also means that areas of the screen where there are no form objects are almost completely ignored. You can take advantage of this behavior in your application if you wish. A good testing technique can be to place an empty gadget in an area of your screen where tapping does something; this way, you will grab the Gremlin's attention, and it will do some tapping there.

Using Gremlins Repeatedly

One very nice thing about Gremlins is that even though each Gremlin (of the 1,000 different ones) has its own sequence of events that it generates, a specific Gremlin always generates the same sequence of events. Thus, if you run Gremlin 5 and find that, on a fresh POSE with your application just loaded on it, your application crashes after event 3006, every time you run that Gremlin with the same starting configuration, the crash happens. We're sure you'll appreciate the ability to reproduce the bug easily.

Other Advice

Make sure you start your Gremlins run in a known configuration. Have a known amount of memory available (best done by starting with a fresh RAM file with POSE by deleting the old RAM file). Let Gremlins launch your application (so that from the beginning your application is receiving events from Gremlins).

Test your application with Gremlins set to generate one million events (start it before you go home at night). That's enough to catch almost anything. You certainly don't need to try each of the 1,000 different Gremlins. Start with just numbers 0 and 1.

Run Gremlins while your source-level debugger is active. That way, if and when your application crashes, you can drop into the debugger and see what's going on. If you can't tell what's going on at the error, Gremlins does provide the ability to step event by event. The log shows the event number at which the error occurs. You can run Gremlins until 5 or 10 events before the error. Then you can step event by event until the error occurs. This may give you a better context to figure out what's going on.


Palm Programming: The Developer's Guide
Copyright © 1999, O'Rielly and Associates, Inc.
Published on the web by permission of O'Rielly and Associates, Inc. Contents modified for web display.

Previous PageTop Of PageTable Of ContentsIndexNext Page