TweetFollow Us on Twitter

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
Volume Number:6
Issue Number:10
Column Tag:C Forum

Related Info: Memory Manager

HeapLister (code)

By Denis Molony, Sultanate of Oman

Listing:  DPMUtilities.h

#define _H_DPMUtilities

void Write (int h, int v, char *text, long val, int width);
Handle WriteHandle (Handle mPtr, int leading, int trailing);
void   WriteAddress (Ptr address, int leading, int trailing);
void   WriteRect (Rect *theRect, int leading, int trailing);
void   WritePoint (Point thePoint, int leading, int trailing);
void   WriteBoolean (Boolean value, int leading, int trailing);
 
void    FillLine (register char *cptr,
 char *display, int totBytes);
void    FourBlanks (void);
void    DrawHex4 (register unsigned int i);
void    DrawHex6 (register unsigned long i);
void    AppendString (register char *s);
void    NumToHex6 (register unsigned long i, register char *s);

#define CharOf(c) ((((c)>=0x20)&&((c)<=0xCF))?(c):’.’)
Listing:  DPMUtilities.c

#include <DPMUtilities.h>
#include <pascal.h>

FontInfo  fInfo;
char    *dp;
char    *countPtr;

char hexChar[16] = {‘0’,’1',’2',’3',’4',’5',’6',’7',
 ‘8’,’9',’A’,’B’,’C’,’D’,’E’,’F’};

void Write (int h, int v, char *text, long val, int width)
{
 Str255 theText;
 int    charWidth = CharWidth (‘0’);
 
 CtoPstr (text);

 MoveTo (h - StringWidth (text), v);
 DrawString (text);
 
 if (width != 0) {
 NumToString (val, theText);
 Move ((width * charWidth) -
 StringWidth (theText), 0);
 DrawString (theText);
 }
 
 PtoCstr (text);
}

void WriteRect (Rect *theRect, int leading, int trailing)
{
 Str255 text;
 
 if (leading > 0)
 Move (leading, 0);
 
 NumToString (theRect->top, text);
 DrawString (text);
 DrawChar (‘/’);
 NumToString (theRect->left, text);
 DrawString (text);
 DrawChar (‘/’);
 NumToString (theRect->bottom, text);
 DrawString (text);
 DrawChar (‘/’);
 NumToString (theRect->right, text);
 DrawString (text);
 
 if (trailing > 0)
 Move (trailing, 0);
}

void WritePoint (Point thePoint, int leading, int trailing)
{
 Str255 text;
 
 if (leading > 0)
 Move (leading, 0);
 
 NumToString (thePoint.v, text);
 DrawString (text);
 DrawChar (‘/’);
 NumToString (thePoint.h, text);
 DrawString (text);
 
 if (trailing > 0)
 Move (trailing, 0);
}

Handle WriteHandle (Handle mPtr, int leading, int trailing)
{
 char   display[10];
 long   trueHandle;
 
 if (leading > 0)
 Move (leading, 0);

 if (mPtr == 0L)
 DrawString (“\pNULL”);
 else {
 NumToHex6 ((long)mPtr, display);
 DrawString (display);
 DrawText (“ ->”, 0, 3);
 
 trueHandle = (long)StripAddress (*mPtr);
 NumToHex6 (trueHandle, display);
 Move (4, 0);
 DrawString (display);
 }
 
 if (trailing > 0)
 Move (trailing, 0);
 
 return ((Handle)trueHandle);
}

void WriteAddress (Ptr address, int leading, int trailing)
{
 char   display[10];
 long   trueHandle;
 
 if (leading > 0)
 Move (leading, 0);

 if (address == 0L)
 DrawString (“\pNULL”);
 else {
 NumToHex6 ((long)address, display);
 DrawString (display);
 }
 
 if (trailing > 0)
 Move (trailing, 0);
}

void WriteBoolean (Boolean value, int leading, int trailing)
{
 Str255 text;
 
 if (leading > 0)
 Move (leading, 0);
 
 if (value)
 DrawString (“\pTrue”);
 else
 DrawString (“\pFalse”);
 
 if (trailing > 0)
 Move (trailing, 0);
}

void AppendString (register char *s)
{
 register char i, len;
 
 *countPtr += (len = *s++);
 for (i = 0; i < len; i++)
 *dp++ = *s++;
}

void DrawHex6 (register unsigned long i)
{
 register int mod = 20;
 register int j;
 register unsigned long mask = 0x000FFFFF;
 
 for (j = 6;j > 0; --j) {
 (*countPtr)++;
 *dp++ = (hexChar[i>>mod]);
 mod -= 4;
 i &= mask;
 mask >>= 4;
 }
}

void DrawHex4 (register unsigned int i)
{
 register int mod = 12;
 register int j;
 register unsigned int mask = 0x0FFF;
 
 for (j = 4;j > 0; --j) {
 (*countPtr)++; 
 *dp++ = hexChar[i>>mod];
 mod -= 4;
 i &= mask;
 mask >>= 4;
 }
}

void FourBlanks ()
{
 (*countPtr) += 4;
 *dp++ = ‘ ‘;
 *dp++ = ‘ ‘;
 *dp++ = ‘ ‘;
 *dp++ = ‘ ‘;
}

void FillLine (register char *cptr, char *display, int totBytes)
{
 register int  k;
 register int  *ptr;
 register char c;
 register long n;
 long   i;
 int    count = 0;

 ptr = (int*) cptr;
 i = (long) cptr;
 
 dp = countPtr = display;
 *dp++ = 0;

 n = i;
 
 DrawHex6 (i);
 
 AppendString (“\p:  “);
 for (k = 0 ;k < 4; k++) {
 if (count >= totBytes)
 FourBlanks();
 else
 DrawHex4(*ptr++);
 n += 2;
 count += 2;
 (*countPtr)++;
 *dp++ = ‘ ‘;
 }
 
 (*countPtr)++;
 *dp++ = ‘ ‘;
 for (k = 0; k < 4; k++) {
 if (count >= totBytes)
 FourBlanks ();
 else
 DrawHex4(*ptr++);
 n += 2;
 count += 2;
 (*countPtr)++;
 *dp++ = ‘ ‘;
 }
 AppendString (“\p  “);

 count = 0;
 for (k = 0; k < 16; k++) {
 c = *cptr++;
 if (++count > totBytes)
 return;
 (*countPtr)++;
 *dp++ = CharOf(c);
 }
}

void NumToHex6 (register unsigned long i, register char *s)
{
 register int mod = 20;
 register int j;
 register unsigned long mask = 0x000FFFFF;
 
 *s++ = 6;
 for (j = 6;j > 0; --j) {
 *s++ = (hexChar[i>>mod]);
 mod -= 4;
 i &= mask;
 mask >>= 4;
 }
}
Listing:  HeapConstants.h

#define LOCKtext 8
#define UNLOCKtext 9
#define HPURGEtext 10
#define HNOPURGEtext 11
#define LINEHEIGHT 11
#define LINESINMAINPANE   12
#define HANDLESIZE 100
#define PTRSIZE  100
Listing:  CHeaderPane.h

#define _H_CHeaderPane
#include <CPane.h>
#include “CHeapInfo.h”

struct CHeaderPane : CPane {
 void   IHeaderPane (CView *anEnclosure,
 CBureaucrat *aSupervisor,
 short aWidth, short aHeight,
 short aHEncl, short aVEncl,
 SizingOption aHSizing, SizingOption
 aVSizing);
 void   Draw (Rect *area);
};
Listing:  CHeaderPane.c

#include “CHeaderPane.h”
#include “CHeapInfo.h”
#include <DPMUtilities.h>

extern CHeapInfo *gHeap;

#define TAB1100
#define TAB2250
#define TAB3360
#define TAB4395

#define LINE1    12
#define LINE2    24
#define LINE3    36

void CHeaderPane::IHeaderPane (CView *anEnclosure,
 CBureaucrat *aSupervisor,
 short aWidth, short aHeight,
 short aHEncl, short aVEncl,
 SizingOption aHSizing, SizingOption aVSizing)
{
 CPane::IPane (anEnclosure, aSupervisor, aWidth,
 aHeight, aHEncl, aVEncl, aHSizing, aVSizing);
}

void CHeaderPane::Draw (Rect *area)
{
 THz    zonePtr;
 blkHdr *hdrPtr;
 FontInfo info;
 int    lineHeight;
 heapHdrheapHeader;
 
 TextFont (geneva);
 TextSize (9);
 
 if (gHeap->GetHeapZone () == 0)
 zonePtr = SystemZone ();
 else
 zonePtr = GetZone ();
 
 hdrPtr = (blkHdr*) &zonePtr->heapData;
 heapHeader = gHeap->GetHeader ();

 PenSize (2, 2);
 MoveTo (0, height - 2);
 LineTo (width, height - 2);
 PenSize (1, 1);

 Write (TAB1, LINE1, “Current heap zone :”, 0,
 0);
 WriteAddress ((Ptr)heapHeader.zonePtr, 6, 0);
 Write (TAB1, LINE2, “Zone start :”, 0, 0);
 WriteAddress ((Ptr)hdrPtr, 6, 0);
 Write (TAB1, LINE3, “Zone end :”, 0, 0);
 WriteAddress ((Ptr)heapHeader.lastUsed, 6, 0);

 Write (TAB2, LINE1, “Total blocks :”,
 heapHeader.totalBlocks, 8);
 Write (TAB2, LINE2, “Total bytes :”,
 heapHeader.totalBUsed, 8);
 Write (TAB2, LINE3, “Identified blocks :”,
 heapHeader.totalKnown, 8);

 Write (TAB3, LINE1, “Free :”,
 heapHeader.totalFree, 4);
 Write (TAB3, LINE2, “NonRel :”,
 heapHeader.totalNonRel, 4);
 Write (TAB3, LINE3, “Rel :”,
 heapHeader.totalRel, 4);

 Write (TAB4, LINE1, “:”,
 heapHeader.totalFreeBytes, 7);
 Write (TAB4, LINE2, “:”,
 heapHeader.totalNonRelBytes, 7);
 Write (TAB4, LINE3, “:”,
 heapHeader.totalRelBytes, 7);
}
Listing:  CDataPane.h

#define _H_CDataPane
#include <CPanorama.h>
#include “CHeapInfo.h”

struct CDataPane : CPanorama {
 void   IDataPane (CView *anEnclosure,
 CBureaucrat *aSupervisor,
 short aWidth, short aHeight,
 short aHEncl, short aVEncl,
 SizingOption aHSizing, SizingOption
 aVSizing);
 void   Draw (Rect *area);
 void   WriteWindow (heapEntry block);
 void   WriteGrafPort (heapEntry block);
 void   WriteControl (heapEntry block);
 void   WriteMenu (heapEntry block);
 void   WriteMenuList (heapEntry block);
 void   WriteResMap (heapEntry block,
 Rect *area);
 void   WritePString (heapEntry block);
 void   WriteCString (heapEntry block);
 void   WriteStringList (heapEntry block,
 Rect *area);
 void   WriteMPtrBlock (heapEntry block,
 Rect *area);
 void   WriteByte (heapEntry block, Rect *area);
 void   SetPaneSize (void);
};

typedef struct ResourceMap {
 char   resHeader[16];
 long   nextResMap;
 int    fileRefNum;
 int    attrs;
 int    typeListOffset;
 int    nameListOffset;
} ResourceMap;

typedef struct ResTypeDef {
 ResTyperesourceType;
 int    totalResources;
 int    refListOffset;
} ResTypeDef;

typedef struct ResourceTypeList {
 int    totalTypes;
 ResTypeDef typeDef[];
} ResourceTypeList;

typedef struct ResourceRefList {
 int    resourceID;
 int    nameOffset;
 long   refData;
 long   dataHandle;
} ResourceRefList;
Listing:  CDataPane.c

#include “CDataPane.h”
#include “CHeapInfo.h”
#include <DPMUtilities.h>

extern CHeapInfo *gHeap;

intlineHeight;
intlineCount;
FontInfoinfo;

void CDataPane::IDataPane (CView *anEnclosure,
 CBureaucrat *aSupervisor,
 short aWidth, short aHeight,
 short aHEncl, short aVEncl,
 SizingOption aHSizing, SizingOption
 aVSizing)
{
 CPanorama::IPanorama (anEnclosure, aSupervisor,
 aWidth, aHeight, aHEncl, aVEncl,
 aHSizing, aVSizing);
}

void CDataPane::SetPaneSize ()
{
 Rect   bounds;
 long   currentLine;
 heapEntryblock;
 long   nLines;
 Point  p;
 
 this->GetBounds (&bounds);
 
 currentLine = gHeap->GetLineSelected ();
 if (currentLine == 0) {
 bounds.bottom = 1;
 this->SetBounds (&bounds);
 return;
 }
 
 block = gHeap->GetBlock (currentLine);
 switch (block.dataType) {
 case BYTE:
 case REGION:
 case CODE:
 nLines = (block.physSize - 9) / 16 + 1;
 break;
 case WINDOW:
 nLines = 20;
 break;
 case GRAFPORT:
 nLines = 25;
 break;
 case MENULIST:
 nLines = 15;
 break;
 case MENU:
 nLines = 30;
 break;
 case RESMAP:
 nLines = 1000;
 break;
 case STR:
 nLines = 30;
 break;
 case MPTRBLOCK:
 nLines = 64;    /* could change */
 break;
 default:
 nLines = 1;
 break;
 }
 if (bounds.bottom != nLines) {
 bounds.bottom = nLines;
 this->SetBounds (&bounds);
 }
 p.v = 0;
 p.h = 0;
 this->ScrollTo (p, FALSE);
}

void CDataPane::Draw (Rect *area)
{
 GrafPtrcurrentPort;
 int    saveFont;
 heapHdrheapHeader;
 heapEntryblock;
 long   currentHeapLine;
 SignedByte currentState;
 Rect   bounds;
 
 currentHeapLine = gHeap->GetLineSelected ();
 if (currentHeapLine == 0)
 return;
 heapHeader = gHeap->GetHeader ();
 block = gHeap->GetBlock (currentHeapLine);
 
 if (block.dataType == GONE)
 return;
 
 GetPort (&currentPort);
 saveFont = currentPort->txFont;
 TextFont (monaco);
 TextSize (9);
 
 GetFontInfo (&info);
 lineHeight = info.ascent + info.descent + info.leading;
 lineCount = info.ascent + info.leading;
 
 if (block.type == RELOCATABLE)    /* lock it */
 {
 currentState = HGetState (block.mPtrAddress);
 HLock (block.mPtrAddress);
 }
 
 switch (block.dataType) {
 case WINDOW:
 WriteWindow (block);
 break;
 case GRAFPORT:
 WriteGrafPort (block);
 break;
 case MPTRBLOCK:
 WriteMPtrBlock (block, area);
 break;
 case CONTROL:
 WriteControl (block);
 break;
 case PSTRING:
 WritePString (block);
 break;
 case CSTRING:
 WriteCString (block);
 break;
 case MENU:
 WriteMenu (block);
 break;
 case MENULIST:
 WriteMenuList (block);
 break;
 case RESMAP:
 WriteResMap (block, area);
 break;
 case STR:
 WriteStringList (block, area);
 break;
 default:
 WriteByte (block, area);
 break;
 }
 TextFont (saveFont);
 if (block.type == RELOCATABLE) /* restore */
 HSetState (block.mPtrAddress, currentState);
}

void CDataPane::WriteWindow (heapEntry block)
{
 WindowPeek wPeek;
 char   *p;
 int    length;
 ControlHandle cHdl;
 int    h1 = 100;
 int    h2 = 280;
 Rect   *rect1;
 
 wPeek = (WindowPeek) block.address;

 Write (h1, lineCount, “windowKind :”,
 (long) wPeek->windowKind, 8);

 Write (h2, lineCount, “title :”, 0, 0);
 Move (6, 0);
 p = (char*) *(wPeek->titleHandle);
 length = *p++;
 DrawText (p, 0, length);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “titleWidth :”,
 (long) wPeek->titleWidth, 8);
 
 Write (h2, lineCount, “titleHandle :”, 0, 0);
 WriteHandle ((Handle)wPeek->titleHandle, 5, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “visible :”, 0, 0);
 WriteBoolean (wPeek->visible, 5, 0);
 
 Write (h2, lineCount, “strucRgn :”, 0, 0);
 WriteHandle ((Handle)wPeek->strucRgn, 5, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “hilited :”, 0, 0);
 WriteBoolean (wPeek->hilited, 5, 0);
 
 Write (h2, lineCount, “contRgn :”, 0, 0);
 WriteHandle ((Handle)wPeek->contRgn, 5, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “goAwayFlag :”, 0, 0);
 WriteBoolean (wPeek->goAwayFlag, 5, 0);
 
 Write (h2, lineCount, “updateRgn :”, 0, 0);
 WriteHandle ((Handle)wPeek->updateRgn, 5, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “zoom box :”, 0, 0);
 WriteBoolean (wPeek->spareFlag, 5, 0);
 
 Write (h2, lineCount, “dataHandle :”, 0, 0);
 WriteHandle ((Handle)wPeek->dataHandle, 5, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “nextWindow :”, 0, 0);
 WriteAddress ((Ptr)wPeek->nextWindow, 5, 0);
 
 Write (h2, lineCount, “windowPic :”, 0, 0);
 WriteHandle ((Handle)wPeek->windowPic, 5, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “userState :”, 0, 0);
 
 if (wPeek->spareFlag) {
 HLock (wPeek->dataHandle);
 rect1 = *(Rect**)(wPeek->dataHandle);
 WriteRect (rect1, 5, 0);
 }
 
 Write (h2, lineCount, “wDefProc :”, 0, 0);
 WriteHandle ((Handle)((long)
 wPeek->windowDefProc & 0x00FFFFFF), 5, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “stdState :”, 0, 0);
 
 if (wPeek->spareFlag) {
 ++rect1;
 WriteRect (rect1, 5, 0);
 HUnlock (wPeek->dataHandle);
 }
 Write (h2, lineCount, “RefCon :”, 0, 0);
 WriteHandle ((Handle)wPeek->refCon, 5, 0);
 lineCount += lineHeight;
 
 cHdl = wPeek->controlList;
 while (cHdl != 0L) {
 Write (h1, lineCount, “Control :”, 0, 0);
 WriteHandle ((Handle) cHdl, 5, 10);
 
 p = (char*) (*cHdl)->contrlTitle;
 length = *p++;
 DrawText (p, 0, length);
 
 cHdl = (*cHdl)->nextControl;
 lineCount += lineHeight;
 }
}

