Previous Page Table of Contents Index Next Page

Palm OS Programmer's Companion


Palm Logo 14 Debugging Strategies

You can use a Palm OS® system manager called the error manager to display unexpected runtime errors such as those that typically show up during program development. Final versions of applications or system software won't use the error manager.

The error manager API consists of a set of functions for displaying an alert with an error message, file name, and the line number where the error occurred. If a debugger is connected, it is entered when the error occurs.

The error manager also provides a "try and catch" mechanism that applications can use for handling such runtime errors as out of memory conditions, user input errors, etc.

This section helps you understand and use the error manager, discussing the following topics:

This chapter only describes programmatic debugging strategies; to learn how to use the available tools to debug your application, see the book Palm OS Programming Development Tools Guide.

Displaying Development Errors

The error manager provides some compiler macros that can be used in source code. These macros display a fatal alert dialog on the screen and provide buttons to reset the device or enter the debugger after the error is displayed. There are three macros: ErrDisplay, ErrFatalDisplayIf, and ErrNonFatalDisplayIf.

The error manager uses the compiler define ERROR_CHECK_LEVEL to control the level of error messages displayed. You can set the value of the compiler define to control which level of error checking and display is compiled into the application. Three levels of error checking are supported: none, partial, and full.

If you set ERR_CHECK_LEVEL to... The compiler...
ERROR_CHECK_NONE (0) Doesn't compile in any error calls.
ERROR_CHECK_PARTIAL (1) Compiles in only ErrDisplay and ErrFatalDisplayIf calls.
ERROR_CHECK_FULL (2) Compiles in all three calls.

During development, it makes sense to set full error checking for early development, partial error checking during alpha and beta test periods, and no error checking for the final product. At partial error checking, only fatal errors are displayed; error conditions that are only possible are ignored under the assumption that the application developer is already aware of the condition and designed the software to operate that way.

Using the Error Manager Macros

Calls to the error manager to display errors are actually compiler macros that are conditionally compiled into your program. Most of the calls take a boolean parameter, which should be set to true to display the error, and a pointer to a text message to display if the condition is true.

Typically, the boolean parameter is an in-line expression that evaluates to true if there is an error condition. As a result, both the expression that evaluates the error condition and the message text are left out of the compiled code when error checking is turned off. You can call ErrFatalDisplayIf, or ErrDisplay, but using ErrFatalDisplayIf makes your source code look neater.

For example, assume your source code looks like this:

result = DoSomething();
ErrFatalDisplayIf (result < 0,
"unexpected result from DoSomething");

With error checking turned on, this code displays an error alert dialog if the result from DoSomething() is less than 0. Besides the error message itself, this alert also shows the file name and line number of the source code that called the error manager. With error checking turned off, both the expression evaluation err < 0 and the error message text are left out of the compiled code.

The same net result can be achieved by the following code:

result = DoSomething();
#if ERROR_CHECK_LEVEL != ERROR_CHECK_NONE
if (result < 0)
    ErrDisplay ("unexpected result from DoSomething");
#endif

However, this solution is longer and requires more work than simply calling ErrFatalDisplayIf. It also makes the source code harder to follow.

Understanding the Try-and-Catch Mechanism

The error manager is aware of the machine state of the Palm OS device and can therefore correctly save and restore this state. The built-in try and catch of the compiler can't be used because it's machine dependent.

Try and catch is basically a neater way of implementing a goto if an error occurs. A typical way of handling errors in the middle of a routine is to go to the end of the routine as soon as an error occurs and have some general-purpose cleanup code at the end of every routine. Errors in nested routines are even trickier because the result code from every subroutine call must be checked before continuing.

When you set up a try/catch, you are providing the compiler with a place to jump to when an error occurs. You can go to that error handling routine at any time by calling ErrThrow. When the compiler sees the ErrThrow call, it performs a goto to your error handling code. The greatest advantage to calling ErrThrow, however, is for handling errors in nested subroutine calls.

Even if ErrThrow is called from a nested subroutine, execution immediately goes to the same error handling code in the higher-level call. The compiler and runtime environment automatically strip off the stack frames that were pushed onto the stack during the nesting process and go to the error handling section of the higher-level call. You no longer have to check for result codes after calling every subroutine; this greatly simplifies your source code and reduces its size.

Using the Try and Catch Mechanism

The following example illustrates the possible layout for a typical routine using the error manager's try and catch mechanism.

Listing 14.1 Try and Catch Mechanism Example


    ErrTry {
p = MemPtrNew(1000);
    if (!p) ErrThrow(errNoMemory);
    MemSet(p, 1000, 0);
    CreateTable(p);
    PrintTable(p);
}

ErrCatch(err) {
// Recover or clean up after a failure in the
// above Try block."err" is an int
// identifying the reason for the failure.

// You may call ErrThrow() if you want to
// jump out to the next Catch block.

// The code in this Catch block doesn't
// execute if the above Try block completes
    // without a Throw.

    if (err == errNoMemory)
    ErrDisplay("Out of Memory");
    else
    ErrDisplay("Some other error");
} ErrEndCatch
    // You must structure your code exactly as
// above. You can't have an ErrTry without an
    //ErrCatch { } ErrEndCatch, or vice versa.


Any call to ErrThrow within the ErrTry block results in control passing immediately to the ErrCatch block. Even if the subroutine CreateTable called ErrThrow, control would pass directly to the ErrCatch block. If the ErrTry block completes without calling ErrThrow, the ErrCatch block is not executed.

You can nest multiple ErrTry blocks. For example, if you wanted to perform some cleanup at the end of CreateTable in case of error,

Summary of Debugging API

Error Manager Functions
ErrDisplay ErrDisplayFileLineMsg
ErrFatalDisplayIf ErrNonFatalDisplayIf
ErrThrow ErrAlert



Palm OS Programmer's Companion

  Previous Page Table of Contents Index Next Page  

This is page 83 of 85 in this book

Palm Computing Platform Development Zone
Copyright © 2000, Palm, Inc. All rights reserved.