Author: Koryn Grant
Copyright: 1998 Koryn Grant
Date: 10 June 1998
MemoryIntegrityLib is a debugging library for 68K and PowerPC that keeps references
to all handles and pointers allocated by an application. It is designed to catch
two common and often difficult to find bugs: memory that has been allocated and then
lost (the handle/pointer for the memory has been discarded, causing a memory leak),
and invalid handles/pointers that are still considered valid by the application (the
memory that the handle/pointer refers to has been disposed). To compile the MemoryIntegrityLib
from the sources requires MyAssertions from Peter Lewis' PNL Libraries, available
from http://www.stairways.com/ , and KGLists,
available from Pascal Central http://www.pascal-central.com/
MemoryIntegrityLib is written with CodeWarrior. Project files are included for
CWPro3 (MemoryIntegrity.µ) and CWPro2 ("MemoryIntegrity.old.µ").
Source code and a text listing of the project files (MemoryIntegrity.µ.text)
are included for use with other environments.
For simplicity I'm going to assume your application is allocating memory via handles.
If you want to allocate pointers in your application, don't worry: for every MemoryIntegrityLib
call for handles, there is a corresponding call for pointers.
To use MemoryIntegrityLib, your application should call InitialiseMemoryRefs
once at program startup. This allows the library to initialise its data structures.
When your application allocates a handle that you want tracked, add it to the list
of tracked handles with a call to AddHandleRef. AddHandleRef also
takes a 15 character string as an argument - this is optional, and provides a description
of the handle for easier identification in the debugger. If you deliberately allocate
memory and then dispose of the reference (maybe you created an object that handles
call-backs and don't need to explicitly reference it) then don't ask MemoryIntegrityLib
to track the handle.
When you dispose of a handle call DeleteHandleRef, passing it the handle
you're disposing. This removes the handle from the list of handles being tracked.
When you are ready to check for leaks/invalid references, call ClearMemoryRefs.
This sets the reference-count for each handle in the list to zero. Next make a call
to NoteHandleRef for every handle you wanted tracked. This increments the
reference-count once for each call made (calling NoteHandleRef multiple
times with the same handle causes no asserts) and asserts if a call is made for any
handle not in the list of tracked handles. When you're done, call CheckMemoryRefs.
This procedure checks the list of handles to make sure each has a non-zero reference
count. If it discovers a handle with a reference count of 0 it asserts and drops
you into the debugger. Make sure that you have a debugger such as MacsBug or Metrowerks
debugger active when you run an application that uses MemoryIntegrityLib. If it finds
any problems it asserts and drops into the debugger, and if there's no debugger that
means you get a system crash.
If NoteHandleRef asserts, you passed it a handle that was not found in
the list of tracked handles. There are two possibilities:
If CheckMemoryRef asserts, then there is a handle in the list of tracked
handles with a reference count of 0. Again there are two possibilities:
In addition to these tests, the list verifies that all handles and pointers passed
to it are valid, and verifies its internal integrity with each call to ClearMemoryRefs
MemoryIntegrityLib keeps track of the handles and pointers you tell it to keep
track of with a simple linked list. The memory required to track a single handle
or pointer is 30 bytes. For most applications this shouldn't be a problem.
Call this to create the list of handles and pointers you want tracked. It is an
error (you'll get an assert) to call any other library routines without having first
initialised the list.
Call this to destroy the list and deallocate all memory used to track handles
and pointers. Any handles and pointers in the list are not affected - after all,
it's your application's job to allocate and deallocate them. Unless you want to reset
the handle/pointer tracking without your application quitting you don't need to call
this. Simply "exiting to shell" is perfectly safe.
Call this to verify that every handle/pointer in the list has a positive reference
count. If it finds a reference count of 0 for a handle or pointer it will assert
and drop you into a debugger.
Call this to set the reference count of every tracked handle/pointer to 0.
procedure AddHandleRef(theHandle : univ Handle; theDescription : ConstStr15Param);
procedure AddPointerRef(thePointer : univ Ptr; theDescription : ConstStr15Param);
Call these procedures to add a handle or pointer to the list of handles and pointers
to be tracked.
procedure NoteHandleRef(theHandle : univ Handle);
procedure NotePointerRef(thePointer : univ Ptr);
Call these procedures to increment the reference count for that handle or pointer.
procedure DeleteHandleRef(theHandle : univ Handle);
procedure DeletePointerRef(thePointer : univ Ptr);
Call these procedures to remove a handle or pointer from the list of tracked handles
and pointers. This is non-destructive, ie only removes the list's reference to the
handle or pointer and does not affect the memory referenced by the handle or pointer.
Inspiration for writing this library came from two sources. The first is Steve
Maguire's excellent book "Writing Solid Code", ISBN
1-55615-551-4. If you consider yourself a serious or professional programmer
then you need to read this book. One of the appendices of "Writing Solid Code"
lists the C source for routines that do more or less what MemoryIntegrityLib does.
The chief differences are that I don't bother to keep track of the size of handles
or pointers (this would be a trivial modification, but I currently have no need for
it) and that Steve's routines don't track an associated string. I added this because
many times when tracking memory leaks I have the address of the offending handle
from ZoneRanger, but have absolutely no idea what it is. It probably won't turn out
to be as handy as I think - them's the breaks. The second source of inspiration is
my "Five Hundred" project. I spent 4 or 5 hours tracking down a memory
leak, and this provided the motivation for spending 2 hours writing MemoryIntegrityLib.
In the end I couldn't find the leak, so I rewrote the routine I thought was the likely
culprit and that fixed it - I don't recommend that strategy, by the way.
MemoryIntegrityLib uses MyAssertions by Peter N Lewis.