Koryn's Units
Koryn Grant, Macintosh Developer



pascalflavouredmac picture

Memory Integrity

Author: Koryn Grant
Copyright: 1998 Koryn Grant
Email: koryn@kagi.com
Date: 10 June 1998

Summary

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/ .

Environment

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.

How to use MemoryIntegrityLib

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.

What went wrong and why

If NoteHandleRef asserts, you passed it a handle that was not found in the list of tracked handles. There are two possibilities:

  1. You forgot to add it to the list to be tracked, d'oh!
  2. The handle is invalid - you've already disposed of the underlying memory but still have a reference to it. This indicates an error in your application.

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:

  1. Your application has a reference to the handle, but you forgot to call NoteHandleRef between calling ClearMemoryRefs and CheckMemoryRefs.
  2. Your application has a memory leak.

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 and CheckMemoryRefs.

Overhead

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.

MemoryIntegrityLib interface

procedure InitialiseMemoryRefs;

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.

procedure DestroyMemoryRefs;

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.

procedure CheckMemoryRefs;

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.

procedure ClearMemoryRefs;

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.

Credits

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.

Download MemoryIntegrity



Copyright ©1998 Koryn Grant.