void CDataPane::WriteGrafPort (heapEntry block)
{
 GrafPtrthePort;
 int    h1 = 100;
 int    h2 = 250;
 
 thePort = (GrafPtr) block.address;
 
 Write (h1, lineCount, “device :”, thePort->device, 3);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “portBits :”, 0, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “portRect :”, 0, 0);
 WriteRect (&thePort->portRect, 6, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “visRgn :”, 0, 0);
 WriteHandle ((Handle)thePort->visRgn, 5, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “clipRgn :”, 0, 0);
 WriteHandle ((Handle)thePort->clipRgn, 5, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “bkPat :”, 0, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “fillPat :”, 0, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “pnLoc :”, 0, 0);
 WritePoint (thePort->pnLoc, 6, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “pnSize :”, 0, 0);
 WritePoint (thePort->pnSize, 6, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “pnMode :”, thePort->pnMode, 5);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “pnPat :”, 0, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “pnVis :”, thePort->pnVis, 5);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “txFont :”, thePort->txFont, 5);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “txFace :”, 0, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “txMode :”, thePort->txMode, 5);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “txSize :”, thePort->txSize, 5);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “spExtra :”, 0, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “fgColor :”, thePort->fgColor, 5);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “bkColor :”, thePort->bkColor, 5);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “colrBit :”, thePort->colrBit, 5);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “patStretch :”,
 thePort->patStretch, 5);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “picSave :”, 0, 0);
 WriteHandle ((Handle)thePort->picSave, 5, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “rgnSave :”, 0, 0);
 WriteHandle ((Handle)thePort->rgnSave, 5, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “polySave :”, 0, 0);
 WriteHandle ((Handle)thePort->polySave, 5, 0);
 lineCount += lineHeight;
 
 Write (h1, lineCount, “grafProcs :”, 0, 0);
 lineCount += lineHeight;
}

void CDataPane::WriteControl (heapEntry block)
{
 ControlHandle cHdl;
 int    h = 100;
 char   *p;
 int    length;
 WindowPeek wPeek;
 
 cHdl = (ControlHandle) block.mPtrAddress;
 Write (h, lineHeight * 1, “Owner :”,
 (long) (*cHdl)->contrlOwner, 8);

 Move (10, 0);
 wPeek = (WindowPeek) (*cHdl)->contrlOwner;
 p = (char*) *(wPeek->titleHandle);
 length = *p++;
 DrawText (p, 0, length);
 DrawText (“ window”, 0, 7);
 
 Write (h, lineHeight * 2, “Min value :”,
 (*cHdl)->contrlMin, 8);
 Write (h, lineHeight * 3, “Max value :”,
 (*cHdl)->contrlMax, 8);
 Write (h, lineHeight * 4, “Current value :”,
 (*cHdl)->contrlValue, 8);
 Write (h, lineHeight * 5, “Title :”, 0, 0);

 Move (5, 0);
 p = (char*) (*cHdl)->contrlTitle;
 length = *p++;
 DrawText (p, 0, length);
}

