< Home < Developers < Development Support < Documentation

## 12 Debugging Strategies

Table of Contents | < Previous | Next > | Index

| Title - Palm OS® Programmer's Companion vol. I 12 Debugging Strategies Displaying Development Errors Using the Error Manager Macros The Try-and-Catch Mechanism Using the Try and Catch Mechanism Summary of Debugging API |  |  |  | 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: Displaying Development Errors Using the Error Manager Macros The Try-and-Catch Mechanism Summary of Debugging API 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. ErrDisplay always displays the error message on the screen. ErrFatalDisplayIf and ErrNonFatalDisplayIf display the error message only if their first argument is TRUE. 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. ## 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 12.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, Put ErrTry/ErrCatch blocks in CreateTable Clean up in the ErrCatch block first Call ErrThrow to jump to the top-level ErrCatch ## Summary of Debugging API #####
Error Manager Functions |
--- | ---
Displaying Errors |
ErrAlert ErrDisplayFileLineMsg ErrNonFatalDisplayIf | ErrDisplay ErrFatalDisplayIf
Catching Exceptions |
ErrCatch ErrExceptionList ErrTry | ErrEndCatch ErrThrow