A grab bag of tips and tricks for debugging Macintosh software with the MacsBug assembly code debugger.
Michael D. Crawford
GoingWare Inc.
crawford@goingware.com
All of us who program on the Macintosh have lots of little tricks that we use to get our job done. Most of these are passed around via e-mail and the Usenet News, if they are passed on at all. In an effort to collect all these in one place for the benefit of all, I've created this page. Please submit your tips to crawford@goingware.com and I will add them to this page - with proper attribution of course.
Debugging Software on the Macintosh
by Terry Teague
[Top]
MacsBug is available via anonymous FTP from Apple Computer from here.
It is included with the two books below, but you will want to get the latest version, especially if you are using a PowerPC.
[Top]
Yes, among them are:
While you don't need to know how to write assembly code to use MacsBug effectively, you will need to know how to read and understand it. Thus you will also need to get assembly code reference manuals, such as:
All of these books may be ordered from the Computer Literacy Bookstore.
[Top]
Contributed by Bill Coderre, bc@wetware.com
Mike sez - also see the MacsBug 6.5.2 HTML Help
[Top]
Most applications call WaitNextEvent. While an application is executing, a Pascal string with the name of the application is placed at location 0x910. Thus the first four characters of the name itself begin at location 0x911. Suppose you want to break while SimpleText is active. Enter MacsBug and give the command:
then continue. You will shortly drop into MacsBug at SimpleText's WaitNextEvent call. This is particularly useful when debugging faceless background applications. If the application does not call WaitNextEvent, try GetNextEvent instead.
[Top]
Programmers should instruct their users and testers about the "stdlog" command in MacsBug.
Stdlog opens a file named "stdlog" on the Desktop and records some valuable information in it.
When you crash, type stdlog and then restart. Find the file named stdlog on the Desktop and mail it to the programmer or technical support when you report a bug in a program.
[Top]
Contributed by Jim Luther, JumpLong@aol.com
My favorite MacsBug command line this week is:
It replaces the _Debugger instructions I've sprinkled through my code with NOPs after I've stepped through the code a few times to make sure it works.
[Top]
Contributed by Darren Giles, Terran Interactive, mars@netcom.com
Always on the lookout for useful debugging tools & tips, I'd love to share ideas with others on the topic. I'll start out by offering a snippet I've found very useful -- hopefully others will do the same.
One thing that's really bugged me about MacsBug on PPC is that the stack crawl has become a much less useful tool. The snippet below gives you the ability to keep track of a list of the last significant points your program has visited. It's a list, not a stack, so you can also see patterns of execution.
Hardly rocket science, but useful & easy to add. Just call DEBUG_STUFF_INIT at startup, then insert a DEBUG_ENTRY wherever you want. To see what's up, especially during a bad hang, just dm [the address that DEBUG_STUFF_INIT dumped out at startup.]
The conditional compilation means that if you turn of debugging in your final build, the release version of the program won't have any of this code in it.
[Top]
Contributed by Dave Stone, dstone@chem.utoronto.ca
I've also used conditional compilation to debug serial communications stuff being processed at interrupt time - something like
etc. Handy, because you can let it rip for a while to see if there is a consistent pattern in the errors in ch - in my case, a stream of Midi data through a very basic freeware Midi Driver.
[Top]
Contributed by Tom Kimpton, Jostens Learning Corporation, tom@jlc.com
One technique that I have used in the past where dropping into the debugger wasn't an option, and logging wasn't getting flushed in time/took too long, was to create a bunch of cursors numbering 00 - 99, and made a call to set the cursor and return the number of the previous cursor:
This way when the machine froze, the cursor would tell me what routine it had frozen in.
[Top]
I had a bug in which the Mac would occasionally freeze during shutdown without the ability to get into MacsBug. It would only occur about once in twenty reboots.
The way I dealt with this was to borrow a display card and hook two monitors up to the Macintosh. You can use the Monitors control panel to select which monitor will be used for MacsBug (hold the option key and drag the happy Mac around).
I wrote a small application that just called ShutDownRestart(), and placed it in the Startup Items folder. Thus, when the Mac came up into the Finder it would immediately reboot. About every twenty minutes it would freeze.
If you define a macro named FirstTime in the Debugger Prefs file, MacsBug will execute it when it loads. I used a macro that was something like:
or some such. The swap command caused MacsBug to be permanently left on the second screen. That way when the crash occurred you could still see the last few things MacsBug did. The ";g" following the a-trap break commands tells MacsBug to continue after the break - this is a "Touch and Go" breakpoint.
One thing you can also do inside a touch and go breakpoint is set new breakpoints. I would take guesses on what traps might be called in the neighborhood of the crash, and have breakpoints set on them when ShutDownRestart was called.
Then I could leave the Mac rebooting on its own in the lab, and pop in every half an hour to check the log, adjust the breakpoints and start it up again.
The actual bug took about five months to find and fix.
[Top]
Contributed by Dave Fleck, Wacom Technology Corp., dfleck@wacom.com
Here's my debugging tip.
I do drivers, and you just plain can't set a breakpoint in ADB completion routines (freezes the keyboard so MacsBug is worthless!).
So I throw one of the routines below into the routine to see when a piece of code gets executed.
What does it do? It "lights up" a bar (length dependant on screen resolution) in the menu bar. So if you DotToggle(300); you get a flashing short line in the menu bar.
[Top]
Contributed by Harold Ekstrom, the ag group, inc., ekstrom@aggroup.com.
Don't you just hate it when beta testers say "it crashes" but don't give you any more information? First, tell them to use the "stdlog" command in MacsBug, then force them to install MacsBug by checking for it during your program's initialization:
[Top]
Contributed by Malcolm Teas, Blaze Technology, mhteas@btech.com
As the instructor and developer of Apple's Developer University class called "Macintosh Debugging Tips and Techniques" I would like to make sure your tips page references this class.
This class is centered around MacsBug as the easiest to learn low-level debugger. It also covers a multitude of low-level topics like memory maps, subroutine calling protocols, code segments and code fragments, reading (and understanding) assembler for 68K and PPC, and many more areas. One key area is how to avoid bugs in the first place. All the information you need to be able to debug software at the low-level.
The class is available from Apple's Developer University.
Another thing I want to mention is the version number of the most current MacsBug is 6.5.3 (as of this writing). This version includes the PPC commands and features.
[I have taken this class and recommend it highly - Mike]
The above was written years ago. I'm afraid Developer University doesn't exist anymore. I'm not sure whether Apple offers anything really like it now. I took several DU classes, and both learned a lot of valuable skills and enjoyed myself. -- Mike
[Top]
Contributed by Moi GoingWare, Inc.
If you compile your program in 68k (even if you are running on a PowerPC) you can use a-trap recording to see the last few trap calls your program made before a crash. This is immensely helpful if your program runs for a while and then crashes.
To turn on a-trap recording, break into the debugger and type "atr". Then cause the crash, and type "atp" (for "a-trap playback") to see the last few trap calls.
Be sure to compile with MacsBug symbols and A6 stack frames turned on.
A-trap recording might not be useful, though, if your program runs for a long ways without calling any traps. It might be engaged in a lengthy calculation, or accessing only library routines - a good reason for using NewPtr instead of malloc.
You can add markers to your code by calling a-traps that have only benign side effects. For example, I like to call InitCursor.
I was debugging some crashy code the other day that processed each word of a text file in a long loop and then crashed. To narrow down where in the loop it crashed I added a bunch of calls to InitCursor at different points in the code. After causing the crash and playing back the a-traps I was able to narrow it down further, which I did by adding more InitCursor calls. Since the a-trap recorder records the function name and offset where the trap call was made, you can see where you have been in the code. You can tell which trap is which by disassembling the function and counting the trap calls from the beginning or end.
A-trap recording works on the 68k because 68k system calls are "exceptions". That is, they are a type of illegal instruction that causes the processor to break from the linear flow of program code and branch to an exception handler, saving the registers and program counter on the stack. The Mac OS programmers cleverly installed an exception handler that figures out which particular illegal instruction code you called and branches to a subroutine that does the desired function and returns.
When you turn on a-trap recording, MacsBug installs its own "a-line" exception handler (so called because it handles illegal instructions that start with hex A) that records the trap word on a circular buffer, along with the program counter, and then branches to the original exception handler. When you play back the traps, MacsBug looks up the symbol name and offset by searching in your program's executable code.
A-trap recording does not work in PowerPC native code (including native code called from a 68k program) because system calls made from PowerPC code is made via a normal subroutine call to a shared library. When your program is loaded into memory the Code Fragment Manager places the addresses of each of the system functions into the transition vectors in your native code. That's why, as much as possible, you should always maintain both native PowerPC and 68k versions of your code. (There is a MacsBug function called "atvb" that does system call breaks from native code but it's not nearly so nice to use as the 68k "atb".)
[Top]
Contributed by Moi GoingWare, Inc.
In a similar vein to the above, you can use a trap call that takes a parameter to break when a particular condition is true. For example, you can break when a resource of type 'FOO#' is loaded with the following command:
Even more interestingly, suppose you want to break when a loop counter reaches a particular value. You might want to only occasionally enable the breakpoint. You could add this code to your program's source:
but you might not know ahead of time what iteration to break on, or you might want to change which iteration you break on each time you run the program. One solution is to call a trap such as SysBeep that ignores its parameter (being sure to turn off the sound first):
then you can use a command like
(If you look carefully at the compiled code you could set a conditional breakpoint that looks at the register or stack location that the counter is kept in, but this is a little easier on the brain).
[Top]
A high-powered alternative to MacsBug is The Debugger from Jasik Designs. ( macnosy@jasik.com)
Jasik's Debugger will do all kinds of things that MacsBug cannot. There are advantages and disadvantes to each debugger. The major difference is that MacsBug is "lightweight" - you can install it by just dropping it in the System Folder, and it has such a small impact on the system that you can leave it installed on regular user or test machines.
While Jasik's Debugger is more powerful, it is harder to install and to use. There is also a difference in price - MacsBug is free, while The Debugger costs a couple hundred dollars - but the price is well worth it. You really should have both debuggers, and should consider the cost of The Debugger as a valuable and basic part of your programmer's toolkit. I personally prefer to keep both debuggers around, using MacsBug most of the time but hauling out Jasik's Debugger when the going gets rough. I know lots of people that use The Debugger as their main debugger.
Among the features of The Debugger are source code debugging of almost any code (not just application code), "training wheels" for PowerPC assembly (the names of the opcodes are spelled out in the disassemblies), sensible debugging of OpenDoc parts, MMU protection, a graphical user interface (windows and menus, as opposed to MacsBug's command line) and many, many more. A significant advantage of The Debugger is the personal technical support provided by Steve Jasik himself.
Also from Jasik Designs are MacNosy, a powerful interactive disassembler, and CoverTest, a code coverage test tool.
[Top]
[Top]
Clicking here will get you nowhere. Have you any idea where these pages went? A lot of links have disappeared since I last updated the page.
Voting for GoingWare at The Programming Pages will encourage more
people to read these articles.
[Top]
Please see the explanation in the Reciprocal Links page.