#define TAB1100
#define TAB2300
#define TAB320

void CDataPane::WriteResMap (heapEntry block, Rect *area)
{
 int    length;
 char   *p;
 int    i, j;
 Rect   drawRect;
 Rect   tempRect;
 ResourceMap*resMap;
 ResourceTypeList*resList;
 ResourceRefList *refList;

 resMap = (ResourceMap*)StripAddress (*block.mPtrAddress);

 SetRect (&drawRect, 0, 0, width, lineHeight * 3);
 if (SectRect (&drawRect, area, &tempRect))
 {
 Write (TAB1, lineCount, “next resMap :”, 0, 0);
 WriteHandle ((Handle)resMap->nextResMap, 5, 0);
 lineCount += lineHeight;
 
 Write (TAB1, lineCount, “file Ref :”,
 resMap->fileRefNum, 8);
 lineCount += lineHeight;
 
 Write (TAB1, lineCount, “attributes :”,
 resMap->attrs, 8);
 
 lineCount = info.ascent + info.leading;
 Write (TAB2, lineCount, “type offset :”,
 resMap->typeListOffset, 8);
 lineCount += lineHeight;
 
 Write (TAB2, lineCount, “res offset :”,
 resMap->nameListOffset, 8);
 lineCount += lineHeight;
 
 resList = (ResourceTypeList*)((char*) resMap +
 resMap->typeListOffset);
 Write (TAB2, lineCount, “resTypes :”,
 resList->totalTypes + 1, 8);
 lineCount += lineHeight * 2;
 } else {
 resList = (ResourceTypeList*)((char*) resMap +
 resMap->typeListOffset);
 lineCount += lineHeight * 4;
 }
 
 SetRect (&drawRect, 0, lineCount - lineHeight,
 width, lineCount);
 
 for (i = 0; i <= resList->totalTypes; i++) {
 MoveTo (TAB3, lineCount);/* resource type */
 DrawText (&resList->typeDef[i].resourceType, 0, 4);
 refList = (ResourceRefList*)((char*)resMap +
 resMap->typeListOffset
 + resList->typeDef[i].refListOffset);
 for (j = 0; j<= resList->typeDef[i].totalResources; j++)
 {
 if (SectRect (&drawRect, area, &tempRect)) {
 Write (TAB3 + 40, lineCount, “:”,
 refList->resourceID, 8);
 if (refList->nameOffset != -1) {
 Move (120, 0);
 p = (char*)resMap +
 resMap->nameListOffset + refList->nameOffset;
 length = *p++;
 DrawText (p, 0, length);
 }
 if (refList->dataHandle != 0) {
 MoveTo (TAB3 + 100, lineCount);
 WriteHandle ((Handle)refList->dataHandle, 0, 0);
 }
 }
 
 lineCount += lineHeight;
 OffsetRect (&drawRect, 0, lineHeight);
 ++refList;
 }
 if (drawRect.top > area->bottom)
 break;
 }
}

void CDataPane::WriteMenu (heapEntry block)
{
 int    h1 = 100;
 int    h2 = 200;
 char   *p;
 int    length;
 MenuInfo *menu;
 int    i;
 
 menu = (MenuInfo *) StripAddress (*block.mPtrAddress);
 Write (h1, lineCount, “Menu title :”, 0, 0);
 
 p = (char*) menu->menuData;
 Move (5, 0);
 length = *p++;
 DrawText (p, 0, length);
 p += length;
 lineCount += lineHeight;
 
 Write (h1, lineCount, “menuID :”, menu->menuID, 4);
 lineCount += lineHeight;
 Write (h1, lineCount, “menuWidth :”, menu->menuWidth, 4);
 lineCount += lineHeight;
 Write (h1, lineCount, “menuHeight :”, menu->menuHeight, 4);
 lineCount += lineHeight;
 Write (h1, lineCount, “menuProc :”,
 (long) menu->menuProc, 8);
 lineCount += lineHeight;
 Write (h1, lineCount, “enableFlags :”,
 menu->enableFlags, 8);
 lineCount += lineHeight;
 
 lineCount = info.ascent + info.leading;
 while (*p != 0) {
 MoveTo (h2, lineCount);
 length = *p++;
 DrawText (p, 0, length);
 p += length;
 
 for (i = 0; i < 4; i++) {
 MoveTo (h2 + 100 + i * 10, lineCount);
 DrawChar (*p++);
 }
 lineCount += lineHeight;
 }
}

void CDataPane::WriteMenuList (heapEntry block)
{
 int    h = 100;
 char   *p;
 int    length;
 int    *intP;
 long   *longP;
 int    totMenus;
 int    i;
 MenuInfo *menu;
 
 intP = (int *) StripAddress (*block.mPtrAddress);
 totMenus = *intP / 6;
 
 Write (h, lineCount, “lastMenu :”, (long) *intP, 8);
 lineCount += lineHeight;
 
 ++intP;
 Write (h, lineCount, “lastRight :”, (long) *intP, 8);
 lineCount += lineHeight;
 
 ++intP;
 Write (h, lineCount, “mbResID :”, (long) *intP, 8);
 
 lineCount = info.ascent + info.leading;
 
 ++intP;
 for (i = 1; i <= totMenus; i++) {
 longP = (long*) intP;
 
 MoveTo (180, lineCount);
 WriteHandle ((Handle)*longP, 0, 20);
 
 menu = (MenuInfo *) StripAddress (*(long*)*longP);
 
 p = (char*) menu->menuData;
 length = *p++;
 DrawText (p, 0, length);
 
 lineCount += lineHeight;
 intP += 3;
 }
}

void CDataPane::WritePString (heapEntry block)
{
 char   *p;
 int    length;
 FontInfo info;
 
 Write (100, lineCount, “Pascal string :”, 0, 0);
 Move (5, 0);
 
 if (block.type == RELOCATABLE)
 p = (char*) StripAddress (*block.mPtrAddress);
 else
 p = (char*) block.address;
 
 length = *p++;
 DrawText (p, 0, length);
}

void CDataPane::WriteCString (heapEntry block)
{
 char   *p;
 char   *q;
 int    length = 0;
 
 Write (100, lineCount, “C string :”, 0, 0);
 Move (5, 0);
 
 if (block.type == RELOCATABLE)
 p = (char*) StripAddress (*block.mPtrAddress);
 else
 p = (char*) block.address;
 
 q = p;
 while (*q++ != 0)
 ++length;
 
 DrawText (p, 0, length);
}

void CDataPane::WriteStringList (heapEntry block, Rect *area)
{
 char   *p;
 int    length;
 int    totalStrings;
 int    i;
 
 Write (100, lineCount, “String list :”, 0, 0);
 
 p = (char*) StripAddress (*block.mPtrAddress);
 totalStrings = *(int*)p;
 p += 2;
 
 for (i = 1; i <= totalStrings; i++) {
 MoveTo (110, lineHeight * (i - 1) +
 info.ascent + info.leading);
 length = *p++;
 DrawText (p, 0, length);
 p += length;
 }
}

void CDataPane::WriteMPtrBlock (heapEntry block,
 Rect *area)
{
 long   *mPtr;
 int    i;
 int    firstLine;
 int    lastLine;
 Str255 text;
 long   lineNo;
 heapEntryblock2;
 THz    theZone;
 long   endAddress = (long)block.address +
 block.physSize;
 
 firstLine = area->top / lineHeight;
 lastLine = area->bottom / lineHeight + 1;
 
 mPtr = (long*)block.address;
 mPtr += firstLine;
 lineCount = firstLine * lineHeight + info.ascent
 + info.leading;
 
 for (i = firstLine; i < lastLine; i++) {
 NumToString (i + 1, text);
 MoveTo (30 - StringWidth (text), lineCount);
 DrawString (text);
 
 Move (20, 0);
 WriteHandle ((Handle) mPtr, 0, 0);
 
 if (gHeap->FindHandle ((Handle)mPtr, &lineNo)) {
 NumToString (lineNo, text);
 Move (35 - StringWidth (text), 0);
 DrawString (text);
 block2 = gHeap->GetBlock (lineNo);
 
 if (block2.dataType != BYTE) {
 Move (15, 0);
 gHeap->WriteDataType (lineNo);
 }
 } else {
 Move (15,0);
 if (*mPtr < (long)block.address || *mPtr > endAddress)
 if (gHeap->FindMPtrBlock ((long)*mPtr, &lineNo)) {
 DrawString (“\plink to line “);
 NumToString (lineNo, text);
 DrawString (text);
 }
 else
 DrawString (“\pdangling”);
 }
 ++mPtr;
 lineCount += lineHeight;
 }
}

void CDataPane::WriteByte (heapEntry block, Rect *area)
{
 int    totLines;
 char   *lineStart;
 char   *endData;
 long   firstLine;
 long   lastLine;
 register int  i;
 long   v;
 char   display[128];
 int    totBytes;
 int    maxLines;
 Str255 text;
 
 maxLines = (height - 1) / lineHeight + 1;

 if (block.type == RELOCATABLE)
 lineStart = (char*) StripAddress (*block.mPtrAddress);
 else
 lineStart = (char*) block.address;
 
 endData = lineStart + block.physSize - 8;
 totLines = ((endData - lineStart) - 1) / 16 + 1;
 if (totLines > maxLines)
 totLines = maxLines;

 firstLine = area->top / lineHeight;
 lastLine = firstLine + totLines;
 lineStart += (firstLine * 16);
 totBytes = 16;

 for (i = firstLine; i < lastLine &&
 lineStart < endData; i++) {
 v = i * lineHeight + info.ascent;
 if (endData - lineStart < 16)
 totBytes = endData - lineStart;
 MoveTo (10, v);
 FillLine (lineStart, display, totBytes);
 DrawString (display);
 lineStart += 16;
 }
}
Listing:  CHeapInfo.h

#define _H_CHeapInfo
#include <CObject.h>
#include <CBureaucrat.h>
#include <CList.h>

#define MAXBLOCKS300
#define MAXMASTERS 10

#define FREE0
#define NONRELOCATABLE    1
#define RELOCATABLE2

