HeapLister
Volume Number: 6
Issue Number: 10
Column Tag: C Forum
Related Info: Memory Manager
HeapLister
By Denis Molony, Sultanate of Oman
Introduction
About six months ago I received my upgrade to Think C version 4.0, and what an
upgrade it was! It took a while to come to grips with the basics of Object Oriented
Programming, but now that things are falling into place for me I think it’s definitely
the way of the future. After a couple of months reading the manual (and re-reading the
manual) and playing around with the example programs, I was looking for a real
subject to try these new ideas out on. That’s when I came across an article in a back
issue of MacTutor by Bob Gordon, titled ‘Peeking at Heap Zones’. It was an excellent
introduction to the Mac’s heap structure, and seemed an ideal subject for a full-blown
application. So, armed with Bob’s article, the Think Class Library (TCL) and the
Memory Manager chapters of Inside Macintosh, I set off. The result is the subject of
this article - HeapLister.
What is HeapLister?
HeapLister is a utility that will show you the contents of every block in either
the Application or System heap. It consists of a single window (see figure 1) which is
divided into three panes; a header pane to display information about the heap zone
itself, a scrolling pane to show all of the blocks in the heap (one block per line), and
another scrolling pane which displays every byte in a selected block. To select a block
you just scroll your way through the list until you find the one you want to display.
Click on the block’s line in the middle pane, and the bottom pane immediately refreshes
itself.
The middle pane contains several columns of information. First comes either an
asterisk (to indicate a non-relocatable block, or a locked relocatable one - like
TMON), or a blank space. Next comes the block’s address in memory, or at least its
address when the snapshot was taken. Then the block’s type is shown, this will be
either Rel, Nonrel or Free. If the block is relocatable, its status flags are displayed.
These flags tell you whether the block is locked, purgeable or a resource. Uppercase
means it is, and lowercase means it’s not. Next is the block’s size correction, this is
the number of unused bytes at the end of the block, followed by the block’s physical
size (including the size correction, if any). Again, if the block is relocatable the
address of its master pointer is shown along with its current address. The current
address is there in case the block has moved. If it has moved there will be a plus sign
after its current address. Finally, if the block is known to be in a particular format,
the format name is shown.
The format of the bottom pane’s output varies from block to block. HeapLister
knows about many of the standard Mac formats ( windows, controls, strings, resource
maps etc.) and will display the data differently for each different kind of block. The
screen shown in figure 1 is displaying a code resource, which defaults to byte format.
Figure 2 shows a window record being displayed. As you can see, each of the fields in
the Window Record data structure are listed together with their contents.
Apart from just displaying each block’s data, HeapLister also lets you manipulate
blocks with most of the standard Memory Manager calls. You can allocate and dispose of
handles and pointers, compact the heap, purge memory, move it around, lock and
unlock handles, and basically cause all sorts of mayhem. The commands menu contains
most memory manager calls which you may issue at will. But be careful, unlocking
things that are meant to stay locked is a certain recipe for a system crash.
Figure 1.
How does it work?
The Macintosh heap is itself a data structure, which we can find by calling
GetZone (for the application heap) or SystemZone (for the system heap). These
routines return a variable of type THz, which is a pointer to a heap zone. The first
object in the heap is a header record, the last is a trailer record, and everything in
between is the application’s data. These bytes are divided up into separate chunks of
varying sizes called blocks. Whenever your application calls NewHandle or NewPtr
the Memory Manager rummages around amongst these blocks until it finds (hopefully)
enough contiguous memory to satisfy your request.
Each block can be one of three types; relocatable, non-relocatable or free.
Relocatable blocks are what you get when you call NewHandle, and may move under
certain, known conditions (see IM for a list of routines that may move or purge
memory). Non-relocatable blocks may never move and are the result of a call to
NewPtr. Free blocks are the bytes in the heap that aren’t currently being used by
anyone.
To display each of the blocks in a heap zone it is simply a matter of following the
path from one block to the next. The zone header gives us the address of the first block,
and each block has an eight byte header that tells us its size (amongst other things). To
find the next block we just add the size to its address, and that is the location of the
next block. We continue doing this until the resulting address is outside of our heap
(past the trailer record).
Figure 2.
That’s the easy bit. Unfortunately, the heap has a habit of shuffling its contents
around, so you can’t rely on things staying put while you scroll around poking your
nose into the interesting bits. For this reason HeapLister takes a snapshot of all the
block headers and copies the details into a fixed size array. This way it can display the
list of blocks without worrying about the Memory Manager moving or purging
something.
The routine that does most of the work in this application is the Refresh method
of the CHeapInfo object. Each time it is called it discards its current snapshot of the
heap and builds another one from scratch. This method gets called whenever the heap
is compacted, new handles or pointers are allocated, or whenever the user requests it.
It performs two functions. The first is the traversal of the heap, where each block gets
entered into the heapList array, along with details about its size, type and status. The
second function is the attempted identification of each block in the heap. HeapList runs
through the window list and records any windows it finds; it does the same for the
menu list, and then examines the resource list. Any resources of type CODE, STR# or
STR are identified. Finally it builds a list of all known master pointer blocks. All this
investigation is optional, so feel free to add to or remove any of this code as you wish.
At the very end of the routine it sends the Document object a message to say that the
heap has been refreshed so that the Document can tell all its panes to redraw
themselves.
The other routine I should discuss is the Draw method of the CDataPane object.
This object is the pane which displays each block’s contents in detail. It asks the heap
object for details about the currently selected block, and calls the routine which is
appropriate for that kind of block. The default block type is BYTE, and the code to
display these blocks I copied and slightly modified from Think’s example desk
accessory HexDump. All of the other known types have their own WriteSomething
routine (WriteWindow, WriteControl, WriteResMap etc). The code is all pretty
straightforward so I won’t go into it here. Suffice to say that if you want to add your
own block display routines, this is where they should go.
Miscellaneous Things
I have tried as much as possible to keep HeapLister 32 bit clean. The main
problem is the way handles are used under System 6. I call StripAddress all over the
place to make sure my handles are clean, but because the hardware currently ignores
the top eight bits of all handles I can’t really be sure I caught them all.
I would like to give HeapLister the ability to look in any heap, not just its own
and the System heap. I know if I made it a DA it would work, but I’d rather keep it as
an application. If anyone out there knows how to get hold of MultiFinder’s heap zone I
would greatly appreciate knowing.
I have tested HeapLister on a Mac Plus, an SE and an SE/30, with and without
MultiFinder. Unfortunately, I don’t have access to anything more exotic so I don’t
know what will happen on anything else. Hopefully everything will just get faster.
Known bugs
Scrolling to the end of very large byte-format blocks will cause display
problems. This is due to the fact that the pane’s drawing location ends up outside the
port’s coordinate grid. I haven’t been able to think of a good solution for this one yet.
Adding your own types
When you spot something you recognise, write a routine to display it in its
correct format, add the appropriate command to the Data menu and define the new type
in the CHeapInfo.h header file. Then add a few lines of code to the DoCommand and
UpdateMenus methods in CHeapInfo.c, and you will be able to select your format from
the menu.
Where to go from here
I find that I have as much fun playing with HeapLister as I did in writing it.
There is a lot of strange stuff floating around in both the application and system
heaps.You can spend hours just following trails of handles to interesting things I also
found it to be very educational to see just how master pointer blocks work, and how
resource maps hang together. In fact, just writing the display routine teaches you a lot
about the object itself.
If anyone does add some interesting routines, I’d love to hear from them. And
when the bugs start to surface, I’d also like to know. I’m sure that I’ve probably
violated a few OOP principles along the way, some because of restrictions imposed by
the volatility of the heap, and some through plain ignorance. Anyone who wants to
point out the error of my ways is very welcome. I may be contacted (mail only, I’m
afraid) at the following address:
Denis Molony
C/- The Royal Oman Police
Directorate of Computers
PO Box 2
Muscat
Sultanate of Oman
Acknowledgements
My thanks go to Bob Gordon for his original article, without which I wouldn’t
have known where to start. Thanks also to whoever wrote Think’s HexDump DA for
their hex-formatting routines. And finally to Think for their wonderful class library.
Writing a Mac application has finally become a matter of just writing your own code -
not 3000 lines of standard interface code - the way it should have been all along.
Continued in next frame