next up previous contents index search.gif
Next: 7. The IPC unit. Up: Unit reference for Free Previous: 5. The GRAPH unit.

Subsections


6. The HEAPTRC unit.

This chapter describes the HEAPTRC unit for Free Pascal. It was written by Pierre Muller.

6.1 Purpose

The HEAPTRC unit can be used to debug your memory allocation/deallocation. It keeps track of the calls to getmem/freemem, and, implicitly, of New/Dispose statements.

When the program exits, or when you request it explicitly. It displays the total memory used, and then dumps a list of blocks that were allocated but not freed. It also displays where the memory was allocated.

If there are any inconsistencies, such as memory blocks being allocated or freed twice, or a memory block that is released but with wrong size, this will be displayed also.

The information that is stored/displayed can be customized using some constants.

6.2 Usage

All that you need to do is to include heaptrc in the uses clause of your program. Make sure that it is the first unit in the clause, otherwise memory allocated in initialization code of units that precede the heaptrc unit will not be accounted for, causing an incorrect memory usage report.

The following example shows how to use the heaptrc unit.

Example

Program heapex;

{ Program used to demonstrate the usage of heaptrc unit }

Uses heaptrc;

Var P1 : ^Longint;
    P2 : Pointer;
    I : longint;
        
begin
  New(P1);
  // causes previous allocation not to be de-allocated
  New(P1);
  Dispose(P1);
  For I:=1 to 10 do
    begin
    GetMem (P2,128);
    // When I is even, deallocate block. We loose 5 times 128
    // bytes this way.
    If (I mod 2) = 0 Then FreeMem(P2,128);
    end;  
  GetMem(P2,128);
  // This will provoke an error and a memory dump
  Freemem (P2,64);
end.
This is the memory dump shown when running this program:

Marked memory at 08052C48 invalid
Wrong size : 128 allocated 64 freed
  0x0804C29C
  0x080509E2
  0x080480A4
  0x00000000
Heap dump by heaptrc unit
13 memory blocks allocated : 1416/1424
6 memory blocks freed     : 708/712
7 unfreed memory blocks : 708
True heap size : 2097152
True free heap : 2096040
Should be : 2096104
Call trace for block 0x08052C48 size 128
  0x080509D6
  0x080480A4
Call trace for block 0x08052B98 size 128
  0x08050992
  0x080480A4
Call trace for block 0x08052AE8 size 128
  0x08050992
  0x080480A4
Call trace for block 0x08052A38 size 128
  0x08050992
  0x080480A4
Call trace for block 0x08052988 size 128
  0x08050992
  0x080480A4
Call trace for block 0x080528D8 size 128
  0x08050992
  0x080480A4
Call trace for block 0x080528A0 size 4
  0x08050961
  0x080480A4

6.3 Constants, Types and variables

The FillExtraInfoType is a procedural type used in the SetExtraInfo call.


\begin{listing}
type
FillExtraInfoType = procedure(p : pointer);
\end{listing}
The following typed constants allow to fine-tune the standard dump of the memory usage by DumpHeap:


\begin{listing}
const
tracesize = 8;
quicktrace : boolean = true;
HaltOnError : boolean = true;
keepreleased : boolean = false;
\end{listing}

Tracesize specifies how many levels of calls are displayed of the call stack during the memory dump. If you specify keepreleased:=True then half the TraceSize is reserved for the GetMem call stack, and the other half is reserved for the FreeMem call stack. For example, the default value of 8 will cause eight levels of call frames to be dumped for the getmem call if keepreleased is False. If KeepReleased is true, then 4 levels of call frames will be dumped for the GetMem call and 4 frames wil be dumped for the FreeMem call. If you want to change this value, you must recode the heaptrc unit.

Quicktrace determines whether the memory manager checks whether a block that is about to be released is allocated correctly. This is a rather time consuming search, and slows program execution significantly, so by default it is set to False.

If HaltOnError is set to True then an illegal call to FreeMem will cause the memory manager to execute a halt(1) instruction, causing a memory dump. By Default it is set to True.

If keepreleased is set to true, then a list of freed memory blocks is kept. This is useful if you suspect that the same memory block is released twice. However, this option is very memory intensive, so use it sparingly, and only when it's really necessary.

6.4 Functions and procedures


6.4.1 DumpHeap

Declaration
procedure DumpHeap;
Description
DumpHeap dumps to standard output a summary of memory usage. It is called automatically by the heaptrc unit when your program exits (by instaling an exit procedure), but it can be called at any time
Errors
None.
See also
MarkHeap


6.4.2 MarkHeap

Declaration
procedure MarkHeap;
Description
MarkHeap marks all memory blocks with a special signature. You can use this if you think that you corruped the memory.
Errors
None.
See also
DumpHeap


6.4.3 SetExtraInfo

Declaration
procedure SetExtraInfo( size : longint;func : FillExtraInfoType);
Description
You can use SetExtraInfo to store extra info in the blocks that the heaptrc unit reserves when tracing getmem calls. Size indicates the size (in bytes) that the trace mechanism should reserve for your extra information. For each call to getmem, func will be called, and passed a pointer to the memory reserved.

When dumping the memory summary, the extra info is shown as Longint values.

Errors
You can only call SetExtraInfo if no memroy has been allocated yet. If memory was already allocated prior to the call to SetExtraInfo, then an error will be displayed on standard error output, and a DumpHeap is executed.
See also
DumpHeap

Example

Program heapex;

{ Program used to demonstrate the usage of heaptrc unit }

Uses heaptrc;

Var P1 : ^Longint;
    P2 : Pointer;
    I : longint;
    Marker : Longint; 
 
Procedure SetMarker (P : pointer);

Type PLongint = ^Longint;

begin
  PLongint(P)^:=Marker;
end; 
    
Procedure  Part1;
        
begin
  // Blocks allocated here are marked with $FFAAFFAA = -5570646
  Marker := $FFAAFFAA;
  New(P1);
  New(P1);
  Dispose(P1);
  For I:=1 to 10 do
    begin
    GetMem (P2,128);
    If (I mod 2) = 0 Then FreeMem(P2,128);
    end;  
  GetMem(P2,128);
end;

Procedure  Part2;
        
begin
  // Blocks allocated here are marked with $FAFAFAFA = -84215046
  Marker := $FAFAFAFA;
  New(P1);
  New(P1);
  Dispose(P1);
  For I:=1 to 10 do
    begin
    GetMem (P2,128);
    If (I mod 2) = 0 Then FreeMem(P2,128);
    end;  
  GetMem(P2,128);
end;

begin
 SetExtraInfo(SizeOf(Marker),@SetMarker);
 Writeln ('Part 1');
 part1;
 Writeln('Part 2');
 part2;
end.



root
1999-06-10