#define cmdRefresh 2000
#define cmdMaxMem2001
#define cmdCompactMem2002
#define cmdPurgeMem2003
#define cmdMoreMasters    2004
#define cmdMaxBlock2005
#define cmdPurgeSpace2006

#define cmdNewHandle 3000
#define cmdNewPointer3001
#define cmdDisposHandle   3002
#define cmdDisposPtr 3003

#define cmdToggleState    4000
#define cmdMoveHHi 4001
#define cmdTogglePurge    4002

#define cmdSystemHeap6000
#define cmdAppHeap16001

enum {BYTE = 5000, PSTRING, CSTRING, WINDOW,
 CONTROL, REGION, MENU, MENULIST, RESMAP,
 MPTRBLOCK, CODE, STR, GRAFPORT, USERH, USERP};
#define GONE5500

typedef struct blkHdr
{
 long   physSize;
 long   relHdl;
} blkHdr, *blkHdrPtr;

typedef struct mPtrBlock
{
 long   startAddress;
 long   endAddress;
} mPtrBlock;

typedef struct heapEntry
{
 long physSize;/* physical size of the block */
 int    type;    /* 0=free, 1=nonrel, 2=rel */
 Ptr    address; /* block’s address*/
 Handle mPtrAddress;/* block’s master ptr addr*/
 int    unused;  /* unused bytes   */
 int    dataType;/* display format */
 int    extra;   /* varies*/
} heapEntry;

typedef struct heapHdr
{
 THz  zonePtr;/* ptr to Application’s heap zone*/
 long totalBlocks; /* total blocks in the heap*/
 long lastUsed;  /* zone->bkLim*/
 long totalBUsed;/* bytes in heap  */
 long totalBFree; /* free bytes : zone->zcbFree*/
 long totalFree; /* free objects   */
 long totalRel;  /* relocatable objects*/
 long totalNonRel; /* nonrelocatable objects */
 long totalKnown;/* identified blocks*/
 long totalFreeBytes;/* bytes in free objects*/
 long totalRelBytes;/* bytes in rel objects*/
 long totalNonRelBytes;/* bytes in nonrel obj*/
 long lineSelected;/* selected display line */
 SignedByte selState;/* status flag byte for
 master pointer */
} heapHdr;

struct CHeapInfo : CBureaucrat
{
 heapEntryheapList[MAXBLOCKS];
 heapHdrheapHeader;
 mPtrBlockmList[MAXMASTERS];
 int    totalMasters;
 int    currentZone;
 CList  *itsHandles;
 CList  *itsPointers;
 
 void   IHeapInfo (CBureaucrat *aSupervisor,
 int whichHeap);
 void   Dispose (void);
 void   SetHeapZone (int zone);
 int    GetHeapZone (void);
 heapHdrGetHeader (void);
 heapEntryGetBlock (long entryNo);
 BooleanFindHandle (Handle address, long *lineNo);
 BooleanFindPointer (Ptr address, long *lineNo);
 BooleanFindMPtrBlock (long address, long *lineNo);
 BooleanHandleInHeap (Handle address);
 long   AddMPtrBlock (long address);
 long   GetLineSelected (void);
 void   SetLineSelected (long lineNo);
 void   WriteDataType (long lineNo);
 void   Refresh (Boolean tellSupervisor);
 void   DoCommand (long theCommand);
 void   UpdateMenus (void);
};
Listing:  CHeapInfo.c

#include <CBartender.h>
#include <CError.h>
#include <DPMUtilities.h>
#include <Constants.h>
#include “HeapConstants.h”
#include “CHeapInfo.h”
#include “CHeapListerDoc.h”
 
extern CHeapInfo *gHeap;
extern CursHandlegWatchCursor;
extern CBartender*gBartender;

void CHeapInfo::IHeapInfo (CBureaucrat
 *aSupervisor, int whichHeap)
{
 CBureaucrat::IBureaucrat (aSupervisor);
 
 itsHandles = new (CList);
 itsHandles->ICluster ();
 itsPointers = new (CList);
 itsPointers->ICluster ();
 
 SetHeapZone (whichHeap);
}

void CHeapInfo::Dispose ()
{
 long   totItems;
 Handle p;
 Ptr    q;
 
 totItems = itsHandles->GetNumItems ();
 while (totItems > 0) {
 p = (Handle)itsHandles->LastItem ();
 itsHandles->Remove ((CObject*)p);
 DisposHandle (p);
 --totItems;
 }
 itsHandles->Dispose ();
 
 totItems = itsPointers->GetNumItems ();
 while (totItems > 0) {
 q = (Ptr)itsPointers->LastItem ();
 itsPointers->Remove ((CObject*)q);
 DisposPtr (q);
 --totItems;
 }
 itsPointers->Dispose ();
 inherited::Dispose();
}

void CHeapInfo::SetHeapZone (int zone)
{
 currentZone = zone;
 Refresh (TRUE);
}

int CHeapInfo::GetHeapZone ()
{
 return (currentZone);
}

void CHeapInfo::Refresh (Boolean tellSupervisor)
{
 blkHdr *hdrPtr;
 unsigned char tagByte;
 long   size;
 long   blockAddr;
 long   blockHdl;
 int    unused;
 THz    theZone;
 long   totalBlocks;
 WindowPeek wPeek;
 long   i, j, k;
 ControlHandle cHdl;
 int    *intP;
 long   *longP;
 int    totMenus;
 long   trueHandle;
 long   *dummyPtr;
 int    max;
 long   blockAddress;
 ResourceMap**resMap;
 ResourceTypeList*resList;
 ResourceRefList *refList;
 int    theDataType;
 
 SetCursor (*gWatchCursor);
 
 heapHeader.totalBlocks = 0;
 heapHeader.totalBUsed = 0;
 heapHeader.totalFree = 0;
 heapHeader.totalRel = 0;
 heapHeader.totalNonRel = 0;
 heapHeader.totalKnown = 0;
 heapHeader.totalFreeBytes = 0;
 heapHeader.totalRelBytes = 0;
 heapHeader.totalNonRelBytes = 0;
 
 totalMasters = 0;
 
 SetLineSelected (0);
 
 if (currentZone == 0)
 heapHeader.zonePtr = SystemZone ();
 else
 heapHeader.zonePtr = GetZone ();
 
 theZone = heapHeader.zonePtr;
 
 hdrPtr = (blkHdr*) &theZone->heapData;
 heapHeader.totalBFree = theZone->zcbFree;
 heapHeader.lastUsed = (long) theZone->bkLim;
 
 while (hdrPtr < (blkHdr*) theZone->bkLim) {
 tagByte = hdrPtr->physSize >> 24;
 unused = tagByte & 0x0F;
 tagByte >>= 6;
 
 size = hdrPtr->physSize & 0x00FFFFFF;
 blockAddr = (long) (Ptr) hdrPtr + 8;
 theDataType = BYTE;
 
 switch (tagByte) {
 case RELOCATABLE:
 blockHdl = (long) (Ptr) theZone +
 hdrPtr->relHdl;
 ++heapHeader.totalRel;
 heapHeader.totalRelBytes += size;
 if (itsHandles->Includes((CObject*)blockHdl))
 theDataType = USERH;
 break;
 case NONRELOCATABLE:
 blockHdl = 0;
 ++heapHeader.totalNonRel;
 heapHeader.totalNonRelBytes += size;
 if (itsPointers->Includes((CObject*)blockAddr))
 theDataType = USERP;
 break;
 case FREE:
 blockHdl = 0;
 ++heapHeader.totalFree;
 heapHeader.totalFreeBytes += size;
 break;
 default:
 break;
 }
 
 totalBlocks = heapHeader.totalBlocks;
 
 if (totalBlocks < MAXBLOCKS) {
 heapList[totalBlocks].type = tagByte;
 heapList[totalBlocks].physSize = size;
 heapList[totalBlocks].address = (Ptr) blockAddr;
 heapList[totalBlocks].mPtrAddress = (Handle) blockHdl;
 heapList[totalBlocks].unused = unused;
 heapList[totalBlocks].dataType = theDataType;
 heapList[totalBlocks].extra = 0;
 
 if (theDataType != BYTE)
 ++heapHeader.totalKnown;
 }
 
 heapHeader.totalBUsed += size;
 ++heapHeader.totalBlocks;
 
 hdrPtr = (blkHdr*) ((Ptr) hdrPtr + size);
 }
/*
 * examine the window list
*/
 wPeek = (WindowPeek) FrontWindow ();
 while (wPeek != 0L) {
 if (FindPointer ((Ptr)wPeek, &i)) {
 heapList[i - 1].dataType = WINDOW;
 ++heapHeader.totalKnown;
 
 if (FindHandle ((Handle)wPeek->titleHandle, &i)) {
 heapList[i - 1].dataType = PSTRING;
 ++heapHeader.totalKnown;
 }
 
 if (FindHandle ((Handle)wPeek->strucRgn, &i)) {
 heapList[i - 1].dataType = REGION;
 ++heapHeader.totalKnown;
 }
 
 if (FindHandle ((Handle)wPeek->contRgn, &i)) {
 heapList[i - 1].dataType = REGION;
 ++heapHeader.totalKnown;
 }
 
 if (FindHandle ((Handle)wPeek->updateRgn, &i)) {
 heapList[i - 1].dataType = REGION;
 ++heapHeader.totalKnown;
 }
 
 cHdl = wPeek->controlList;
 while (cHdl != 0L) {
 if (FindHandle ((Handle)cHdl, &i)) {
 heapList[i - 1].dataType = CONTROL;
 ++heapHeader.totalKnown;
 }
 cHdl = (*cHdl)->nextControl;
 }
 }
 wPeek = wPeek->nextWindow;
 }
/*
 * examine the menu list
*/
 if (FindHandle (MenuList, &i)) {
 heapList[i - 1].dataType = MENULIST;
 ++heapHeader.totalKnown;
 }
 
 intP = (int*) *MenuList;
 totMenus = *intP / 6;
 
 for (i = 1; i <= totMenus; i++) {
 intP += 3;
 longP = (long *) intP;
 
 if (FindHandle ((Handle) *longP, &j)) {
 heapList[j - 1].dataType = MENU;
 ++heapHeader.totalKnown;
 }
 }
/*
 * examine the resource list
*/
 resMap = (ResourceMap**)TopMapHndl;
 
 do {
 if (HandleInHeap ((Handle)resMap)) {
 if (FindHandle ((Handle) resMap, &j)) {
 heapList[j - 1].dataType = RESMAP;
 ++heapHeader.totalKnown;
 resList = (ResourceTypeList*)((char*)
 *resMap + (*resMap)->typeListOffset);
 for (i = 0; i < resList->totalTypes; i++) {
 switch (resList->typeDef[i].resourceType) {
 case ‘CODE’:
 case ‘STR#’:
 case ‘STR ‘:
 refList = (ResourceRefList*)((char*)
 *resMap + (*resMap)->typeListOffset
 + resList->typeDef[i].refListOffset);
 for (j = 0; j <= resList->
 typeDef[i].totalResources; j++) {
 if (FindHandle ((Handle) refList->
 dataHandle, &k)) {
 switch (resList->
 typeDef[i].resourceType) {
 case ‘CODE’:
 heapList[k-1].dataType = CODE;
 heapList[k-1].extra =
 refList->resourceID;
 break;
 case ‘STR#’:
 heapList[k-1].dataType = STR;
 break;
 case ‘STR ‘:
 heapList[k-1].dataType = PSTRING;
 break;
 default:
 break;
 }
 ++heapHeader.totalKnown;
 }
 ++refList;
 }
 break;
 
 default:
 break;
 }
 }
 }
 }
 resMap = (ResourceMap**)(*resMap)->nextResMap;
 } while (resMap != 0L);
/*
 * Check out all the handles. First, follow the
 * linked list of available master pointers,
 * creating a list of master pointer blocks
 * with one or more free slots
*/
 longP = (long*)theZone->hFstFree;
 while (longP != 0L) {
 AddMPtrBlock ((long)longP);
 longP = (long*)*longP;
 }
/*
 * Then check each allocated handle in the heap.
 * This will give us a complete list of all master
 * pointer blocks, including those that are
 * completely used.
*/
 max = (heapHeader.totalBlocks >= MAXBLOCKS ?
 MAXBLOCKS : heapHeader.totalBlocks);
 
 for (i = 0; i < max; i++)
 if (heapList[i].type == RELOCATABLE)
 AddMPtrBlock ((long)heapList[i].mPtrAddress);
/*
 * Now loop through this new list and mark each
 * entry as a master pointer block.
*/
 for (i = 0; i < totalMasters; i++) {
 if (FindPointer ((Ptr)mList[i].startAddress, &j)) {
 heapList[j - 1].dataType = MPTRBLOCK;
 ++heapHeader.totalKnown;
 }
 }
/*
 * tell our supervisor that we’ve rebuilt the heap
*/
 if (tellSupervisor)
 itsSupervisor->DoCommand (cmdHeapRefreshed);
}

