PowerPlant Debugging Classes
Volume Number: 15 (1999)
Issue Number: 5
Column Tag: PowerPlant Workshop
PowerPlant's Debugging Classes
by John C. Daub, Austin, Texas USA
Techniques and tools to help you find and prevent bugs
What We Have Here is a Failure To Deallocate
That line comes from infamous Coolhand Leak (see the help text for the "leaks" 'dcmd'). And like it or not, things like this - bugs - happen. Humans write software, by nature humans make mistakes, so it's a fair bet that there will be mistakes (bugs) in software. Of course no one likes bugs, especially the users of our software, so it behooves us programmers to do our best to write solid and bug-free code. I've found three stages in which the programmer can work to make their code bug free: before, during, and after coding.
Before coding requires proper planning and design in terms of interfaces, implementation, tool selection, and so on. Some would argue this stage is most important as starting off on the wrong foot is more difficult to recover from down the road. During coding involves techniques such as performing assertions to validate arguments and return values, checking for nil, and other processes that you can perform while typing code. After coding involves such things as running under a debugger or stress-testing/validation tool (such as QC or Spotlight), or worst case, receiving a bug report from a user.
The core of PowerPlant mostly offers tools for the "during" stage. Looking in UDebugging and UException, you find the core elements for checking errors, throwing exceptions, and performing Signals (informational messages). These tools provide a good foundation to help you write more solid code, but certainly more can be done. Enter the Debugging Classes.
The Debugging Classes are a fairly recent addition to PowerPlant. They debuted on CodeWarrior Professional 3, but I wasn't happy with their implementation. I have since rewritten the classes, and the fruits of this labor can be seen on Pro 5, which should be on your desk by the time you read this article. In this article, I'd like to introduce you to the Debugging Classes and the wonderful tools they have to offer towards helping you write better code, squash any bugs you may find, and hopefully prevent bugs from happening in the first place. The Debugging Classes don't offer much to help with the "before" stage (aside from the fact you should choose to use them), but they do provide a plethora of tools for the "during" and "after" stage. The Debugging Classes are considered "Advanced Classes," so if you are a PowerPlant newbie, you may wish to get better acquainted with the core and basics of PowerPlant first. But do return to look at the Debugging Classes eventually - they'll help you write better code in the long run. Without further ado, let's dig in!
Overview
The Debugging Classes were written with a few chief goals in mind: to be drop and play, to be flexible, and to be simple yet powerful. The classes needed to be as "drop and play" as possible; that is, they needed to be designed so they could be added to (or removed from) an existing project with minimal effort. The classes are not integrated throughout PowerPlant (at least at this stage) to avoid dependencies upon the classes. This was important as existing projects may have their own debugging mechanisms. It also provides a means for users to try out the classes on their existing project, but if they determine they aren't right for the job, they can easily be removed. Theoretically, to add the classes to an existing project, all one has to do is add the files from the _Debugging Classes folder to a project/target, establish a few macros for third party supports (as laid out in PP_Debug.h), then call LDebugMenuAttachment::InstallDebugMenu() from their Application's Initialize() method:
void
CMyApp::Initialize()
{
#if PP_Debug
LDebugMenuAttachment::InstallDebugMenu(this);
#endif
}
There are other means of instantiating the Debug menu as well as overall use of the Debugging Classes, but for the newcomer, this is the best place to start.
The Debugging Classes were also designed to be as flexible as possible. When I first wrote the classes, I generated everything at runtime: the windows, the menus, the behaviors. This was done to minimize impact and conflicts with existing projects. However, this method turned out to be less than optimal as it didn't provide a simple means of customization, and it also tied the user to the implementation provided by the Factory (pun intended). For the Pro 5 version of the classes, I removed all of the on-the-fly creation and instead used the tried-and-true Mac method of using Resources. By using Resources, the code is cleaner, the code is smaller, and it's easier to customize. The one downside is the potential for Resource ID's to conflict. To work around this possibility, one can use the factory Resources as a template for your own custom versions. Then when creating the Debug menu, one can fill out an SDebugInfo structure with the relevant information about the Resources. For example, the default 'MENU' ID for the Debug menu is 600. If instead you wished to use 1000, you could create the Debug menu like this:
void
CMyApp::Initialize()
{
#if PP_Debug
SDebugInfo theDebugInfo;
LDebugMenuAttachment::SetDebugInfoDefaults(theDebugInfo);
theDebugInfo.debugMenuID = 1000;
mDebugAttachment = new
LDebugMenuAttachment(theDebugInfo);
ValidateObject_(mDebugAttachment);
mDebugAttachment->InitDebugMenu();
AddAttachment(mDebugAttachment);
#endif
}
Slightly more code than the previous snippet, but it allows for greater flexibility in the long run. Don't worry if you don't understand everything that's going on just yet. By the end of this article things should look a little more familiar.
Another item of flexibility is the extensive use of macros throughout the classes. The macros are designed to be the point of entry to the Debugging Classes within your code. These macros toggle their behavior based upon the nature of your target: debug build or release/final build. This way you can write your code once, gain full debug features in your debug builds, but have the code stripped out or reverted to a "clean, release" version of the functionality for you release builds. So instead of doing this:
#if PP_Debug
LPane* thePane = theWindow->FindPaneByID(1);
ThrowIfNil_(thePane);
LCaption* theCaption = dynamic_cast<LCaption*>(thePane);
ThrowIfNil_(theCaption);
#else
LCaption* theCaption = dynamic_cast<LCaption*>
(theWindow->FindPaneByID(1));
ThrowIfNil_(theCaption);
#endif
you can instead use one of the Debugging Classes macros:
LCaption* theCaption =
FindPaneByID_(theWindow, 1, LCaption);
You can find FindPaneByID_ and other useful macros in PP_DebugMacros.h, as well as throughout many of the header files like UOnyx and UDebugNew.
The third goal of the classes was to be as simple as possible, yet as powerful as possible. There is a great deal of code involved in the classes, but most of this code you never have to interact with directly. The Tree classes provide a dynamic runtime display of the Commander Chain and Visual Hierarchy. There are many classes and a lot of code to make this magic possible, but all you have to do is select Show Command Chain or Show Visual Hierarchy from the Debug menu - the details are handled for you automatically. Furthermore, if something is better handled by another tool, the Debugging Classes strive to work that tool into PowerPlant instead of reinventing the wheel. UOnyx provides a PowerPlant-savvy interface to Onyx Technology's QC and Spotlight tools. UDebugNew provides a wrapper to DebugNew (a tool for validating C++ pointers), and works with UOnyx to help DebugNew and Spotlight play nice together.
Of course, a final goal of the classes is to help you write better code, and diagnose and fix problems. Now that you've gotten a taste of the classes, let's take a look at the specifics of what the Debugging Classes have to offer.
What's In There?
Support Classes
If you're unfamiliar with the Debugging Classes, you're probably wondering what this Debug menu is. The Debug menu, as implemented by LDebugMenuAttachment, is a special menu added to your Application's MenuBar that provides you with runtime access to many of the Debugging Classes runtime features.
Figure 1. The Debug menu from LDebugMenuAttachment.
The Debugging Classes are designed with the Debug menu as the central point. You don't have to use LDebugMenuAttachment to use the Debugging Classes (many of the classes can be used independently), but LDebugMenuAttachment certainly facilitates things - again I would recommend newcomers to start here and work on customization after you're more familiar with the classes.
From the Debug menu, you can: break into a Debugger; launch helper applications like ZoneRanger (on the CW Pro Mac Tools CD) or HeapBoss (from Biit, Inc); display the Command Chain and Visual Hierarchy (Tree Classes); perform heap manipulations (compact, purge, scramble, via UHeapUtils and LHeapAction); interact with DebugNew (UDebugNew); interact with QC (UOnyx); manipulate the values of gDebugThrow and gDebugSignal (UDebugging); consume memory (UMemoryEater); and validate your PPob Resources (UValidPPob). One of the advantages to the menu is that some of these values normally have no runtime interface, e.g. gDebugThrow/gDebugSignal or DebugNew; to manipulate these values you have to change your code, recompile, and re-run. However through the Debug menu, you can manipulate these values at runtime with no need to recompile your project/target.
Another part of the Support Classes is LHeapAction. LHeapAction is a Periodical object that performs certain actions on a given heap (typically the Application's heap). These actions include compacting, purging, compacting and purging (at the same time), and if QC support is enabled, performing a heap consistency check, bounds checking, and a heap scramble. You can access these actions and modify their frequency via the Debug menu.
Figure 2. HeapAction submenu.
To perform such actions on your heap, either immediately or periodically, is a boon towards helping you locate stale Handle usage, avoiding problems with cached Resources, and overall stress-testing your Application.
Stream Classes
There's actually only one Stream Class, LDebugStream, but the utility of this class cannot be underestimated. Those of you that use the "debugging by printf" technique will find LDebugStream a useful feature. LDebugStream is a Stream class that sends data to a debugging location. This location (EFlushLocation) can be a debugger, a file, the mechanism used by gDebugThrow or gDebugSignal, or to a console such as the SIOUX console window (the ability to flush to a console is new in Pro 5). One item of note is that LDebugStream does not inherit from LStream; this is intentional as an LStream can be two-ways (in and out), but LDebugStream is one-way (out, to the EFlushLocation). Furthermore, LStream is oriented towards binary streams, whereas LDebugStream outputs ASCII text.
LDebugStream provides many methods and operator overloads to ease streaming. There are numerous Write methods for many data types both basic (integers, bool's) and Mac-specific (Rect, Point, RGBColor, RgnHandle, FourCharCode, NumVersion, Fixed). When these data types are written, they are written in a useful human-readable manner. For example, outputting a Point would generate the line:
Point - v: 15 h: 20
LDebugStream also provides detailed information about raw Handle's and Ptr's via the Dump methods. These Dump methods were introduced in Pro 5, and not only provide a raw look at the memory, but also useful information about the memory, especially useful with Handle's. A DumpHandle_() of STRx_Standards yields this result:
--------------------------------------
"PP Debug Tester DEBUG PPC" debug log for Sun, Mar 7, 1999 4:23:13 PM
--------------------------------------
Results of Handle dump. FILE: "CDebugApp.cp" LINE: 347
Address: 0x1E30BA4 | Master pointer: 0x1E47F40 | Handle data size: 97 | State: UPR
Resource Info - Type: 'STR#' ID: 200 Name: Standards
Resource Attributes - SysRef: OFF SysHeap: OFF Purgeable: ON Locked: OFF Protected:
OFF Preload: OFF Changed: OFF
Results of Raw dump. FILE: "CDebugApp.cp" LINE: 347
Dumping 97 bytes of memory starting at address: 0x1E47F40
OFFSET | CONTENTS (HEX) | CONTENTS (ASCII)
------------------------------------
0x00000000 | 0004 1644 6562 7567 6769 6E67 2043 6C61 | ...Debugging Cla
0x00000010 | 7373 6573 2044 656D 6F0D 5361 7665 2046 | sses Demo.Save F
0x00000020 | 696C 6520 4173 3A1C 4F70 656E 3A20 4465 | ile As:.Open: De
0x00000030 | 6275 6767 696E 6720 436C 6173 7365 7320 | bugging Classes
0x00000040 | 4465 6D6F 1C53 6176 653A 2044 6562 7567 | Demo.Save: Debug
0x00000050 | 6769 6E67 2043 6C61 7373 6573 2044 656D | ging Classes Dem
0x00000060 | 6F | o
End Raw dump. FILE: "CDebugApp.cp" LINE: 347
End Handle dump. FILE: "CDebugApp.cp" LINE: 347
In this dump you can see information such as the address of the Handle and its master pointer, the size of the Handle, its state (lock, purgable, resource), and the raw memory contents. As well, if the Handle is a Resource Handle, information about the Resource and its attributes are listed for your edification. Notice as well that DumpHandle_() is a macro. As was mentioned previously, the Debugging Classes provide macros for ease of use. See LDebugStream.h for more DebugStream macros.
Tree Classes
The Tree Classes are one of the niftier portions of the Debugging Classes, as well as the most common reason for PowerPlant users to start using the Debugging Classes (since everyone seems to run into a Commander Chain problem sooner or later). Long long ago in a cubicle far far away from mine, Greg Bolsinga (Metrowerks' Class Wrangler Wrangler) had problems with his Commander Chain. He hacked up a utility that would display the Commander Chain in a Pane at runtime. Since it helped him solve his problem, Greg passed his (admitted) hack along to me to see what we could do with it officially for PowerPlant. We placed it on CW Pro 2 as an example, but there the seed was sown for the Debugging Classes. Since that time, LCommanderTree has been cleaned up and fleshed out with more functionality than the original, abstracted back to a more common base class (LTree), and from there subclassed to also provide information about the Visual Hierarchy (LPaneTree). In addition to displaying the Visual Hierarchy of the frontmost regular or modal window, LPaneTree also displays Attachment and Broadcaster/Listener information.
Figure 3. LCommanderTree and LPaneTree in action.
Looking at Figure 3 you can see the Trees display a great deal of information about the respective Tree. First, each LTreeWindow contains a set of controls across the top to allow you to manipulate the Tree/Window's behavior. You can set the Tree to automatically refresh at a certain interval (this interval is currently set via the Debug menu), or to refresh immediately. You can save the current Tree to a file (internally using LDebugStream), and you can also toggle the extra information (such as the Attachment and Broadcaster/Listener information) on and off as it is optional information and can clutter the screen.
The CommanderTree provides you with a visual representation of your Application's Commander Chain. Each level of indentation represents a level down the chain, and only objects inheriting from LCommander are displayed. For each object, its name is given as well as its Commander duty state. If the object is also a Pane, it's PaneIDT is listed. Text styles are used to designate the on-duty chain, the target, and any latent Commanders; the styles are bold, red color, and italics, respectively, by default, but you can customize these styles as you wish.
The PaneTree provides you with a representation of the Visual Hierarchy of the frontmost window in the regular or modal layer (floating windows are not supported by default as targeting floaters is tricky to do). Basic Pane information is provided, such as object name, PaneIDT, state (active, enable, visible), and descriptor. If you turn on the display of extra information, any Attachments on the Pane will be listed (along with basic Attachment information such as its Message and the value of mExecuteHost). If the Pane is a Broadcaster its Listeners will be listed, and if the Pane is a Listener its Broadcasters will be listed. One additional feature of LPaneTree is that the (deepest sub-) Pane currently being moused over will be colored in green (by default, user customizable) to aid in identifying Panes at runtime - especially useful if you create your Views on the fly.
Headers
There are three header files used throughout the Debugging Classes: PP_Debug.h, PP_DebugConstants.h, and PP_DebugMacros.h. PP_Debug.h is akin to PP_Prefix.h in that every file in the Debugging Classes somehow #includes this file. It is also similar to PP_Macros.h in that PP_Debug.h contains the macros used to establish the state of feature support within the Debugging Classes; this mostly pertains to third party supports (QC, Spotlight, MoreFiles, DebugNew). PP_DebugConstants.h contains the constants used by the Debugging Classes. Most of this file you do not need to concern yourself with, however one item of note is that the CommandT range of 600-799 inclusive is reserved for use by the Debugging Classes. Fortunately, this range is within the range of PowerPlant's reserved CommandT's (-999 to 999 inclusive) so you shouldn't run into any conflicts.
The PP_DebugMacros.h file is certainly the most interesting file of the three global headers. Within this file are many useful macros that you can and should use throughout your code. There are validation macros: ValidatePtr_(), ValidateHandle_(), ValidateObject_(), ValidateSimpleObject_(). These validation macros ensure the given variable is valid by performing nil checks and using QC and DebugNew if those supports are enabled. You should use these macros whenever you allocate memory/objects or when the state of memory/object could be in question. Also within the file are assertion macros: AssertHandleLocked_() ensures the given Handle is locked, AssertHandleUnlocked_() ensures the given Handle is unlocked, AssertHandleResource_() ensures the given Handle is a Resource Handle, and AssertHandleMemory_() ensures the given Handle is a Memory Handle. As these macros are assertions, they will be preprocessed out of release/final builds. The Verify_() macro used to be a synonym for Assert_(), but in Pro 5 its behavior changed slightly. It still performs an assertion, but now the Verify_() statement is not preprocessed away in release/final builds. The DisposeOf_() suite of macros are used in place of delete. In debug builds, the pointer is validated, delete is called, and the pointer variable is set to nil; in release/final builds, the pointer is deleted and the variable set to nil.
Although the Debugging Classes are currently and mostly separate from the rest of PowerPlant, certain ideas have found their way into the core of PowerPlant. One of these are the Suppression macros: StDisableDebugThrow_(), StDisableDebugSignal_(), StChangeDebugThrow_(), and StChangeDebugSignal_(). These macros temporarily modify the EDebugAction of gDebugThrow or gDebugSignal. The Disable macros set the EDebugAction to debugAction_Nothing, and the Change macros set the EDebugAction to whatever new EDebugAction is specified. You'd want to use these macros in situations where a Throw_() or Signal_() might occur but you don't want PowerPlant's UDebugging mechanisms to kick in.
try {
StDisableDebugThrow_();
// This will ThrowIfOSErr_(memFullErr); if we
// run out of memory.
DoSomethingMemoryIntensive();
} catch (LException& iErr) {
// No problems. Running out of memory isn't
// fatal.
if (iErr.GetErrorCode() != memFullErr) {
throw; // rethrow
}
}
In the above snippet, I know it's possible to run out of memory. I want this situation to be handled and the exception to still be thrown, but I don't want to hear about it (I don't want to break into the debugger or have the Throw 'ALRT' displayed). By using StDisableDebugThrow_() the UDebugging functionality is temporarily suppressed. It is important to note the "St" designation of the macros - they do behave like stack-base classes; in fact, they use StValueChanger (UMemoryMgr.h) to accomplish their task.
Finally, PP_DebugMacros.h contain some useful macros that replace the tedium of FindPaneByID and dynamic_cast's. Typically one calls FindPaneByID() along these lines:
LCaption* theCaption = dynamic_cast<LCaption*>
(theWindow->FindPaneByID(1));
ThrowIfNil_(theCaption);
This code sequence does work, but there is a hidden chance for failure: if FindPaneByID() does not find the Pane it returns nil, and then the dynamic_cast will fail as well (although it is safe to dynamic_cast a nil pointer). The problem is that you cannot tell from the ThrowIfNil_() if the failure was due to FindPaneByID() failing or the dynamic_cast failing. FindPaneByID_() alleviates this ambiguity by first performing a FindPaneByID() and validating that result. If the result is good, then the dynamic_cast is performed and that result is validated as well. Along the way, any failures are noted for exactly the source of the failure to help you better pinpoint the source of the failure. The improved code is:
LCaption* theCaption =
FindPaneByID_(theWindow, 1, LCaption);
There is also a FindPaneByIDNoThrow_() variant that raises a Signal instead of Throwing, and returns nil upon failure.
The DebugCast_() and DebugCastNoThrow_() macros perform a dynamic_cast of one pointer to another (it does not handle dynamic_cast'ing of references). They validate the arguments and report the error by Throwing or Signaling. These four macros not only provide a more robust means of performing their functionality, but it's a lot easier to type the one line of code. :-)
Resources
This portion of the Debugging Classes is fairly straightforward. DebugTrees.CTYP contains the CTYP's for the Tree classes (LTree, LPaneTree, LCommanderTree, LTreeWindow). If it's not already there, place this file into the Custom Types folder within the Metrowerks PowerPlant folder of your CodeWarrior installation (this is the same folder where the Constructor application resides). It is not essential to add this file to your project/target, but you're welcome to do so if you wish to include the 'CTYP' Resources in your binaries.
PP Debug Support.ppob and PP Debug Support.rsrc contain vital Resources used by the Debugging Classes. PP Debug Support.ppob file contains the default 'PPob', 'MENU', and 'Txtr' Resources used by the various c lasses. You can add this file as-is to your project/target, or you can copy the Resources to your own file and customize them as you wish. PP Debug Support.rsrc contains the nifty debug icon used as the menu title for the Debug menu (optional, cosmetic) and contains replacement Resources for the ThrowAt and SignalAt alerts (as found in PP DebugAlerts.rsrc). These replacement Resources are used in conjunction with UDebuggingPlus.cp, which we'll discuss in the next subsection. You're welcome to use the Resources as-is or customize them as you wish.
Utility Classes
The Debugging Classes contain many utility classes and namespaces to help in your coding efforts. UDebuggingPlus.cp is a replacement for UDebugging.cp. Both files implement the UDebugging namespace, but only one file can be within your project at a time (this is akin to how UDesktop.cp and UFloatingDesktop.cp work). Before Pro 5, UDebugging's ThrowAt and SignalAt alerts (as found in PP DebugAlerts.rsrc) would display the alert, and the only action you could take in response to the alert was to click OK and proceed in whatever manner the flow of the code would take you. This wasn't always optimal. UDebuggingPlus.cp (and PP Debug Support.rsrc) were written to provide you with greater options upon receiving the ThrowAt or SignalAt alerts.
Figure 4. Improved SignalAt alert.
In Figure 4 you can see the additional options provided by the improved UDebuggingPlus. Continue did just that - continue execution. Quit immediately calls ExitToShell. Debugger breaks into a debugger, Quiet changes the respective EDebugAction to debugAction_Nothing, and Log outputs the data from the Signal or Throw to a file via LDebugStream. Furthermore, the whitespace given to display the condition/error code is larger than the original alerts to allow for more error text. Although Figure 4 shows the SignalAt alert, the ThrowAt alert is the same in terms of functionality.
As of Pro 5, UDebugging and PP DebugAlerts.rsrc now support most of the functionality provided by UDebuggingPlus/PP Debug Support.rsrc. However, as of this writing, the Log functionality is missing. When all of UDebuggingPlus' functionality is moved into UDebugging, UDebuggingPlus will be obsoleted.
UDebugNew provides a wrapper for DebugNew, mostly to enable DebugNew and Spotlight to work together (as DebugNew does funky yet kosher things that Spotlight would otherwise complain about). UDebugNew also contains various macros to facilitate using DebugNew in your code, interfaces are given to aspects of DebugNew that don't have interfaces, removes the tedium of remembering what is and is not supported in DebugNew based upon the setting of DEBUG_NEW, and provides a more PowerPlant-savvy error handler. As of this writing Metrowerks is working on a replacement and improvement for DebugNew called DebugMem. Eventually support for DebugMem will be added to the Debugging Classes, but this support is not part of Pro 5.
UDebugUtils is a collection of sundry utilities. CheckEnvironment() examines your environment at runtime and alerts you to any potentially problem situations, such as not having a debugger installed. It is not required to call CheckEnvironment(), but it is highly recommended to do so. A good place to call it is immediately after Toolbox initialization. Debugger() and DebugStr() are replacements for the Toolbox traps of the same name. These versions however check for the presence of a debugger before calling the Toolbox trap to avoid any ugly crashes. UDebugUtils.h contains wrapper macros for the Debugger traps: PP_Debugger_() and PP_DebugStr_() go through the UDebugUtils bottlenecks, and Debugger_() and DebugStr_() call straight to the Toolbox. The advantage of these macros is that they will be preprocessed out of final/release builds. How many times have you inadvertently left a DebugStr call in your release builds?
UHeapUtils provides means for manipulating heaps. You can compact, purge, and scramble your heaps. Compacting and purging is accomplished through Toolbox routines, but scrambling is a bit odd. If QC is present, then QC's scrambling mechanisms are used. But if QC is not present, DebugStr's are used to drop into MacsBug to use its heap scrambling functionality. It's a little ugly, but it works. There's no excuse not to scramble your heap as it will sooner or later turn up problems (don't forget about LHeapAction).
UMemoryEater helps you simulate low-memory situations by consuming memory.
Figure 5. Eat Memory dialog.
When you select Eat Memory... from the Debug menu, the dialog in Figure 5 is displayed. You can specify the number of bytes to eat and what type of memory to eat: Handle (relocatable) or Ptr (non-relocatable). This functionality is best used with ZoneRanger to keep an eye on the exact state of your heap as you consume memory. It is important to test your Application's functionality in low-memory situations. LGrowZone can help, but it is only one part of a good low-memory handling strategy.
UOnyx provides wrappers for Spotlight and QC from Onyx Technology. Use of UOnyx is optional, which is important if you do not own QC and/or Spotlight. The key features of UOnyx are the famous macros to aid in supporting QC/Spotlight in debug builds but is removed from release builds, and wrappers for the QC API to facilitate its ease of use and make it more PowerPlant-savvy. The QC and Spotlight products and API's are not provided by Metrowerks; you must purchase these products separately from Onyx Technology.
UProcess is a wrapper for the Process Manager that strives to make using the Process Manager a bit easier. This class can be used independent of the Debugging Classes, as its functionality certainly extends beyond the bounds of debugging. UVolume contains utility functions for working with volumes. It is not a complete class by any means. UVolume (and UProcess as well) were actually written to aid in the implementation of LDebugMenuAttachment's feature for launching ZoneRanger and HeapBoss. See UVolume::FindApp() and UProcess::LaunchApp() to see how these fit together.
Finally, UValidPPob provides a means for validating the data within your 'PPob' Resources.
Figure 6. Validate PPob dialog.
Currently it looks at what classes are within a specified PPob (specified by the dialog in Figure 6), compares this list to the URegistrar table, and any unregistered classes are reported via a Signal_(). A ValidateAllPPobs() method is provided as well to validate every PPob, and the results of this validation are streamed to a file via LDebugStream. Currently UValidPPob can only list what is in a PPob but not registered. The other direction, listing what is registered but not in any PPob ("over-registration") cannot be cleanly done. One key reason is the Appearance Classes, which register the Implementation classes but these classes never appear in any PPob DataStream.
Project Stationery, Sample Code, & Documentation
The last part of the Debugging Classes are non-source code support files. For Pro 5, the PowerPlant project stationery has been revamped. The old standbys of Basic, Document, Appearance, and Network remain, and a new "Advanced Stationery" has been introduced. This stationery is geared towards the serious new project. It includes most of the core PowerPlant files, Thread Classes, Table Classes, Appearance Classes, and of course the Debugging Classes. The project and default sources are also set for debug and release targets. Debug targets turn compiler optimizations off and debug information on, while release targets turn compiler optimizations on and debug information off. The default Application object class contains a fair amount of debugging infrastructure to help you get started, using the Debugging Classes and MetroNub Utilities (see the (Debugger Extras) folder in your CodeWarrior install). One extra file in the Advanced Stationery is PPDebug_New.cp. This file implements global replacements for all variants of operators new and delete. These implementations utilize DebugNew and the Debugging Classes to make the operators more robust and better integrated with PowerPlant and your project. Also provided is PP_NewHandler(), a PowerPlant-savvy C++ new_handler (see §18.4.2.2 in the C++ language standard).
Also new in Pro 5 is documentation on the Debugging Classes. A chapter on the Debugging Classes has been added to the PowerPlant Advanced Topics manual. You've probably noticed this article does not contain any tutorials nor in-depth HOWTO's for using the Debugging Classes. This was an intentional decision as the chapter and its accompanying example code do a good job to get you going in using the Debugging Classes. If this article has sparked your interest in these classes, your next stop should be reading this chapter and doing its example code.
Customization
I'd like to briefly touch on the subject of customizing the classes. Before you consider customizing the classes, first take the time to familiarize yourself with the classes and tools as provided from the Factory. I tried to minimize the need for customization, so try out the defaults and find what works for you and what you might wish to improve upon.
One area that I personally would customize is the Debug menu itself. The most useful customization might be adding keyboard shortcuts to commands you frequently use, as always going to the menu can be a bit tedious (I'm one of those people that prefer the keyboard over the mouse). Another might be adding your own commands to the menu to help you execute test code or start a specific chain of events. Regardless of what you do, don't modify the default Resources and Resource/source files. Instead, make copies of the relevant Resource or subclass the sources local to your project and modify those instead. Better still, consider making your own project stationery that contains all of your customizations so every new project you create will be established just as you like it from the onset.
Futures
The Debugging Classes shipped in the _In Progress folder on CodeWarrior Professional 5, but the classes are fairly robust and stable. I do not anticipate a great deal of changes to them in the future. Certainly rough edges will be smoothed, user feature requests will be considered, and any bugs found (heaven forbid ;-) will of course be fixed. One outstanding issue is the integration of the classes throughout the core of PowerPlant; the jury is still out on this one. The classes will need to fully stabilize first as well as weighing the potential impact integration could have upon existing source bases. I'd also like to see about adding supports for more third party products, such as HeapManager. Of course these are all hopes and dreams and by no means constitute any sort of promise nor guarantee they actually will happen (although I hope they will :-).
One point of definite uncertainty is how the Debugging Classes will evolve in terms of Carbon and Mac OS X. Some of the Debugging Classes technology is centered around the Mac OS of today, both in terms of low-level supports, like UDebugUtils, and higher-level assumptions, like UHeapUtils. Some classes should go across with no problems (Trees), some may need to be modified slightly (the Raw methods in LDebugStream), some may become moot (UHeapUtils), and others are totally unknown at this time (UDebugUtils). We'll just have to wait and see how Carbon and OS X shape up before the fate of the Debugging Classes can be fully determined.
Conclusion
I hope you've enjoyed this article on the PowerPlant Debugging Classes. They've certainly been one of the most fun bits of PowerPlant I've written, and IMHO one of the more useful. If you haven't used the Debugging Classes before, please do give a read over their chapter in the Advanced Topics manual and give them a try. If you have used the Debugging Classes, try out an aspect that you may not have used yet, like the various macros. And if you wish, feel free to email me with any stories of how the Debugging Classes helped you, feature requests, bug reports, and even flames (but I hope there aren't many of those).
Good luck, happy programming, and I hope you find the Debugging Classes a helpful, useful, and essential part of your toolbox.
Bibliography
John C. Daub spends a lot of time working on PowerPlant. When he's not writing code for Metrowerks, he writes a lot of his own PowerPlant classes for the Contributed Class archives. John's seen a lot of code in his days, and they are part of the inspiration behind the Debugging Classes. John is also still using System 7.6.1 at home, so please don't complain about the screenshots. You can reach John at hsoi@pobox.com.