|
Volume Number: 13 (1997)
Issue Number: 5
Column Tag: develop
Squashing Memory Leaks With TidyHeap
by Mike Fullerton
One of the more mundane aspects of application development is making sure any system resources that your application allocates get deallocated, particularly memory allocations. Think of the hours you've spent tracking down pesky memory leaks. A powerful debugging and QA tool, TidyHeap watches over memory allocations and helps you track down leaky blocks. Learn how to make sure that memory allocated by your application is always managed correctly.
TidyHeap is a tool for tracking and verifying dynamically allocated memory in C++ applications. It started out as a quick and dirty way to keep track of undeleted objects in the MacApp framework and evolved into a tool for anyone writing application software in C++. TidyHeap works with any application built in C++, with or without a framework. Among its powerful features are the following:
- If your application allocates an object or data that's never deleted, TidyHeap tells you about it when your application quits. (Objects and data allocated by your application are called client objects in this article.)
- TidyHeap can provide you with the filename and line number where any existing client object was created, including a client object that's never deleted.
- You can tell TidyHeap to watch for double deletions on all client objects or on a specific client object.
- You can verify the integrity of a single client object or all client objects at each pass through the event loop.
- You can have TidyHeap break into MacsBug when a specific client object is deleted.
- You can force every single new operation to fail in consecutive order, in consecutive runs of your application.
- You can design your own tests for specific client objects or for all client objects.
In short, TidyHeap is designed to be flexible, powerful, and user extensible. Currently, TidyHeap runs only on the PowerPC(tm) platform (if you would like to see it implemented for 680x0 machines, send e-mail to mabugs@devtools.apple.com), and it's compatible only with Metrowerks CodeWarrior (we MacAppsters plan to make it compatible with the various MPW compilers sometime soon). A package of TidyHeap materials is available at www.mactech.com.
TidyHeap Basics
TidyHeap itself is a small library of C++ classes that intercept and track all allocations and deallocations performed by the global operators new and delete. It does this invisibly - you don't need to do anything but call new and delete in the way you usually do. Also, since TidyHeap is essentially a debugging tool, it's compiled and enabled only if its preprocessor constant qTidyHeap is defined to true. This enables you to turn it off easily when building nondebug versions of your application.
TidyHeap works with any data type that can be created with new. This can be all the objects in your application or framework, structs created to be passed to Toolbox routines, or huge char arrays created as buffers. It really doesn't matter. TidyHeap treats all allocations as raw blocks of allocated memory, without knowing or caring what the user data is.
The raw materials
TidyHeap ships with two CodeWarrior projects and every source file needed to build and use it. The projects are for building the shared library, TidyHeapSharedLib, and the static library, TidyHeapPPC_d. Both libraries are required because TidyHeapSharedLib doesn't contain TidyHeap itself, but is the mechanism I use to make sure that TidyHeap is the last thing to be deleted, after static destruction time.
It's essential that TidyHeap be the very last thing deleted when the client application quits. This guarantees that the application won't try to delete anything after TidyHeap, which could cause TidyHeap to return inaccurate information. To make sure that it terminates after static destruction time, TidyHeap registers a callback in TidyHeapSharedLib that's called when the shared library terminates. It so happens that this occurs after static destruction time, which ensures that TidyHeap is the very last thing deleted.
You may build the shared library if you need to; however, a built version of TidyHeapSharedLib is also included. Install this as you would any shared library, either in the same folder as the application or in your Extensions folder - just be careful not to install it in both places. Also, don't forget to add TidyHeapSharedLib to your application's project! At this time weak importing of the shared library is not supported, so don't use weak linking. If you do, your application may crash.
The second CodeWarrior project is for TidyHeap itself. There are about 35 source files. You can include them directly in your application's project or you can build them into a static library with the TidyHeapPPC_d.n project and include the static library in your project. The choice is yours. Personally, I prefer building TidyHeap as a library.
Finally, TidyHeap ships with two small tools - New Dropper and TidyHeap Director. New Dropper allows you to easily insert into or remove from your source files an important macro, and TidyHeap Director lets you change TidyHeap's behavior without recompiling your code. These tools aren't yet fully supported by Apple (so use them at your own risk), but they're very useful - as you'll see later.
Hooking in your framework or project
Hooking into TidyHeap is simple. You define a C function named .TidyHeap declares and calls the function but doesn't define it.
(If this function isn't defined, you'll get a link error.) defines the global operators new and delete that TidyHeap calls, and allocates AC_CTidyHeap or a client-defined subclass of TidyHeap. (See "MacApp Naming Changes" for the reasons behind TidyHeap's naming conventions.)
TidyHeap has static pointers to the global operators new and delete. By default, the static pointers point to NewPtr and DisposePtr. These are the functions that TidyHeap calls to actually create and delete memory blocks. The function must set the proper memory allocation and deallocation functions for your framework before it creates AC_CTidyHeap or its subclass. For example, the following code defines for the MacApp framework in which MAOperatorNew and MAOperatorDelete are MacApp memory allocation and deallocation hooks. (Although this and later examples use MacApp, don't forget that TidyHeap works with any framework.)
AC_CTidyHeap* CreateTidyHeap() { AC_CTidyHeap::GlobalNew = MAOperatorNew; AC_CTidyHeap::GlobalDelete = MAOperatorDelete; return TH_new TMacAppTidyHeap(); }
The AC_CTidyHeap class includes virtual functions that you can override if you want to change TidyHeap's behavior. For example, the method HandleNilAllocation is called by TidyHeap when it forces new to fail. If your framework throws an exception when new fails, you can override HandleNilAllocation to throw the same exception. This enables TidyHeap to properly emulate what happens when new fails in your environment. (More on forcing new to fail later.)
To use the default TidyHeap and the default memory allocation hooks, include the file CreateDefaultTidyHeap_AC.cp, which defines a function that uses the defaults:
AC_CTidyHeap* CreateTidyHeap() { return new AC_CTidyHeap; }
Now you're ready to use TidyHeap and begin reducing those hours spent tracking memory leaks.
First contact
In this section you'll meet the headers and trailers, the AC_CTidyHeapBlock object, and the TH_new macro, all of which TidyHeap uses to track and store data it needs to watch over your memory allocations and perform various actions.
TidyHeap itself is a single object that's created when the global operator new or the AC_CTidyHeap static member function Instance is called for the first time. Instance returns a pointer to the AC_CTidyHeap object. If this object hasn't been allocated yet, Instance creates it and returns the pointer. In this way you're guaranteed to have a valid pointer to the AC_CTidyHeap object. You can use Instance to call AC_CTidyHeap's interface. For example, the following line of code returns the total amount of memory currently allocated by your application using the new operator.
long sizeTally = AC_CTidyHeap::Instance()->GetSizeTally();
Header and trailers
When your application calls new to allocate a client object, TidyHeap intercepts the call and increases the amount of memory allocated by a small amount in order to put a header and trailer around the client object's memory block. TidyHeap stores data in the header and trailer that allows it to track your client object. The header contains a pointer to an AC_CTidyHeapBlock object, which has been allocated by TidyHeap and stored in a linked list. This is how TidyHeap tracks all the client objects. Every client object allocated with new has its own AC_CTidyHeapBlock object (described in the next section).
The header and trailer also contain three markers. Markers are long integers that TidyHeap sets to magic values you can easily recognize when looking at a raw memory dump of your object - 0xF1F1F1F1, 0xF2F2F2F2, and 0xF3F3F3F3. These markers act as fences around your client object (TidyHeap operates on the assumption that if the markers get trashed your client object is probably trashed also). TidyHeap can quickly verify the integrity of your block of data by checking the markers. There are two markers in the header, one just before and one just after the pointer to the client object's AC_CTidyHeapBlock object, and one marker in the trailer, as shown in Figure 1.
AC_CTidyHeapBlock and block IDs
The AC_CTidyHeapBlock class is important. An AC_CTidyHeapBlock object holds information about the client object your application allocated, and member functions defined for the class are used to perform various operations on the client object.
Your client object's block ID is one of the more useful pieces of information you can obtain from AC_CTidyHeapBlock. The block ID is a unique number assigned by TidyHeap to each client object that's created with new. The block ID corresponds to the number of times new has been called, so if the block ID is 6, the client object was created on the sixth call to new. Here's an example of how to get the block ID from a client object:
TClientObject* myObject = new TClientObject; AC_CTidyHeapBlock* objBlock = AC_CTidyHeap::Instance()->GetBlock(myObject); long blockId = objBlock->GetID();
Among the member functions defined for the AC_CTidyHeapBlock class is the Verify method. By first getting the pointer to its AC_CTidyHeapBlock object and then calling AC_CTidyHeapBlock::Verify, you can verify the integrity of your client object. The Verify method cross-references the pointer to the client object's AC_CTidyHeapBlock object that the client object stores in its header with the pointer to its client object that the AC_CTidyHeapBlock object stores. It also checks the validity of the block's markers. For example, if you write past the end of your object, the trailer marker will be invalid and TidyHeap will notify you by returning an error from Verify. Here's an example of using the Verify method:
TClientObject* myObject = new TClientObject; AC_CTidyHeapBlock* objBlock = AC_CTidyHeap::Instance()->GetBlock(myObject); AC_CTidyHeapError err = objBlock->Verify();
Storing filename and line number with TH_new
TidyHeap provides TH_new, an optional macro replacement for the global operator new. TH_new stores the filename and line number of where the client object was created in the client object's AC_CTidyHeapBlock object. The filename and line number enable you to find the code that created undeleted client objects. TH_new uses the C++ preprocessor's built-in __FILE__ and __LINE__ macros, which is why TH_new needed to be a macro. Of course, you don't want to ship your application with TidyHeap enabled, so TH_new resolves to new when qTidyHeap is not defined or is defined to false, making it possible to leave the TH_new macro in your shipping code.
Listing 1. Getting the filename and line number void main() { TClientObject* myObject = TH_new TClientObject; AC_CTidyHeapBlock* objBlock = AC_CTidyHeap::Instance()->GetBlock(myObject); const char* fileNamePtr = objBlock->GetFileName(); long lineNum = objBlock->GetLineNum(); printf("MyObject was created in file: %s at line #%ld\n", fileNamePtr, lineNum); }
Listing 1 shows how to get a filename and line number for an object created with TH_new. When the program is run, you get the following output:
MyObject was created in file: Main.cp at line #1
The New Dropper tool that ships with TidyHeap makes it easy to switch between new and TH_new. Drag your project's folder onto the New Dropper tool and you're presented with two options: change TH_new to new or change new to TH_new. That's it. When you make a choice, New Dropper scans the source files you dropped on it and makes the chosen change. New Dropper finds all the source files in all the folders, no matter how deeply you've nested them. It only opens files that it decides are text files with source code in them. In other words, it won't open your project files, or even a text file that doesn't have a ".h" or ".cp" appended to its name. It doesn't change new in comments, in object-specific overrides of the new operator in a client object, or if the file has "TidyHeap" anywhere in its name. The software is provided as is, so please make a backup of your source before using it!
Warnings, logging, and the log file
TidyHeap has two types of messages - a logged message and a warning - and provides a default mechanism for directing logged messages to a log file. The log file is written to the same folder as your application and is named myapplication.log. It's an MPW text file and is compatible with MPW scripting. The AC_CTidyHeap method you can use to log a string is
AC_CTidyHeap::Logf(const char*, ...);
All the output from Logf is appended to the log file, and it's designed to be compatible with the ANSI-standard printf. Here's some code showing how you could use Logf:
TClientObject* myObject = TH_new TClientObject; AC_CTidyHeapBlock* objBlock = AC_CTidyHeap::Instance()->GetBlock(myObject); AC_CTidyHeap::Instance()->Logf("The object I just created has an ID of %ld", objBlock->GetID());
The warning mechanism is simply a wrapper on top of DebugStr that can be turned on and off. You'll get a TidyHeap warning in only a few situations - in general, to notify you that something has gone wrong. For example, if your application terminates and there are five client objects that haven't been deleted, you'll get the warning "TidyHeap Warning: You have 5 undeleted blocks."
This undeleted-block warning has a g appended to it so that your application will continue immediately after dropping into MacsBug. I think you'll like it better than typing g and Return a thousand times.
The AC_CTidyHeap method that you use to send a warning is
AC_CTidyHeap::Warnf(const char*, ...);
Like Logf, it behaves the same way as the ANSI printf, except that the output goes to MacsBug, of course.
When you get a warning, TidyHeap usually logs more information about the problem to the log file, which is described next.
Listing 2. Leaving undeleted objects #include "UClientObject.h" #include "CTidyHeap_AC.h" void main() { TClientObject* object1 = TH_new TClientObject; TClientObject* object2 = new TClientObject; TClientObject* object3 = TH_new TClientObject; delete object3; }
Using the log file
Let's see how to use the log file to track down memory leaks. For example, after running the program in Listing 2, you'll get this warning from TidyHeap: "TidyHeap Warning: You have 2 undeleted blocks." When there are undeleted client objects after your application quits, TidyHeap logs information about all the objects that haven't been deleted to the log file.
Here's what the log file looks like after you run the program in Listing 2:
### TidyHeap Termination Messages: ### 2 Outstanding blocks, 3 total blocks, Ave Size 4.500000 ### New called 3 times, Delete called 1 times ### BlockInfo: ID:1, Address:0x1EC8F8C, Size:4 File 'Main.cp'; Line 6 ### BlockInfo: ID:2, Address:0x1EC902C, Size:4 File 'unknown'; Line 0 ### Sum of outstanding block sizes = 8 bytes
The log file contains some useful information, as you can see. There are two outstanding blocks - the two TClientObject objects that the program in Listing 2 neglected to delete. The new operator was called three times, and delete was called once, which jibes with what we know from the example code.
Because object1 was created by the TH_new macro, you can see exactly where it was created and go back and make sure it gets deleted - a piece of cake, even on huge projects.
Unfortunately, we didn't use TH_new for object2, so TidyHeap logged its location as "unknown." However, you can tell which object it was by looking at its block ID. The object has a block ID of 2 (### BlockInfo: ID:2), which means it was created second. In the case of this example program, it's trivial to figure out which object was created second. It's not so trivial to locate the client object that has a block ID of 1,233 in a two million-line project. Later, I'll show you a couple of advanced techniques for tracking down a block when all you have is its block ID.
Actions
With actions you can modify TidyHeap's behavior, the AC_CTidyHeapBlock object, or the client object that's being operated on currently. Actions are lightweight subclasses of AC_CTidyHeapAction. TidyHeap's action class architecture makes it easy to customize and add features to TidyHeap. Read on to learn how to install actions, use the actions that ship with TidyHeap, and create your own actions.
Installing into global and object contexts
Action objects are differentiated by their context. There are two types of context: global and object. An action that's executed in a global context operates on all allocated objects, while an action that's executed in an object context operates on only a single object.
TidyHeap manages two different lists of actions that operate in the global context - the global new and global delete actions. When a global new action is installed, it's performed on every block of memory that's processed through new; a global delete action is performed on every block of memory processed through delete.
For example, AC_CActionVerifyBlock is one of the action classes that ship with TidyHeap. When an AC_CActionVerifyBlock action is installed as a global delete action, every block of memory is verified as it's being deleted. You would install an AC_CActionVerifyBlock action as a global delete action as follows:
AC_CActionVerifyBlock* theAction = TH_new AC_CActionVerifyBlock; AC_CTidyHeap::Instance()->AcquireDeleteAction(theAction);
Here's the code to install AC_CActionVerifyBlock as a global new action:
AC_CTidyHeap::Instance()->AcquireNewAction(theAction);
All actions, once they're installed, are deleted automatically at the appropriate time by TidyHeap. The method names start with "Acquire" to make it clear that TidyHeap owns the installed action.
When installing an action into an object context, you can install it only as a delete action. You do this by getting the pointer to the client object's AC_CTidyHeapBlock object, and then calling the AC_CTidyHeapBlock class's AcquireDeleteAction method. Once an action is installed in an object context, it will be performed once as the client object is being deleted.
Listing 3 shows how to install an AC_CActionBreakOnDelete action. This action calls DebugStr to break the program into MacsBug when the client object is deleted. AC_CActionBreakOnDelete provides an alternate method for tracking down leaks. If you install this action in a client object that you're suspicious of, and your program never gets the appropriate MacsBug message, you know the client object has leaked. The MacsBug message you would get from the program in Listing 3 when the block is deleted is "Deleting Block 1."
You don't have to use TH_new when creating AC_CTidyHeapAction classes. TidyHeap's objects are tracked differently from client objects so that none of the TidyHeap classes will show up as undeleted blocks.
Listing 3. Installing AC_CActionBreakOnDelete #include "CTidyHeapActions_AC.h" void main() { TClientObject* myObject = TH_new TClientObject; AC_CTidyHeapBlock* objBlock = AC_CTidyHeap::Instance()->GetBlock(myObject); AC_CActionBreakOnDelete* theAction = TH_new AC_CActionBreakOnDelete; objBlock->AcquireDeleteAction(theAction); delete myObject; }
Built-in action classes
TidyHeap ships with a handful of action classes. You can use some of these action classes in more than one context. For example, as shown in the previous section, you can use AC_CActionVerifyBlock as a global delete action, but there's no reason why you can't also use it as a delete action assigned to a specific object. Just be sure to allocate a new action object for each context you install the action in.
AC_CActionBreakOnDelete and AC_CActionVerifyBlock are described in the previous section. The remaining built-in action classes are described below.
AC_CActionBreakOnNew
In the section "Warnings, Logging, and the Log File" I pointed out that it can be extremely difficult to locate a client object when all you have is the block ID. The action class AC_CActionBreakOnNew helps you do this. You can install an AC_CActionBreakOnNew object as a global new action. AC_CActionBreakOnNew takes a long integer as a parameter in its constructor; this parameter represents a block ID. When the client object with the given ID is allocated with new, the action will break into MacsBug. At this point you can do a stack crawl in your favorite debugger. Here's an example where you're trying to track down the client object with the block ID 1042:
AC_CActionBreakOnNew* theAction = TH_new AC_CActionBreakOnNew(1042); AC_CTidyHeap::Instance()->AcquireNewAction(theAction);
Another way to locate a client object with a block ID is by using the block ID to set a conditional breakpoint in your debugger deep in the guts of TidyHeap. For example, set it on the line in CTidyHeap_AC.cp that increments the counter in the method CreateBlock (currently line 287, blockPtr->fId = fCount). AC_CTidyHeap::CreateBlock allocates the memory for client objects' AC_CTidyHeapBlock objects.
AC_CActionMakeNewFail
The action class AC_CActionMakeNewFail does what its name implies - it takes a long integer n as a constructor parameter that tells the action to make calls to new fail on the nth call. Install an AC_CActionMakeNewFail action as a global new action.
AC_CActionVerifyAll
An AC_CActionVerifyAll action iterates through the list of currently allocated client objects and verifies them all, one at a time. TidyHeap warns you if it finds a corrupt object. Be warned that this significantly slows down your application if installed in a global context. You can use the action class AC_CActionVerifyAll in either of the action contexts.
AC_CActionDoubleDeleteWatcher
The action class is particularly useful. An AC_CActionDoubleDeleteWatcher action marks the client objects sent into delete as deleted, but prevents the memory allocated for the client object from actually being deleted. If the object is deleted a second time, TidyHeap will catch it. The downside to installing AC_CActionDoubleDeleteWatcher is that objects being watched are never actually deleted. If you're having problems with a client object being deleted multiple times, I recommend cranking up your application's memory partition and running with AC_CActionDoubleDeleteWatcher installed. It will catch the double deletion as soon as it occurs. You can install an AC_CActionDoubleDeleteWatcher action either as a global delete action or in a specific client object.
Even when an AC_CActionDoubleDeleteWatcher action isn't installed, TidyHeap still looks out for double deletions. Every client object that's deleted is marked, and if it comes through delete again, TidyHeap catches it if possible. The trouble is that in normal operating circumstances, the second time through, the object pointer is pointing at invalid memory, so the "already deleted" mark may not be valid - anything could be occupying that memory. However, in my experience, double deletions usually happen within a very short time of each other, so you have a decent chance of finding the memory still undisturbed. As long as memory is undisturbed, TidyHeap catches the second deletion.
AC_CActionVerifyBlock may catch a double deletion if it's installed as a delete action, because the memory the client object originally occupied may be overwritten, in which case the markers will probably be invalid and you'll get a corrupted-object warning.
AC_CActionExemptBlocks
I added the action AC_CActionExemptBlocks so that it would be possible to tell TidyHeap to ignore certain objects. For example, there are a few objects that MacApp Release 12 creates during startup that are not deleted (this was done to optimize quit times). To ensure that TidyHeap's undeleted-block messages are accurate, you can exempt those blocks. Install the AC_CActionExemptBlocks action as a global new action. Listing 4 shows the code to do this (the code is taken from the sample application Skeleton that ships with MacApp).
Listing 4. Exempting blocks void main() { // Here's where the client objects we want to exempt are created, // so install the action. AC_CTidyHeapAction* action = new AC_CActionExemptBlocks; AC_CTidyHeap::Instance()->AcquireNewAction(action); InitUMacApp(4); InitUPrinting(); InitUViewSkeleton(); // Now we want to start tracking objects, so remove the exempter. AC_CTidyHeap::Instance()->RemoveNewAction(action); // RemoveNewAction doesn't delete the action, so we do it ourselves. delete action; TApplicationSkeleton* aApplicationSkeleton = new TApplicationSkeleton; aApplicationSkeleton->IApplicationSkeleton(); aApplicationSkeleton->Run(); }
AC_CActionGarbageIn and AC_CActionGarbageOut
The AC_CActionGarbageIn and AC_CActionGarbageOut actions fill the client object's memory with garbage values just after allocation and just before deletion, respectively. The values are defined in the objects' class definitions as kGarbageInValue (0xF5) and kGarbageOutValue (0xF6). The values are meant to be recognizable and to cause a bus error if dereferenced. Install AC_CActionGarbageIn as a global new action. You can install AC_CActionGarbageOut either as a global delete action or in a specific client object.
AC_CActionExemptUnknownBlocks
With the AC_CActionExemptUnknownBlocks action installed, TidyHeap only tracks objects created with TH_new. This is handy for times when you have a lot of undeleted blocks and want to debug those created with TH_new separately. I used this action when working with MacApp Release 12, which, as I noted above, deliberately doesn't delete some blocks that were created at startup time. It worked like a charm! Install AC_CActionExemptUnknownBlocks as a global new action.
AC_CActionIncrementalNewFailer
The AC_CActionIncrementalNewFailer action is specially designed for you QA folks to make your engineers' lives miserable. AC_CActionIncrementalNewFailer makes new fail sequentially - first new number one fails, then number two fails, then number three, and so on. The kicker is that TidyHeap saves the count into a text file so that the next new that will fail is persistent between runs of your application. This stresses your failure-handling code to the limit! Install this action as a global new action.
Rolling your own action classes
The AC_CTidyHeapAction class is a very simple and lightweight class. To make your own action class, simply subclass AC_CTidyHeapAction and override the pure virtual function DoAction. The function takes as a parameter a pointer to an AC_CTidyHeapBlock object, which is the object TidyHeap is currently operating on. Listing 5 shows the code overriding DoAction for the AC_CActionBreakOnNew action class.
Listing 5. AC_CActionBreakOnNew::DoAction void AC_CActionBreakOnNew::DoAction(AC_CTidyHeapBlock* inBlock) { if (inBlock->GetID() == fID) { char str[80]; ::sprintf(str, "AC_CActionBreakOnNew: Block ID: %ld", fID); ::c2pstr(str); ::DebugStr((StringPtr) str); } }
Changing Settings with Tidyheap Director
TidyHeap writes its current settings to a file named myapplication.prf in your application's folder. It saves to this file any changes to TidyHeap settings made between runs of your application so that you can use the TidyHeap Director tool to edit the file and change the behavior of TidyHeap without recompiling your code.
Figure 2 shows the TidyHeap Settings File dialog box. The application you're working with appears after "Target Application." On the left are five Features preferences.
• Check Enabled to turn TidyHeap on; unchecking this option effectively turns off TidyHeap completely.
• Check Warnings to send warnings to MacsBug.
• If Warnings is enabled, you can set Undeleted Blocks, which causes TidyHeap to send a list of undeleted blocks to MacsBug when your application terminates.
• Check Log Undeleted Blocks to enable logging of undeleted blocks to the log file (see the section "Using the Log File"). If you have many undeleted blocks, the logging process can take a while and you may want to shut off the logging.
• Check Pointers Live Only in App Heap to tell TidyHeap to verify that pointers you send into GetBlock or VerifyBlock are within your application's memory heap.
Use the checkboxes under Global New and Global Delete to automatically install the listed actions as global new or delete actions when TidyHeap starts up.
The settings file is read once - when TidyHeap is created. To make changes to your settings, make sure your application isn't running at the time. The next time your application is run, your new settings will be loaded.
Alternative to TidyHeap
There are other tools with features similar to TidyHeap. This section tells you a little about some of them and compares them with TidyHeap.
Metrowerks ships a tool called DebugNew. You'll find the library and a demo for DebugNew on the CodeWarrior CDs. DebugNew has some of the same features as TidyHeap, but it's not as extensible and doesn't have as many features. The features it does have are quite valuable, however: it can check for leaks, validate blocks of allocated memory, and verify that a block is in the application heap. DebugNew also has a macro like TH_new called NEW, and it has a log file. DebugNew is smaller than TidyHeap and is written in C, so it may run in a smaller footprint and it doesn't require C++.
Another tool for tracking memory leaks is the ZoneRanger application, also from Metrowerks. ZoneRanger is quite good not only at tracking memory leaks, but also at fine-tuning your application's heap and managing memory. This is a great tool that every programmer should have at hand.
QC and Spotlight by Onyx Technology are MacOS system extensions that provide memory debugging for applications without requiring that you modify the source code or recompile the application. QC stress tests applications for all kinds of memory-related errors. These tools work well with applications that directly allocate memory with the Macintosh Memory Manager or that manage resources returned by the Resource Manager, and can be used in conjunction with TidyHeap.
Memory Mine by Adianta is a standalone tool for monitoring heaps and stress testing applications. It flags heap corruption when it happens and you can see memory leaks as they occur. Source code is not needed to monitor heaps.
Apple ships a handful of dcmds (debugger commands for MacsBug) that are extremely helpful as well: Blat makes sure that nobody writes to the first 256 bytes of low memory, DisposeResource catches resource handles that are passed to DisposeHandle, Leaks watches for memory leaks when you tell it to (via "leaks on"/"leaks off" commands to MacsBug), EvenBetterBusError watches for dereferences of NIL and writes to NIL (location 0), and Xap fills pointers and handles that were disposed of with a value that causes a bus error if dereferenced. Furthermore, the useful system extension DoubleTrouble catches double disposes of a handle.
Another very useful tool from Apple is the Debugging Modern Memory Manager, which, not surprisingly, is a debugging version of the Modern Memory Manager. It performs many of the functions described in this article, including filling a client object's memory with garbage, checking for writes past the end of blocks, and checking for bad handles passed into Memory Manager routines.
Terminate
Well, there you have it. You've got TidyHeap hooked into your framework or application and you're ready to let it track your client objects and help you figure out where a leaky block was created, using either the TH_new macro or its block ID. You can now squash all your memory leaks with ease. You also learned about some of the more advanced features of TidyHeap, including the action classes that ship with it. And, you're ready to use TidyHeap Director to modify TidyHeap's behavior without recompiling your application.
I hope that TidyHeap makes it possible for you to focus on making insanely great software without having to worry so much about the more irritating and mundane tasks of application programming, such as tracking undeleted objects. Happy block hunting!
Thanks to our technical reviewers Brian Arnold, Paul Black, Geoff Clapp, Mike Rossetti, and Jason Yeo.
MACAPP NAMING CHANGES
The MacApp team is working to make MacApp a less monolithic framework. Whenever practical and possible, we're designing and implementing MacApp's new features so that they can be used independently of MacApp. TidyHeap is a prime example of this trend. Other features in development are networking support, cooperative and multiprocessing threading support, OpenDoc container support (ALOE, the Apple Library for Object Embedding), and such useful things as enhanced Standard Template Library (STL) support and a handful of C++ design patterns (useful abstractions for common problems). We're calling this collection of features "Apple Classes," but that may not be the final name.
We want everyone to be able to use Apple Classes without running into namespace and file inclusion collisions, so after much heated debate, we decided to adopt and adapt the OpenDoc Development Framework (ODF) naming convention until we get universal support for C++ namespaces in all our compilers. That's why all the TidyHeap classes are named AC_CClassName. The "AC" stands for "Apple Classes" and represents the scope of the class (ODF begins names with "FW" for "Framework"). The underscore conceptually represents the C++ scope operator (::). (For example, AC_CTidyHeap will become AppleClasses::CTidyHeap when C++ namespaces are supported.) The second "C" in "AC_C" represents the usage of the class - "C" for classes, "M" for mixin classes, and "S" for structs.
Filenames in Apple Classes significantly diverge from the ODF naming convention. ODF is cross-platform (Windows and Macintosh), so all its files conform to the Windows 8-character maximum. Because we don't need to do this, we decided to make the filename the same as the class name with a slight twist, which is that we append "_AC" to the class name like this: CTidyHeap_AC.h. We put the difficult-to-type underscore at the end to make life easier in dealing with the files in the Finder and for situations where you would want to use type-ahead, such as in a CodeWarrior project window.
If the TidyHeap classes accompanying this article begin with letters other than "AC," it's because a different name for the collection of features referred to here as Apple Classes was adopted after this article was published.
- SPREAD THE WORD:
- Slashdot
- Digg
- Del.icio.us
- Newsvine