long CHeapInfo::AddMPtrBlock (long address)
{
 int    i;
 int    max;
/*
 * check our list of known master pointer blocks
*/
 for (i = 0; i < totalMasters; i++) {
 if (address >= mList[i].startAddress &&
 address <= mList[i].endAddress)
 return (mList[i].startAddress);
 }
/*
 * not known - search the heap and add it to the
 * list if we find it.
*/
 max = (heapHeader.totalBlocks >= MAXBLOCKS ?
 MAXBLOCKS : heapHeader.totalBlocks);
 
 for (i = 0; i < max; i++) {
 if (heapList[i].type == NONRELOCATABLE) {
 if (address >= (long)heapList[i].address
 && address <= (long)heapList[i].address +
 heapList[i].physSize) {
 mList[totalMasters].startAddress =
 (long)heapList[i].address;
 mList[totalMasters].endAddress =
 (long)heapList[i].address
  + heapList[i].physSize;
 ++totalMasters;
 return ((long)heapList[i].address);
 }
 }
 }
 return (0L);    /* failure - not in our heap */
}

Boolean CHeapInfo::FindMPtrBlock (long address,
 long *lineNo)
{
 int    i;
 
 *lineNo = 0;
/*
 * check our list of known master pointer blocks
*/
 for (i = 0; i < totalMasters; i++) {
 if (address >= mList[i].startAddress &&
 address < mList[i].endAddress) {
 FindPointer ((Ptr) mList[i].startAddress, lineNo);
 return (TRUE);
 }
 }
 return (FALSE);
}

Boolean CHeapInfo::FindHandle (Handle address, long *lineNo)
{
 long   j;
 long   *blockAddress;
 long   max;
 
 *lineNo = 0;
 if (address == 0L)
 return (FALSE);
 
 max = (heapHeader.totalBlocks >= MAXBLOCKS ?
 MAXBLOCKS : heapHeader.totalBlocks);
 
 for (j = 0; j < max; j++) {
 blockAddress = (long*)StripAddress (*heapList[j].mPtrAddress);

 if (blockAddress == (long*)StripAddress (*address))
 {
 *lineNo = j + 1;/* visible line number */
 return (TRUE);
 }
 }
 return (FALSE);
}

Boolean CHeapInfo::FindPointer (Ptr address, long *lineNo)
{
 long   j;
 long   hi, lo;
 
 if (address == 0L)
 return (FALSE);
 
 hi = (heapHeader.totalBlocks >= MAXBLOCKS ?
 MAXBLOCKS - 1 : heapHeader.totalBlocks - 1);
 lo = 0;
 
 do {
 j = (hi + lo) / 2;
 if (heapList[j].address == address) {
 *lineNo = j + 1;/* visible line number */
 return (TRUE);
 }
 else if (heapList[j].address > address)
 hi = j - 1;
 else
 lo = j + 1;
 } while (hi >= lo);
 return (FALSE);
}

Boolean CHeapInfo::HandleInHeap (Handle address)
{
 if ((address > (Handle) heapHeader.zonePtr)
 && (address < (Handle)heapHeader.lastUsed))
 return (TRUE);
 else
 return (FALSE);
}

void CHeapInfo::WriteDataType (long lineNo)
{
 Str255 text;
 
 switch (heapList[lineNo - 1].dataType) {
 case BYTE:
 DrawString (“\pByte”);
 break;
 case REGION:
 DrawString (“\pRegion”);
 break;
 case WINDOW:
 DrawString (“\pWindow”);
 break;
 case GRAFPORT:
 DrawString (“\pGrafPort”);
 break;
 case MENULIST:
 DrawString (“\pMenu list”);
 break;
 case MENU:
 DrawString (“\pMenu”);
 break;
 case RESMAP:
 DrawString (“\pResource map”);
 break;
 case CONTROL:
 DrawString (“\pControl record”);
 break;
 case PSTRING:
 DrawString (“\pPascal string”);
 break;
 case CSTRING:
 DrawString (“\pC string”);
 break;
 case MPTRBLOCK:
 DrawString (“\pMaster ptr block”);
 break;
 case CODE:
 DrawString (“\pCode “);
 NumToString (heapList[lineNo - 1].extra, text);
 DrawString (text);
 break;
 case STR:
 DrawString (“\pString List”);
 break;
 case USERH:
 DrawString (“\pUser handle”);
 break;
 case USERP:
 DrawString (“\pUser pointer”);
 break;
 case GONE:
 DrawString (“\pReclaimed”);
 break;
 default:
 DrawString (“\pUnknown”);
 break;
 }
}

heapHdr CHeapInfo::GetHeader ()
{
 return (heapHeader);
}

heapEntry CHeapInfo::GetBlock (long blockNo)
{
 long   lineNo;
 Size   ptrSize;
 
 if (heapList[--blockNo].dataType != GONE) {
 if (heapList[blockNo].type == RELOCATABLE) {
 if (FindMPtrBlock
 ((long)*heapList[blockNo].mPtrAddress, &lineNo)) {
 heapList[blockNo].dataType = GONE;
 itsSupervisor->DoCommand
 (cmdRefreshHeapLine);
 }
 }
 if (heapList[blockNo].type == NONRELOCATABLE) {
 ptrSize = GetPtrSize (heapList[blockNo].address);
 if (ptrSize == 0) {
 heapList[blockNo].dataType = GONE;
 itsSupervisor->DoCommand (cmdRefreshHeapLine);
 }
 }
 }
 return (heapList[blockNo]);
}

long CHeapInfo::GetLineSelected (void)
{
 return (heapHeader.lineSelected);
}

void CHeapInfo::SetLineSelected (long selLine)
{
 heapHeader.lineSelected = selLine;
}

