March 94 - DEBUGGING ON POWERPC
DEBUGGING ON POWERPC
DAVE FALKENBURG AND BRIAN TOPPING
Debugging on a PowerPC processor-based Macintosh is just like debugging on any
other Macintosh, only different. You should bring along the debugging skills you
carefully honed on 680x0-based machines but expect the mechanics of debugging to be
easier thanks to the PowerPC two-machine debugger. We give you basic instructions
and provide a sample program that you can crash like crazy while you learn to debug
PowerPC code.
The most important thing to realize when you set out to develop (and hence debug) for
the PowerPC processor-based Macintosh is that this beast is still a Macintosh. Besides
having a 680x0 emulator, the CPU has a Macintosh Toolbox in ROM, low-memory
globals, a trap dispatcher, and 680x0 interrupt vectors. Since it retains so many
elements you know and love, you don't have to throw away any of what you've learned
about debugging with MacsBug, TMON, or any other debugger.
On the other hand, if all the new Macintosh had up its sleeve were 680x0 emulation,
we wouldn't be writing this article. As described in "Making the Leap to PowerPC
indevelop Issue 16 and in the imminentInside Macintosh: PowerPC System Software ,
the PowerPC runtime architecture is new and improved. A couple of new managers --
the Code Fragment Manager and the Mixed Mode Manager -- help bridge the software
gap between the 680x0 emulator and the PowerPC 601 microprocessor, and introduce
some new twists and turns in how code is loaded and executed.
This article introduces you to the two-machine debugger developed for debugging
PowerPC code. It then lays down some debugging ground rules, describes the
circumstances in which your program might end up in the debugger, and discusses
extensions and dcmds old and new to assist you in debugging. Finally, it talks about how
to debug even in the absence of the debugger nub.
On this issue's CD you'll find CrashOMatic, a sample program you can use to explore
the debugger without risking your own code. CrashOMatic is designed to cause crashes
or demonstrate unusual aspects of PowerPC debugging. When launching CrashOMatic,
hold down the Control key to force the debugger to take control.
To experiment with debugging CrashOMatic and to develop and debug native PowerPC
applications, you'll need the Macintosh on RISC Software Developer's Kit (soon to be
available from APDA) or one of the other PowerPC development kits available from
third parties. The Macintosh on RISC Software Developer's Kit contains R2Db, an
MPW-based cross-compiler called PPCC, and other assorted tools used for building
PowerPC applications.
INTRODUCING R2DB
Apple's new debugger for the PowerPC processor-based Macintosh is called R2Db, for
"RISC two- machine debugger." (As this issue goes to press, the fate of this name is
undecided, so it may be different by the time you read this.) This modernized cross
between ReAnimator and SourceBug allows for single stepping, setting breakpoints,
and disassembling PowerPC code fragments. Like MacsBug, it's a systemwide low-level
debugger; unlike MacsBug, R2Db also enables source-level debugging and is designed
for debugging PowerPC applications. But as we hinted at earlier, you probably don't
want to throw away your MacsBug skills just yet. R2Db can be used in conjunction
with MacsBug, as explained in the section "Working With dcmds and MacsBug.
THE TWO-MACHINE SCHEME
R2Db is a two-machine debugger, as illustrated in Figure 1. The R2Db application
runs on thehost machine , which can be any Macintosh at all, preferably one with a
large screen and enough CPU power to run a debugger built with MacApp 3.0. The part
of R2Db called the PPC Debugger Nub runs on thetarget machine , the PowerPC
processor-based Macintosh running the program you want to debug. The host machine
acts as a remote control panel for the target. The machines need to be connected by a
standard 8-pin printer cable.
Using two machines to debug code has several advantages. For one, a bug in your
application that locks up the keyboard can't bring your debugging to a halt. For
another, you can debug interrupt- level code without having to have incredible luck.
(Can you say "MacsBug caused the exception"?) Throughout the development of
Macintosh with PowerPC, the system software team relied on R2Db to debug such
nasty (but necessary) things as the Memory and Resource Managers. Running a
single-machine debugger on such shaky ground can lead to premature aging and the
loss of some motor functions.
On the other hand, two-machine debugging has the disadvantage of requiring two
Macintosh systems. (Oh darn, I guess it's time to ask the boss for another Macintosh
Quadra.) Don't worry, though -- Apple (and others) are busy working on
single-machine debugging environments for those folks who develop on smaller
budgets.
Figure 1 The Two-Machine Debugging Scheme
R2DB BASICS
When you first launch R2Db, it presents a Standard File dialog box from which you
choose an xSYM file to use when debugging. If you've used SourceBug, you know that a
SYM file bundles together information about the application's source and object code
and enables the debugger to associate a range of machine-language instructions with a
line of C source code. The xSYM file is an extended version of the MPW SYM file that
supports both 680x0 and PowerPC code. To support debugging "fat" applications --
those with both 680x0 and PowerPC versions packaged together -- twodifferent kinds
of SYM files are needed: the SYM file for the 680x0 version and the xSYM file for the
PowerPC version.
When a PowerPC application is being debugged at the source level, its xSYM file and all
its source code must be available on the machine running R2Db. Without the xSYM
file, your application can still be debugged, but not at the source level.
When you choose an xSYM file, the R2Db browser window appears. This window,
which will be familiar to users of Smalltalk or MacApp's Mouser, enables you to
examine source code by file and function. Choosing Go To Debugger from the Debug
menu makes the browser window look like the one shown in Figure 2. In the top left
corner, a list of source files is presented. When a source file is chosen, the functions
belonging to that file are listed in the top right corner. (Sorry, C++ fans -- there's
no object browsing in this release of R2Db.) The status of the target machine, along
with a few stepping controls, appears in a control palette.
A small arrow points to the current instruction or line of code being executed.
Breakpoints can be set by clicking to the left of the source display; a small hexagonal
"stop sign" marks any breakpoints you've set. Double-clicking a breakpoint enables
you to choose from a myriad of useful variants on the traditional behavior. Finally,
you can switch between assembly and source views by using the pop-up menu at the
lower left of the display. To see exactly how the compiler translates your C code into
native PowerPC code, select a line of source code and then switch to the assembly view,
where it's highlighted.
Figure 2The R2Db Browser During CrashOMatic Debugging
Lurking under the R2Db menu bar are some useful commands, including commands to
get register displays, memory dumps, and even 680x0 disassembly. If you ever find
yourself stopped outside ofyour application, choose Show Instructions from the Views
menu to get a disassembly at the current point of execution. This can also be helpful
when debugging without an xSYM file.
R2DB IDIOSYNCRASIES
Like all things in life, R2Db has a couple of idiosyncrasies you should be aware of.
Debugger versus SysBreak. If you're familiar with SADE and SourceBug, you're
probably accustomed to using SysBreak and SysBreakStr to add high-level breakpoints
to your code. These functions aren't supported by R2Db, so you should use the familiar
Debugger and DebugStr calls that you would normally use with MacsBug or other
low-level debuggers. If a PowerPC debugger isn't installed, these calls are routed to
MacsBug for handling. If you prefer using MacsBug to inspect data structures or log
debugging messages, you can use the functions Debugger68k and DebugStr68k.
Memory Manager access faults. Starting with the Macintosh IIci, Apple added a
hack to the existing Memory Manager to correctly support changes for 32-bit
addressing and NuBusTMexpansion cards. The change involved adding bus error
wrappers within several internal routines to automatically call StripAddress and
retry when a 24-bit handle is passed to the Memory Manager while the machine is
temporarily operating in 32-bit mode. These bus error wrappers don't exist on
68000 machines like the Macintosh Plus, SE, and Classic (a fine reason to test your
software on all sorts of machines).
These handlers also mask a serious problem: fake handles, fake pointers, and fake heap
zones being passed to the Memory Manager. For Macintosh with PowerPC, the Memory
Manager has been completely rewritten and actually preserves this tolerant behavior
-- but with a twist. When any PowerPC debugger is installed, these Memory Manager
exceptions get routed through the debugger to point out the problem to the developer.
We would have added a "feature" to CrashOMatic so that you could see this behavior in
action, but fortunately it doesn't happen that often. Before examining the fields of a
handle or pointer block, the Memory Manager checks a magic cookie in the block as a
first guard against fake handles. If we were to contrive an example without setting that
magic cookie, the HLock call would still return an error code as it should; we just
wouldn't see the bus error handlers get hit.
There are, however, some applications in which you will see the bus error handlers
get hit. Open a file in ResEdit 2.1.1, for example, and you'll see access faults in R2Db.
Choose Propagate Exception from R2Db's Control menu and the Memory Manager will
clean up after ResEdit.
In future versions of Macintosh system software, this compatibility hack will be
removed. Consider yourself warned.
GROUND RULES FOR DEBUGGING ON POWERPC
Before you start debugging your PowerPC application, you should commit the following
ground rules to memory. This will ensure that you get off on the right foot.
Rule 1: Always use a nonoptimized build for source-level debugging.RISC
C compilers radically reorder the sequence of instructions when generating optimized
code. This makes straightforward source-level debugging impossible -- imagine
single stepping to the next line of code and having the arrow move to a statement three
lines before where you just were. Following is a little program to demonstrate why
you don't want to do source-level debugging with an optimized build of your code unless
you really know what you're doing. Ignore the fact that "dude" is never initialized
before being used.
void main(void)
{
long counter, dude;
float fooVal = 1.0;
for (counter = 1; counter < 1000; counter++)
{
fooVal = counter * fooVal;
dude++;
}
}
Below is the nonoptimized compiler output. (Incidentally, we compiled this program
using the IBM compiler; if you compile it with PPCC you'll see different results.)
Notice that the basic top-to- bottom structure of the C source is preserved.