QC
Volume Number: 10
Issue Number: 12
Column Tag: Tools Of The Trade
QC: Test For Success 
Integrated debugging & stress testing software
By Paul Robichaux, Fairgate Technologies, Harvest, AL
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
About the Author
Paul Robichaux - Paul Robichaux leads a dual life: by day, he builds Unix and
Windows NT applications for a major computer company. During the rest of the time,
he builds custom-engineered Mac applications and WWW pages for clients in a wide
variety of fields. He welcomes reader e-mail at paul@fairgate.com, or visit his WWW
page at http://www.iquest.com/~fairgate.
Taking the Sting
Out Of The “D” Word
Every programmer does it. Experienced programmers usually do less of it than
neophytes, and programmers on some platforms do it more than others. What is it?
Caffeine consumption? Swearing at operating system designers? Well, besides those...
I’m talking about debugging. By some estimates, debugging can take up to 40% of
development time, and studies have indicated that there can be as much as a
twenty-to-one difference in productivity between a run-of-the-mill debugger and a
really skilled debugger.[1]
The Macintosh operating system architecture offers unique challenges to
debuggers. The combination of handles and memory-moving traps provide fertile
ground for bugs related to the almost Brownian motion of relocatable blocks.
Memory-related errors are made more difficult to isolate and fix because writing to
memory that belongs to other applications, or writing beyond the bounds of a
particular block, doesn’t always cause a crash. If a crash does occur, it can often be
quite some time after the original bad write, so the cause of the bug can be masked by
other paths taken through the source code.
There are a wide range of freely available tools available for memory & resource
debugging. They were generally written because their authors needed them, and then
saw benefit to giving them away so others could benefit from their work. That was good
for everyone. However, the heroes who wrote and distributed them (people like Greg
Marriott, who wrote EvenBetterBusError, DoubleTrouble, and DisposeHandle; and
Bo3b Johnson, who wrote Leaks, Blat, and ZapHandles) have real jobs, and decided not
to make a career of keeping the tools up to date and adding features. For example, Blat
doesn’t work on very old or very new Mac CPUs. DisposeResource and DoubleTrouble
slow system-wide performance since they are always on when installed. Still others
reveal faults not only in your code, but in others’ as well. That’s fine if you have time
to deal with other folks’ buggy software, but it sometimes gets in the way of debugging
your own.[2]
Onyx Software has gone the extra mile and built QC™, a control panel which
offers a variety of tools designed to help you build rock-solid code. QC allows you to
selectively turn on a wide range of stress-testing features (including almost all of
those provided by the above-mentioned tools) on a per-application basis. QC also
includes a versatile API which allows you to embed calls to QC’s engine within your
own code. The QC documentation says that QC is “specifically designed to make
memory-related errors reproducible,” and it excels at that task.
What QC Is For
QC is designed to perform two key functions. First, it helps you find memory and
resource-handling errors during development. Second, it provides a simple way for
quality assurance testers to put unusual stress on an application to flush out any
remaining errors or performance problems.
Most developers are already familiar with the two primary types of debuggers:
low-level and source-level. Both types allow you to interactively examine the contents
of memory and processor registers. The primary difference between them is how you
interact with the code you’re debugging. Low-level debuggers, like Macsbug, typically
require that you work with the disassembled contents of memory. You set breakpoints
by absolute or relative memory address, and the contents of structures referenced by
pointers may not always be identifiable by type. Source-level debuggers show you the
source statements of your programs and allow you to set breakpoints and examine
variables in the context of that, and some even allow you to execute function calls
without interrupting execution.
QC doesn’t really fall into either category. It’s not a debugger in and of itself; it’s
a debugging aid. You can’t manipulate or examine the contents of memory using QC.
Instead, it can force your code to drop into the installed low-level debugger when it
encounters an error, or it can simply beep. With the provided API, you can perform
any operation expressible in code when an error occurs. The API also allows you to
turn on a particular kind of debugging for any section in your code, which makes the
beep option more useful than you might otherwise suspect.
Getting Started With QC
As you’d expect, installing the QC control panel is very, very simple: drag it to
your system folder and reboot. When you open it the first time you’ll have to
personalize it; after that, you’ll see the control panel’s main window:
The center list, called the “target list,” allows you to set debugging checks on an
individual basis. Each application can have a unique combination of the 15 distinct
checks that QC knows how to perform, and you can modify which checks should be
performed while the application is being QCed. If you prefer, QC can automatically
start testing when the application or code resource under study starts executing. The
block at the window’s bottom allows you to specify a hot key to toggle QC at any time.
Pressing the hot key causes QC to start checking the currently active application;
pressing it again turns checking off. You can also use the auto-launch setting to start
QC’s tests when an application starts or a code resource is loaded.
Figure 1: The QC control panel main window.
Double-clicking one of the items in the target list brings up a dialog of test
options. List items with downward-pointing triangles have associated parameters
which you can set either via the control panel or the API routines.
Figure 2: QC’s test configuration dialog.
You can choose QC’s behavior when it detects an error - QC will either beep or
dump you into the installed low-level debugger. If you’re using the API routines, QC
will not generate an error on its own; instead, it’ll return an error code so that you
can do whatever reporting or cross-checking is appropriate. If you choose, your
callback routine can let QC handle the error itself.
What QC Knows How To Check
QC has a total of 15 different tests. Any combination of tests can be enabled for
any application or code resource. You can run the tests on both application and system
memory and heaps. All but two of the tests run on 68k and PowerPC Macs; the
“invalidate free memory” and “block bounds checking” tests are automatically
disabled when running under the Modern Memory Manager.
I found that the tests tended to fall into one of three categories: memory-handling
tests, Toolbox use tests, and stress tests. Some combinations of tests are particularly
effective at flushing out errors.
Memory and Resource Tests
These tests verify that your code is staying inside its own heap and address space.
In particular, these tests are useful for catching code which uses handles or pointers
after they’ve been disposed.
Cross-reference master pointers
This test checks that every relocatable handle in your heap is pointed to by a
master pointer within a master pointer block. As you might imagine, it’s not good if
you have a relocatable block in your heap which isn’t pointed to by a master pointer.
Validate handles/pointers
This test validates each handle or pointer passed to Memory Manager calls to
ensure that their addresses fall within the application’s heap and that the block to
which the pointer or handle points is of the correct type. This is perhaps the most
useful single test in QC’s arsenal, since it can detect a multiplicity of sins.
Detect writes to location zero
This very useful test sets a trap for code which uses dangling pointers or handles.
When enabled, QC places a special tag value at memory location zero; at each trap call
to see, QC checks to ensure that location zero still contains that tag.
Dereferencing zero
Like the “detect writes to location zero” option, this option salts location zero
with a special value. In this case, the special value is one that causes a bus error (on