void CHeapInfo::DoCommand (long theCommand)
{
 Size   size;
 Size   grow;
 long   contig;
 Handle p;
 Ptr    q;
 char   text1[10];
 char   text2[10];
 long   lineNo;
 long   currentLine = heapHeader.lineSelected - 1;
 unsigned char mPtrFlags;
 
 switch (theCommand) {
 case cmdRefresh:
 this->Refresh (TRUE);
 break;
 case cmdMaxMem:
 size = MaxMem (&grow);
 this->Refresh (TRUE);
 NumToString (size, text1);
 NumToString (grow, text2);
 ParamText (“\pLargest “, text1,
 “\p. Can grow “, text2);
 InitCursor ();
 Alert (ALRTgeneral, NULL);
 break;
 case cmdCompactMem:
 size = CompactMem (99999);
 this->Refresh (TRUE);
 NumToString (size, text1);
 ParamText (“\pLargest “, text1, “\p”, “\p”);
 InitCursor ();
 Alert (ALRTgeneral, NULL);
 break;
 case cmdPurgeMem:
 PurgeMem (99999);
 this->Refresh (TRUE);
 break;
 case cmdMoreMasters:
 MoreMasters ();
 this->Refresh (TRUE);
 break;
 case cmdMaxBlock:
 size = MaxBlock ();
 NumToString (size, text1);
 ParamText (“\pLargest “, text1, “\p”, “\p”);
 InitCursor ();
 Alert (ALRTgeneral, NULL);
 break;
 case cmdPurgeSpace:
 PurgeSpace (&size, &contig);
 NumToString (size, text1);
 NumToString (contig, text2);
 ParamText (“\pTotal free space “, text1,
 “\p. Largest “, text2);
 InitCursor ();
 Alert (ALRTgeneral, NULL);
 break;
 case cmdNewHandle:
 p = NewHandleClear (HANDLESIZE);
 CheckAllocation (p);
 itsHandles->Add ((CObject*)p);
 HLock (p);
 StuffHex (*p, “\p44656E6973204D6F6C6F6E79”);
 HUnlock (p);
 this->Refresh (FALSE);
 if (FindHandle (p, &lineNo))
 heapHeader.lineSelected = lineNo;
 itsSupervisor->DoCommand(cmdHeapRefreshed);
 break;
 case cmdNewPointer:
 q = NewPtrClear (PTRSIZE);
 CheckAllocation (q);
 itsPointers->Add ((CObject*)q);
 StuffHex (q, “\p44656E6973204D6F6C6F6E79”);
 this->Refresh (FALSE);
 if (FindPointer (q, &lineNo))
 heapHeader.lineSelected = lineNo;
 itsSupervisor->DoCommand
 (cmdHeapRefreshed);
 break;
 case cmdDisposHandle:
 p = heapList[currentLine].mPtrAddress;
 itsHandles->Remove ((CObject*)p);
 DisposHandle (p);
 heapList[currentLine].dataType = GONE;
 itsSupervisor->DoCommand(cmdRefreshHeapLine);
 itsSupervisor->DoCommand(cmdRefreshData);
 break;
 case cmdDisposPtr:
 q = heapList[currentLine].address;
 itsPointers->Remove ((CObject*)q);
 DisposPtr (q);
 heapList[currentLine].dataType = GONE;
 itsSupervisor->DoCommand(cmdRefreshHeapLine);
 itsSupervisor->DoCommand(cmdRefreshData);
 break;
 case cmdToggleState:
 p = heapList[currentLine].mPtrAddress;
 mPtrFlags = HGetState
 (heapList[currentLine].mPtrAddress);
 if (mPtrFlags & 0x80)    /* locked */
 HUnlock (p);
 else
 HLock (p);
 itsSupervisor->DoCommand(cmdRefreshHeapLine);
 break;
 case cmdMoveHHi:
 p = heapList[currentLine].mPtrAddress;
 MoveHHi (p);
 this->Refresh (TRUE);
 if (FindHandle (p, &lineNo))
 heapHeader.lineSelected = lineNo;
 break;
 case cmdTogglePurge:
 p = heapList[currentLine].mPtrAddress;
 mPtrFlags = HGetState
 (heapList[currentLine].mPtrAddress);
 if (mPtrFlags & 0x40)    /* purgeable */
 HNoPurge (p);
 else
 HPurge (p);
 itsSupervisor->DoCommand(cmdRefreshHeapLine);
 break;
 case BYTE: case PSTRING: case CSTRING:
 case WINDOW:    case CONTROL:case REGION:
 case MENU: case MENULIST:case RESMAP:
 case MPTRBLOCK: case CODE: case STR:
 case GRAFPORT:  case USERH:case USERP:
 heapList[heapHeader.lineSelected -
 1].dataType = theCommand;
 itsSupervisor->DoCommand(cmdRefreshData);
 itsSupervisor->DoCommand(cmdRefreshHeapLine);
 break;
 case cmdSystemHeap:
 if (currentZone != 0)
 this->SetHeapZone (0);
 break;
 case cmdAppHeap1:
 if (currentZone != 1)
 this->SetHeapZone (1);
 break;
 default: itsSupervisor->DoCommand (theCommand);
 break;
 }
}

void CHeapInfo::UpdateMenus ()
{
 unsigned char mPtrFlags;
 long   currentLine = heapHeader.lineSelected - 1;
 Str255 text;
 
 inherited::UpdateMenus ();
 gBartender->EnableCmd (cmdRefresh);
 if (gHeap->GetHeapZone () > 0) {
 gBartender->EnableCmd (cmdMaxMem);
 gBartender->EnableCmd (cmdCompactMem);
 gBartender->EnableCmd (cmdPurgeMem);
 gBartender->EnableCmd (cmdMoreMasters);
 gBartender->EnableCmd (cmdMaxBlock);
 gBartender->EnableCmd (cmdPurgeSpace);
 gBartender->EnableCmd (cmdNewHandle);
 gBartender->EnableCmd (cmdNewPointer);
 if (currentLine >= 0) {
 if (heapList[currentLine].type == RELOCATABLE) {
 mPtrFlags = HGetState
 (heapList[currentLine].mPtrAddress);
 if (mPtrFlags & 0x80)    /* locked */
 {
 GetIndString (text, STRcommon, UNLOCKtext);
 gBartender->SetCmdText (cmdToggleState, text);
 } else {
 gBartender->EnableCmd (cmdMoveHHi);
 GetIndString (text, STRcommon, LOCKtext);
 gBartender->SetCmdText (cmdToggleState, text);
 }
 if (mPtrFlags & 0x40)    /* purgeable */
 {
 GetIndString (text, STRcommon, HNOPURGEtext);
 gBartender->SetCmdText (cmdTogglePurge, text);
 } else {
 GetIndString (text, STRcommon, HPURGEtext);
 gBartender->SetCmdText (cmdTogglePurge, text);
 }
 gBartender->EnableCmd (cmdTogglePurge);
 gBartender->EnableCmd (cmdToggleState);
 gBartender->EnableCmd (cmdDisposHandle);
 }
 if (heapList[currentLine].type == NONRELOCATABLE)
 gBartender->EnableCmd (cmdDisposPtr);
 }
 }
 
 if (currentLine > 0 &&
 heapList[currentLine].dataType != GONE) {
 gBartender->EnableCmd (BYTE);
 gBartender->EnableCmd (PSTRING);
 gBartender->EnableCmd (CSTRING);
 gBartender->EnableCmd (WINDOW);
 gBartender->EnableCmd (CONTROL);
 gBartender->EnableCmd (REGION);
 gBartender->EnableCmd (MENU);
 gBartender->EnableCmd (MENULIST);
 gBartender->EnableCmd (RESMAP);
 gBartender->EnableCmd (MPTRBLOCK);
 gBartender->EnableCmd (CODE);
 gBartender->EnableCmd (STR);
 gBartender->EnableCmd (GRAFPORT);
 gBartender->EnableCmd (USERH);
 gBartender->EnableCmd (USERP);
 
 gBartender->CheckMarkCmd (
 heapList[heapHeader.lineSelected -
 1].dataType, TRUE);
 }
 gBartender->EnableCmd (cmdSystemHeap);
 gBartender->EnableCmd (cmdAppHeap1);
 gBartender->CheckMarkCmd (currentZone +
 cmdSystemHeap, TRUE);
}


Continued in next frame
Volume Number:6
Issue Number:10
Column Tag:C Forum

Related Info: Memory Manager

HeapLister (code 2)

By Denis Molony, Sultanate of Oman


Listing:  CHeapListerPane.h

#define _H_CHeapListerPane
#include <CPanorama.h>
#include “CHeapInfo.h”

struct CHeapListerPane : CPanorama
{
 Cursor itsCursor;

 void   IHeapListerPane (CView *anEnclosure,
 CBureaucrat *aSupervisor,
 short aWidth, short aHeight,
 short aHEncl, short aVEncl,
 SizingOption aHSizing, SizingOption
 aVSizing);
 void   Draw (Rect *area);
 void   DrawLine (long lineNo);
 void   DoClick (Point hitPt, short modifierKeys,
 long when);
 void   SetPaneSize (void);
 void   ScrollToSelection (void);
};
Listing:  CHeapListerPane.c

#include “CHeapListerPane.h”
#include “CHeapInfo.h”
#include “CHeapListerDoc.h”
#include “HeapConstants.h”
#include “DPMUtilities.h”

extern CHeapInfo *gHeap;
extern shortgClicks;

void CHeapListerPane::IHeapListerPane
 (CView *anEnclosure,
 CBureaucrat *aSupervisor,
 short aWidth, short aHeight,
 short aHEncl, short aVEncl,
 SizingOption aHSizing,
 SizingOption aVSizing)
{
 CPanorama::IPanorama(anEnclosure, aSupervisor,
 aWidth, aHeight, aHEncl, aVEncl, aHSizing, aVSizing);
 wantsClicks = TRUE;
}

void CHeapListerPane::Draw (Rect *area)
{
 Str255 text;
 THz    zonePtr;
 blkHdr *hdrPtr;
 int    h1 = 5;
 int    h2 = 180;
 int    h3 = 310;
 int    h4 = 380;
 int    v = 50;
 heapEntryblock;
 long   trueHandle;
 int    firstLine;
 int    lastLine;
 long   currentLine;
 heapHdrheapHeader;
 GrafPtrcurrentPort;
 unsigned char mPtrFlags;
 long   selectedLine;
 Rect   inverseRect;
 FontInfo info;
 char   display[10];
 
 TextFont (monaco);
 TextSize (9);
 GetFontInfo (&info);
 
 heapHeader = gHeap->GetHeader ();

 firstLine = (area->top) / LINEHEIGHT - 1;
 if (firstLine < 1)
 firstLine = 1;
 
 lastLine = (area->bottom) / LINEHEIGHT + 1;
 if (lastLine > heapHeader.totalBlocks)
 lastLine = heapHeader.totalBlocks;
 if (lastLine > MAXBLOCKS)
 lastLine = MAXBLOCKS;
 
 selectedLine = gHeap->GetLineSelected ();
 
 for (currentLine = firstLine; currentLine <=
 lastLine; ++currentLine)  {
 block = gHeap->GetBlock (currentLine);
 
 v = (currentLine - 1) * LINEHEIGHT + info.ascent - 1;
 
 NumToString (currentLine, text);
 MoveTo (h1 + 25 - StringWidth (text), v);
 DrawString (text);
 
 NumToHex6 ((long)block.address, display);
 MoveTo (h1 + 40, v);
 DrawString (display);
 
 MoveTo (h1 + 90, v);
 switch (block.type) {
 case FREE:
 DrawString (“\pFree”);
 break;
 case NONRELOCATABLE:
 DrawString (“\pNonrel”);
 MoveTo (h1 - 2, v);
 DrawChar (‘*’);
 break;
 case RELOCATABLE:
 DrawString (“\pRel”);
 Move (30, 0);
 mPtrFlags = HGetState (block.mPtrAddress);
 
 if (mPtrFlags & 0x80)    /* locked */
 DrawChar (‘L’);
 else
 DrawChar (‘l’);
 
 if (mPtrFlags & 0x40)    /* purgeable */
 DrawChar (‘P’);
 else
 DrawChar (‘p’);
 
 if (mPtrFlags & 0x20)    /* resource */
 DrawChar (‘R’);
 else
 DrawChar (‘r’);
 
 if (mPtrFlags & 0x80)    /* locked */
 {
 MoveTo (h1 - 2, v);
 DrawChar (‘*’);
 }
 break;
 default:
 SysBeep (8);
 break;
 }
 
 NumToString (block.unused, text);
 MoveTo (h1 + 165, v);
 DrawString (text);
 
 NumToString (block.physSize - 8, text);
 MoveTo (h1 + 175, v);
 Move (40 - StringWidth (text), 0);
 DrawString (text);
 
 if (block.type == RELOCATABLE) {
 MoveTo (h1 + 225, v);
 if (WriteHandle (block.mPtrAddress, 0, 0) !=
 (Handle)block.address)
 DrawChar (‘+’);
 }
 
 if (block.dataType != BYTE) {
 MoveTo (h1 + 330, v);
 gHeap->WriteDataType (currentLine);
 }
 
 if (currentLine == selectedLine) {
 SetRect (&inverseRect, 0, (currentLine - 1) *
 LINEHEIGHT, width, currentLine * LINEHEIGHT - 1);
 InvertRect (&inverseRect);
 }
 }
}

void CHeapListerPane::DrawLine (long lineNo)
{
 Rect   area;
 FontInfo info;
 
 SetRect (&area, 0, (lineNo - 1) * LINEHEIGHT,
 width, lineNo * LINEHEIGHT - 1);
 inherited::RefreshRect (&area);
}

void CHeapListerPane::SetPaneSize ()
{
 Rect   bounds;
 heapHdrheader;
 long   totLines;
 Point  p;
 
 header = gHeap->GetHeader ();
 totLines = header.totalBlocks;
 
 if (totLines > MAXBLOCKS)
 totLines = MAXBLOCKS;
 
 this->GetPosition (&p);
 
 if (p.v > totLines - LINEHEIGHT) {
 p.v = totLines - LINESINMAINPANE;
 this->ScrollTo (p, FALSE);
 }
 
 this->GetBounds (&bounds);
 
 if (bounds.bottom != totLines) {
 bounds.bottom = totLines;
 this->SetBounds (&bounds);
 }
}

void CHeapListerPane::ScrollToSelection ()
{
 Point  p;
 long   currentLine;
 
 currentLine = gHeap->GetLineSelected ();
 if (currentLine == 0)
 return;
 
 p.h = 0;
 p.v = currentLine - (LINESINMAINPANE + 1) / 2;
 
 if (p.v > bounds.bottom - LINESINMAINPANE)
 p.v = bounds.bottom - LINESINMAINPANE;
 else if (p.v < 0)
 p.v = 0;
 
 this->ScrollTo (p, FALSE);
}

void CHeapListerPane::DoClick (Point hitPt, short modifierKeys, long 
when)

{
 long   currentLine;
 Rect   inverseRect;
 long   originalLine;
 long   newLine;
 Point  mouse;
 Point  panePosition;
 int    lineHeight;
 FontInfo info;
 GrafPtrcurrentPort;
 int    saveTextSize;
 heapHdrheapHeader;
 short  paneHeight, paneWidth;
 
 GetPort (&currentPort);
 saveTextSize = currentPort->txSize;
 
 TextSize (9);
 GetFontInfo (&info);
 lineHeight = info.ascent + info.descent +
 info.leading;
 TextSize (saveTextSize);
 
 heapHeader = gHeap->GetHeader ();
 
 originalLine = currentLine =
 gHeap->GetLineSelected ();
 
 while (StillDown ()) {
 GetMouse (&mouse);
 AutoScroll (mouse);
 
 newLine = mouse.v / lineHeight + 1;
 if (newLine < 0)
 newLine = 0;
 if (newLine > heapHeader.totalBlocks)
 newLine = 0;
 
 if (newLine != currentLine) {
 if (currentLine > 0) {
 SetRect (&inverseRect, 0, (currentLine - 1)
 * lineHeight, width, currentLine *
 lineHeight - 1);
 InvertRect (&inverseRect);
 }
 SetRect (&inverseRect, 0, (newLine - 1) *
 lineHeight, width, newLine *
 lineHeight - 1);
 InvertRect (&inverseRect);
 
 gHeap->SetLineSelected (newLine);
 currentLine = newLine;
 }
 }
 if (currentLine != originalLine)
 itsSupervisor->DoCommand (cmdRefreshData);
}
Listing:  HeapListerApp.h

#define _H_CHeapListerApp
#include <CApplication.h>

struct CHeapListerApp : CApplication
{
 void IHeapListerApp (void);
 void SetUpFileParameters (void);
 void DoCommand (long theCommand);
 void UpdateMenus (void);
 void SetUpMenus (void);
 void CreateDocument (void);
};
Listing:  HeapListerApp.c

#include <CBartender.h>
#include <Commands.h>
#include <Constants.h>
#include “CHeapListerApp.h”
#include “CHeapListerDoc.h”

extern  OSType   gSignature;
extern  CBartender *gBartender;

void CHeapListerApp::IHeapListerApp (void)
{
 CApplication::IApplication(4, 20480L, 2048L);
}

void CHeapListerApp::SetUpFileParameters (void)
{
 inherited::SetUpFileParameters();
 sfNumTypes = 1;
 sfFileTypes[0] = ‘TEXT’;
 gSignature = ‘*DPM’;
}

void CHeapListerApp::DoCommand (long theCommand)
{
 switch (theCommand) {
 case cmdAbout:
 ParamText (“\pHeapLister application - “,
 “\pwritten by Denis Molony “,
 “\pusing the Think Class Library”,
 “\p (March 1990)”);
 Alert (ALRTgeneral, NULL);
 break;
 
 default: inherited::DoCommand(theCommand);
 break;
 }
}

void CHeapListerApp::SetUpMenus ()
{
 inherited::SetUpMenus ();
 gBartender->SetUnchecking (21, TRUE);
 gBartender->SetUnchecking (22, TRUE);
 gBartender->SetUnchecking (23, TRUE);
}

void CHeapListerApp::UpdateMenus ()
{
 inherited::UpdateMenus ();
 gBartender->DisableCmd (cmdOpen);
}

void CHeapListerApp::CreateDocument ()
{
 CHeapListerDoc  *theDocument;
 
 theDocument = new(CHeapListerDoc);
 theDocument->IHeapListerDoc(this, TRUE);
 theDocument->NewFile();
}
Listing:  HeapListerDoc.h

#define _H_CHeapListerDoc
#include <CDocument.h>
#include “CHeapInfo.h”
#include “CHeaderPane.h”
#include “CHeapListerPane.h”
#include “CDataPane.h”

struct CHeapListerDoc : CDocument
{
 CHeaderPane*itsHeaderPane;
 CHeapInfo*itsHeap;
 CDataPane*itsDataPane;
 CHeapListerPane *itsHeapListerPane;

 void   IHeapListerDoc (CBureaucrat *aSupervisor,
 Boolean printable);
 void   Dispose (void);
 void   DoCommand (long theCommand);
 void   UpdateMenus (void);
 void   NewFile (void);
};

#define cmdHeapRefreshed  997
#define cmdRefreshHeapLine998
#define cmdRefreshData    999
Listing:  HeapListerDoc.c

#include <Global.h>
#include <Commands.h>
#include <CApplication.h>
#include <CBartender.h>
#include <CBorder.h>
#include <CDecorator.h>
#include <CDesktop.h>
#include <CError.h>
#include <CPanorama.h>
#include <CScrollPane.h>
#include <TBUtilities.h>
#include “CHeapListerDoc.h”
#include “CHeapListerPane.h”
#include “CHeaderPane.h”
#include “HeapConstants.h”

#define WINDHeapLister    500
extern  CApplication *gApplication;
extern  CBartender *gBartender;
extern  CDecorator *gDecorator;
extern  CDesktop *gDesktop;
extern  CBureaucrat*gGopher;
extern  OSType   gSignature;
extern  CError   *gError;
extern  CHeapInfo*gHeap;

void CHeapListerDoc::IHeapListerDoc (CBureaucrat
 *aSupervisor, Boolean printable)
{
 CDocument::IDocument(aSupervisor, printable);
}

void CHeapListerDoc::Dispose ()
{
 gHeap->Dispose ();
 inherited::Dispose();
}

void CHeapListerDoc::DoCommand (long theCommand)
{
 Size   size;
 long   currentLine;
 Point  p;
 
 switch (theCommand) {
 case cmdRefreshData:/* new line selected */
 itsDataPane->SetPaneSize ();
 itsDataPane->Refresh ();
 break;
 case cmdRefreshHeapLine:
 currentLine = gHeap->GetLineSelected ();
 itsHeapListerPane->DrawLine (currentLine);
 break;
 case cmdHeapRefreshed:
 if (itsWindow != NULL) {
 itsHeaderPane->Refresh ();
 currentLine = gHeap->GetLineSelected ();
 itsHeapListerPane->SetPaneSize ();
 if (currentLine > 0)
 itsHeapListerPane->ScrollToSelection ();
 itsHeapListerPane->Refresh ();
 itsDataPane->SetPaneSize ();
 itsDataPane->Refresh ();
 }
 break;
 default:
 inherited::DoCommand(theCommand);
 break;
 }
}

void CHeapListerDoc::UpdateMenus ()
{
 inherited::UpdateMenus ();
 gBartender->DisableCmd (cmdSaveAs);
}

void CHeapListerDoc::NewFile (void)
{
 FontInfo info;
 CHeapListerPane *theMainPane;
 CDataPane*theDataPane;
 CScrollPane*theScrollPane;
 CBorder*theBorder;
 GrafPtrcurrentPort;
 int    saveTextSize;
 int    saveFont;
 Rect   wAperture;
 int    i;
 heapHdrheader;
 long   totLines;
 
 gHeap = new (CHeapInfo);
 gHeap->IHeapInfo (this, 1);
 gGopher = gHeap;
 
 header = gHeap->GetHeader ();
 totLines = header.totalBlocks;
 if (totLines > MAXBLOCKS)
 totLines = MAXBLOCKS;

 itsWindow = new (CWindow);
 itsWindow->IWindow (WINDHeapLister, FALSE, gDesktop, this);
 itsWindow->GetAperture (&wAperture);
 
 itsHeaderPane = new (CHeaderPane);
 itsHeaderPane->IHeaderPane (itsWindow, this,
 50, 42, 0, 0,
 sizELASTIC, sizFIXEDTOP);
 itsHeaderPane->FitToEnclFrame (TRUE, FALSE);
 
 theScrollPane = new (CScrollPane);
 theScrollPane->IScrollPane (itsWindow, this,100,
 LINEHEIGHT * LINESINMAINPANE + 1,
 0, 42,sizELASTIC, sizFIXEDTOP,
 FALSE, TRUE, FALSE);
 theScrollPane->FitToEnclFrame (TRUE, FALSE);

 theMainPane = new (CHeapListerPane);
 itsMainPane = theMainPane;
 itsHeapListerPane = theMainPane;
 itsGopher = gHeap;

 theMainPane->IHeapListerPane (theScrollPane,
 this, 0, totLines, 0, 0, sizELASTIC, sizELASTIC);
 theMainPane->FitToEnclosure (TRUE, TRUE);
 theMainPane->SetScales (1, LINEHEIGHT);
 
 theScrollPane->InstallPanorama (theMainPane);

 theBorder = new (CBorder);
 theBorder->IBorder (itsWindow, this, 10, 2, 0,
 LINEHEIGHT * LINESINMAINPANE + 43,
 sizELASTIC, sizFIXEDTOP);
 theBorder->FitToEnclosure (TRUE, FALSE);

 theScrollPane = new (CScrollPane);
 theScrollPane->IScrollPane (itsWindow, this, 0,
 wAperture.bottom -
 (LINEHEIGHT * LINESINMAINPANE + 45),
 0, LINEHEIGHT * LINESINMAINPANE + 45,
 sizELASTIC, sizELASTIC,
 FALSE, TRUE, TRUE);
 theScrollPane->FitToEnclFrame (TRUE, FALSE);
 
 theDataPane = new (CDataPane);
 theDataPane->IDataPane (theScrollPane, this,
 0, 0, 0, 0, sizELASTIC, sizELASTIC);
 itsDataPane = theDataPane;
 theDataPane->SetScales (1, LINEHEIGHT);
 theScrollPane->SetSteps (1, 1);
 theDataPane->FitToEnclosure (TRUE, TRUE);
 theScrollPane->InstallPanorama (theDataPane);
 itsWindow->Select ();
}
Listing:  HeapLister.c

#include “CHeapListerApp.h”
#include “CHeapInfo.h”

extern CApplication*gApplication;
CHeapInfo *gHeap;

void main()
{
 gApplication = new(CHeapListerApp);
 ((CHeapListerApp*)gApplication)->
 IHeapListerApp();
 gApplication->Run();
 gApplication->Exit();
}
Listing:  RMaker text

!HeapLister.Π.rsrc

TYPE MENU
 ,20
Commands
Refresh#2000 /R
MaxMem#2001
CompactMem#2002
PurgeMem#2003
MoreMasters#2004
MaxBlock#2005
PurgeSpace#2006
(-
New Handle#3000 /H
New Pointer#3001 /P
DisposHandle#3002
DisposPtr#3003
(-
Lock handle#4000
MoveHHi#4001
HPurge#4002

 ,21
Data
Bytes#5000 /B
(-
Pascal string#5001
C string#5002
(-
Window record#5003
GrafPort#5012 /G
Control record#5004
(-
Region#5005
Menu#5006 /M
Master pointer block#5009
(-
User handle#5013
User pointer#5014

 ,22
Resources
Resource map#5008
Menu map#5007
Code#5010
String list#5011

 ,23
Zone
System heap#6000
Application#6001

TYPE WIND
 ,500
Application Heap
40,2,330,455
Invisible GoAway
0
0

TYPE STR#
 ,128
11
quitting
closing
Undo
Redo
Untitled
Show Clipboard
Hide Clipboard
Lock handle
Unlock handle
HPurge
HNopurge

 
AAPL
$105.22
Apple Inc.
+0.39
MSFT
$46.13
Microsoft Corpora
+1.11
GOOG
$539.78
Google Inc.
-4.20

MacTech Search:
Community Search:

Software Updates via MacUpdate

Ember 1.8.2 - Versatile digital scrapboo...
Ember (formerly LittleSnapper) is your digital scrapbook of things that inspire you: websites, photos, apps or other things. Just drag in images that you want to keep, organize them into relevant... Read more
Tonality Pro 1.1.2 - Professional-grade...
Tonality Pro gives you the power to create stunning and dramatic black & white images. This is a complete monochrome image editor with more than 150 one-click style presets, totally unique... Read more
VueScan 9.4.49 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
OS X Server 4.0 - For OS X 10.10 Yosemit...
Designed for OS X and iOS devices, OS X Server makes it easy to share files, schedule meetings, synchronize contacts, develop software, host your own website, publish wikis, configure Mac, iPhone,... Read more
TotalFinder 1.6.12 - Adds tabs, hotkeys,...
TotalFinder is a universally acclaimed navigational companion for your Mac. Enhance your Mac's Finder with features so smart and convenient, you won't believe you ever lived without them. Tab-based... Read more
BusyCal 2.6.3 - Powerful calendar app wi...
BusyCal is an award-winning desktop calendar that combines personal productivity features for individuals with powerful calendar sharing capabilities for families and workgroups. BusyCal's unique... Read more
calibre 2.7 - Complete e-library managem...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital... Read more
Skitch 2.7.3 - Take screenshots, annotat...
With Skitch, taking, annotating, and sharing screenshots or images is as fun as it is simple.Communicate and collaborate with images using Skitch and its intuitive, engaging drawing and annotating... Read more
Delicious Library 3.3.2 - Import, browse...
Delicious Library allows you to import, browse, and share all your books, movies, music, and video games with Delicious Library. Run your very own library from your home or office using our... Read more
Art Text 2.4.8 - Create high quality hea...
Art Text is an OS X application for creating high quality textual graphics, headings, logos, icons, Web site elements, and buttons. Thanks to multi-layer support, creating complex graphics is no... Read more

Latest Forum Discussions

See All

Rami Ismail Opens Up distribute​() for D...
Rami Ismail Opens Up distribute​() for Developers Posted by Jessica Fisher on October 24th, 2014 [ permalink ] Rami Ismail, Chief Executive of Business and Development at indie game studio | Read more »
Great Hitman GO Goes on Sale and Gets Ne...
Great Hitman GO Goes on Sale and Gets New Update – Say That Three Times Fast Posted by Jessica Fisher on October 24th, 2014 [ permalink ] | Read more »
Rival Stars Basketball Review
Rival Stars Basketball Review By Jennifer Allen on October 24th, 2014 Our Rating: :: RESTRICTIVE BUT FUNUniversal App - Designed for iPhone and iPad Rival Stars Basketball is a fun mixture of basketball and card collecting but its... | Read more »
Rubicon Development Makes Over a Dozen o...
Rubicon Development Makes Over a Dozen of Their Games Free For This Weekend Only Posted by Jessica Fisher on October 24th, 2014 [ permalink ] | Read more »
I Am Dolphin Review
I Am Dolphin Review By Jennifer Allen on October 24th, 2014 Our Rating: :: NEARLY FIN-TASTICUniversal App - Designed for iPhone and iPad Swim around and eat nearly everything that moves in I Am Dolphin, a fun Ecco-ish kind of game... | Read more »
nPlayer looks to be the ultimate choice...
Developed by Newin Inc, nPlayer may seem like your standard video player – but is aiming to be the best in its field by providing high quality video play performance and support for a huge number of video formats and codecs. User reviews include... | Read more »
Fighting Fantasy: Caverns of the Snow Wi...
Fighting Fantasy: Caverns of the Snow Witch Review By Jennifer Allen on October 24th, 2014 Our Rating: :: CLASSY STORYTELLINGUniversal App - Designed for iPhone and iPad Fighting Fantasy: Caverns of the Snow Witch is a sterling... | Read more »
A Few Days Left (Games)
A Few Days Left 1.01 Device: iOS Universal Category: Games Price: $3.99, Version: 1.01 (iTunes) Description: Screenshots are in compliance to App Store's 4+ age rating! Please see App Preview for real game play! **Important: Make... | Read more »
Toca Boo (Education)
Toca Boo 1.0.2 Device: iOS Universal Category: Education Price: $2.99, Version: 1.0.2 (iTunes) Description: BOO! Did I scare you!? My name is Bonnie and my family loves to spook! Do you want to scare them back? Follow me and I'll... | Read more »
Intuon (Games)
Intuon 1.1 Device: iOS Universal Category: Games Price: $.99, Version: 1.1 (iTunes) Description: Join the battle with your intuition in a new hardcore game Intuon! How well do you trust your intuition? Can you find a needle in a... | Read more »

Price Scanner via MacPrices.net

Weekend sale: 13-inch 128GB MacBook Air for $...
Best Buy has the 2014 13-inch 1.4GHz 128GB MacBook Air on sale for $849.99, or $150 off MSRP, on their online store. Choose free home shipping or free local store pickup (if available). Price valid... Read more
Nimbus Note Cross=Platform Notes Utility
Nimbus Note will make sure you never forget or lose your valuable data again. Create and edit notes, save web pages, screenshots and any other type of data – and share it all with your friends and... Read more
NewerTech’s Snuglet Makes MagSafe 2 Power Con...
NewerTech has introduced the Snuglet, a precision-manufactured ring designed to sit inside your MagSafe 2 connector port, providing a more snug fit to prevent your power cable from unintentional... Read more
Apple Planning To Sacrifice Gross Margins To...
Digitimes Research’s Jim Hsiao says its analysts believe Apple is planning to sacrifice its gross margins to save its tablet business, which has recently fallen into decline. They project that Apple’... Read more
Who’s On Now? – First Instant-Connect Search...
It’s nighttime and your car has broken down on the side of the highway. You need a tow truck right away, so you open an app on your iPhone, search for the closest tow truck and send an instant... Read more
13-inch 2.5GHz MacBook Pro on sale for $949,...
Best Buy has the 13″ 2.5GHz MacBook Pro available for $949.99 on their online store. Choose free shipping or free instant local store pickup (if available). Their price is $150 off MSRP. Price is... Read more
Save up to $125 on Retina MacBook Pros
B&H Photo has the new 2014 13″ and 15″ Retina MacBook Pros on sale for up to $125 off MSRP. Shipping is free, and B&H charges NY sales tax only. They’ll also include free copies of Parallels... Read more
Apple refurbished Time Capsules available sta...
The Apple Store has certified refurbished Time Capsules available for up to $60 off MSRP. Apple’s one-year warranty is included with each Time Capsule, and shipping is free: - 2TB Time Capsule: $255... Read more
Textilus New Word, Notes and PDF Processor fo...
Textilus is new word-crunching, notes, and PDF processor designed exclusively for the iPad. I haven’t had time to thoroughly check it out yet, but it looks great and early reviews are positive.... Read more
WD My Passport Pro Bus-Powered Thunderbolt RA...
WD’s My Passport Pro RAID solution is powered by an integrated Thunderbolt cable for true portability and speeds as high as 233 MB/s. HighlightsOverviewSpecifications Transfer, Back Up And Edit In... Read more

Jobs Board

*Apple* Solutions Consultant - Apple Inc. (U...
…important role that the ASC serves is that of providing an excellent Apple Customer Experience. Responsibilities include: * Promoting Apple products and solutions Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Project Manager / Business Analyst, WW *Appl...
…a senior project manager / business analyst to work within our Worldwide Apple Fulfillment Operations and the Business Process Re-engineering team. This role will work Read more
*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.