summaryrefslogtreecommitdiff
path: root/rtl/inc/heaptrc.pp
diff options
context:
space:
mode:
Diffstat (limited to 'rtl/inc/heaptrc.pp')
-rw-r--r--rtl/inc/heaptrc.pp1223
1 files changed, 1223 insertions, 0 deletions
diff --git a/rtl/inc/heaptrc.pp b/rtl/inc/heaptrc.pp
new file mode 100644
index 0000000000..47ada78d2f
--- /dev/null
+++ b/rtl/inc/heaptrc.pp
@@ -0,0 +1,1223 @@
+{
+ $Id: heaptrc.pp,v 1.44 2005/04/04 15:16:26 peter Exp $
+ This file is part of the Free Pascal run time library.
+ Copyright (c) 1999-2000 by the Free Pascal development team.
+
+ Heap tracer
+
+ See the file COPYING.FPC, included in this distribution,
+ for details about the copyright.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+unit heaptrc;
+interface
+
+{ 1.0.x doesn't have good rangechecking for cardinals }
+{$ifdef VER1_0}
+ {$R-}
+{$endif}
+
+{$goto on}
+
+Procedure DumpHeap;
+Procedure MarkHeap;
+
+{ define EXTRA to add more
+ tests :
+ - keep all memory after release and
+ check by CRC value if not changed after release
+ WARNING this needs extremely much memory (PM) }
+
+type
+ tFillExtraInfoProc = procedure(p : pointer);
+ tdisplayextrainfoProc = procedure (var ptext : text;p : pointer);
+
+{ Allows to add info pre memory block, see ppheap.pas of the compiler
+ for example source }
+procedure SetHeapExtraInfo( size : ptrint;fillproc : tfillextrainfoproc;displayproc : tdisplayextrainfoproc);
+
+{ Redirection of the output to a file }
+procedure SetHeapTraceOutput(const name : string);
+
+const
+ { tracing level
+ splitted in two if memory is released !! }
+{$ifdef EXTRA}
+ tracesize = 16;
+{$else EXTRA}
+ tracesize = 8;
+{$endif EXTRA}
+ { install heaptrc memorymanager }
+ useheaptrace : boolean=true;
+ { less checking }
+ quicktrace : boolean=true;
+ { calls halt() on error by default !! }
+ HaltOnError : boolean = true;
+ { set this to true if you suspect that memory
+ is freed several times }
+{$ifdef EXTRA}
+ keepreleased : boolean=true;
+{$else EXTRA}
+ keepreleased : boolean=false;
+{$endif EXTRA}
+ { add a small footprint at the end of memory blocks, this
+ can check for memory overwrites at the end of a block }
+ add_tail : boolean = true;
+ { put crc in sig
+ this allows to test for writing into that part }
+ usecrc : boolean = true;
+
+
+implementation
+
+type
+ pptrint = ^ptrint;
+
+const
+ { allows to add custom info in heap_mem_info, this is the size that will
+ be allocated for this information }
+ extra_info_size : ptrint = 0;
+ exact_info_size : ptrint = 0;
+ EntryMemUsed : ptrint = 0;
+ { function to fill this info up }
+ fill_extra_info_proc : TFillExtraInfoProc = nil;
+ display_extra_info_proc : TDisplayExtraInfoProc = nil;
+ error_in_heap : boolean = false;
+ inside_trace_getmem : boolean = false;
+ { indicates where the output will be redirected }
+ { only set using environment variables }
+ outputstr : shortstring = '';
+
+type
+ pheap_extra_info = ^theap_extra_info;
+ theap_extra_info = record
+ check : cardinal; { used to check if the procvar is still valid }
+ fillproc : tfillextrainfoProc;
+ displayproc : tdisplayextrainfoProc;
+ data : record
+ end;
+ end;
+
+ { warning the size of theap_mem_info
+ must be a multiple of 8
+ because otherwise you will get
+ problems when releasing the usual memory part !!
+ sizeof(theap_mem_info = 16+tracesize*4 so
+ tracesize must be even !! PM }
+ pheap_mem_info = ^theap_mem_info;
+ theap_mem_info = record
+ previous,
+ next : pheap_mem_info;
+ size : ptrint;
+ sig : longword;
+{$ifdef EXTRA}
+ release_sig : longword;
+ prev_valid : pheap_mem_info;
+{$endif EXTRA}
+ calls : array [1..tracesize] of pointer;
+ exact_info_size : word;
+ extra_info_size : word;
+ extra_info : pheap_extra_info;
+ end;
+
+var
+ ptext : ^text;
+ ownfile : text;
+{$ifdef EXTRA}
+ error_file : text;
+ heap_valid_first,
+ heap_valid_last : pheap_mem_info;
+{$endif EXTRA}
+ heap_mem_root : pheap_mem_info;
+ getmem_cnt,
+ freemem_cnt : ptrint;
+ getmem_size,
+ freemem_size : ptrint;
+ getmem8_size,
+ freemem8_size : ptrint;
+
+
+{*****************************************************************************
+ Crc 32
+*****************************************************************************}
+
+var
+ Crc32Tbl : array[0..255] of longword;
+
+procedure MakeCRC32Tbl;
+var
+ crc : longword;
+ i,n : byte;
+begin
+ for i:=0 to 255 do
+ begin
+ crc:=i;
+ for n:=1 to 8 do
+ if odd(crc) then
+ crc:=(crc shr 1) xor $edb88320
+ else
+ crc:=crc shr 1;
+ Crc32Tbl[i]:=crc;
+ end;
+end;
+
+
+Function UpdateCrc32(InitCrc:longword;var InBuf;InLen:ptrint):longword;
+var
+ i : ptrint;
+ p : pchar;
+begin
+ p:=@InBuf;
+ for i:=1 to InLen do
+ begin
+ InitCrc:=Crc32Tbl[byte(InitCrc) xor byte(p^)] xor (InitCrc shr 8);
+ inc(p);
+ end;
+ UpdateCrc32:=InitCrc;
+end;
+
+Function calculate_sig(p : pheap_mem_info) : longword;
+var
+ crc : longword;
+ pl : pptrint;
+begin
+ crc:=cardinal($ffffffff);
+ crc:=UpdateCrc32(crc,p^.size,sizeof(ptrint));
+ crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(ptrint));
+ if p^.extra_info_size>0 then
+ crc:=UpdateCrc32(crc,p^.extra_info^,p^.exact_info_size);
+ if add_tail then
+ begin
+ { Check also 4 bytes just after allocation !! }
+ pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info)+p^.size;
+ crc:=UpdateCrc32(crc,pl^,sizeof(ptrint));
+ end;
+ calculate_sig:=crc;
+end;
+
+{$ifdef EXTRA}
+Function calculate_release_sig(p : pheap_mem_info) : longword;
+var
+ crc : longword;
+ pl : pptrint;
+begin
+ crc:=$ffffffff;
+ crc:=UpdateCrc32(crc,p^.size,sizeof(ptrint));
+ crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(ptrint));
+ if p^.extra_info_size>0 then
+ crc:=UpdateCrc32(crc,p^.extra_info^,p^.exact_info_size);
+ { Check the whole of the whole allocation }
+ pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info);
+ crc:=UpdateCrc32(crc,pl^,p^.size);
+ { Check also 4 bytes just after allocation !! }
+ if add_tail then
+ begin
+ { Check also 4 bytes just after allocation !! }
+ pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info)+p^.size;
+ crc:=UpdateCrc32(crc,pl^,sizeof(ptrint));
+ end;
+ calculate_release_sig:=crc;
+end;
+{$endif EXTRA}
+
+
+{*****************************************************************************
+ Helpers
+*****************************************************************************}
+
+procedure call_stack(pp : pheap_mem_info;var ptext : text);
+var
+ i : ptrint;
+begin
+ writeln(ptext,'Call trace for block $',hexstr(ptrint(pointer(pp)+sizeof(theap_mem_info)),8),' size ',pp^.size);
+ for i:=1 to tracesize do
+ if pp^.calls[i]<>nil then
+ writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
+ { the check is done to be sure that the procvar is not overwritten }
+ if assigned(pp^.extra_info) and
+ (pp^.extra_info^.check=$12345678) and
+ assigned(pp^.extra_info^.displayproc) then
+ pp^.extra_info^.displayproc(ptext,@pp^.extra_info^.data);
+end;
+
+
+procedure call_free_stack(pp : pheap_mem_info;var ptext : text);
+var
+ i : ptrint;
+begin
+ writeln(ptext,'Call trace for block at $',hexstr(ptrint(pointer(pp)+sizeof(theap_mem_info)),8),' size ',pp^.size);
+ for i:=1 to tracesize div 2 do
+ if pp^.calls[i]<>nil then
+ writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
+ writeln(ptext,' was released at ');
+ for i:=(tracesize div 2)+1 to tracesize do
+ if pp^.calls[i]<>nil then
+ writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
+ { the check is done to be sure that the procvar is not overwritten }
+ if assigned(pp^.extra_info) and
+ (pp^.extra_info^.check=$12345678) and
+ assigned(pp^.extra_info^.displayproc) then
+ pp^.extra_info^.displayproc(ptext,@pp^.extra_info^.data);
+end;
+
+
+procedure dump_already_free(p : pheap_mem_info;var ptext : text);
+begin
+ Writeln(ptext,'Marked memory at $',HexStr(ptrint(pointer(p)+sizeof(theap_mem_info)),8),' released');
+ call_free_stack(p,ptext);
+ Writeln(ptext,'freed again at');
+ dump_stack(ptext,get_caller_frame(get_frame));
+end;
+
+procedure dump_error(p : pheap_mem_info;var ptext : text);
+begin
+ Writeln(ptext,'Marked memory at $',HexStr(ptrint(pointer(p)+sizeof(theap_mem_info)),8),' invalid');
+ Writeln(ptext,'Wrong signature $',hexstr(p^.sig,8),' instead of ',hexstr(calculate_sig(p),8));
+ dump_stack(ptext,get_caller_frame(get_frame));
+end;
+
+{$ifdef EXTRA}
+procedure dump_change_after(p : pheap_mem_info;var ptext : text);
+ var pp : pchar;
+ i : ptrint;
+begin
+ Writeln(ptext,'Marked memory at $',HexStr(ptrint(pointer(p)+sizeof(theap_mem_info)),8),' invalid');
+ Writeln(ptext,'Wrong release CRC $',hexstr(p^.release_sig,8),' instead of ',hexstr(calculate_release_sig(p),8));
+ Writeln(ptext,'This memory was changed after call to freemem !');
+ call_free_stack(p,ptext);
+ pp:=pointer(p)+sizeof(theap_mem_info);
+ for i:=0 to p^.size-1 do
+ if byte(pp[i])<>$F0 then
+ Writeln(ptext,'offset',i,':$',hexstr(i,8),'"',pp[i],'"');
+end;
+{$endif EXTRA}
+
+procedure dump_wrong_size(p : pheap_mem_info;size : ptrint;var ptext : text);
+begin
+ Writeln(ptext,'Marked memory at $',HexStr(ptrint(pointer(p)+sizeof(theap_mem_info)),8),' invalid');
+ Writeln(ptext,'Wrong size : ',p^.size,' allocated ',size,' freed');
+ dump_stack(ptext,get_caller_frame(get_frame));
+ { the check is done to be sure that the procvar is not overwritten }
+ if assigned(p^.extra_info) and
+ (p^.extra_info^.check=$12345678) and
+ assigned(p^.extra_info^.displayproc) then
+ p^.extra_info^.displayproc(ptext,@p^.extra_info^.data);
+ call_stack(p,ptext);
+end;
+
+
+function is_in_getmem_list (p : pheap_mem_info) : boolean;
+var
+ i : ptrint;
+ pp : pheap_mem_info;
+begin
+ is_in_getmem_list:=false;
+ pp:=heap_mem_root;
+ i:=0;
+ while pp<>nil do
+ begin
+ if ((pp^.sig<>$DEADBEEF) or usecrc) and
+ ((pp^.sig<>calculate_sig(pp)) or not usecrc) and
+ (pp^.sig <>$AAAAAAAA) then
+ begin
+ writeln(ptext^,'error in linked list of heap_mem_info');
+ RunError(204);
+ end;
+ if pp=p then
+ is_in_getmem_list:=true;
+ pp:=pp^.previous;
+ inc(i);
+ if i>getmem_cnt-freemem_cnt then
+ writeln(ptext^,'error in linked list of heap_mem_info');
+ end;
+end;
+
+
+{*****************************************************************************
+ TraceGetMem
+*****************************************************************************}
+
+Function TraceGetMem(size:ptrint):pointer;
+var
+ allocsize,i : ptrint;
+ oldbp,
+ bp : pointer;
+ pl : pdword;
+ p : pointer;
+ pp : pheap_mem_info;
+begin
+ inc(getmem_size,size);
+ inc(getmem8_size,((size+7) div 8)*8);
+{ Do the real GetMem, but alloc also for the info block }
+ allocsize:=size+sizeof(theap_mem_info)+extra_info_size;
+ if add_tail then
+ inc(allocsize,sizeof(ptrint));
+ p:=SysGetMem(allocsize);
+ pp:=pheap_mem_info(p);
+ inc(p,sizeof(theap_mem_info));
+{ Create the info block }
+ pp^.sig:=$DEADBEEF;
+ pp^.size:=size;
+ pp^.extra_info_size:=extra_info_size;
+ pp^.exact_info_size:=exact_info_size;
+ {
+ the end of the block contains:
+ <tail> 4 bytes
+ <extra_info> X bytes
+ }
+ if extra_info_size>0 then
+ begin
+ pp^.extra_info:=pointer(pp)+allocsize-extra_info_size;
+ fillchar(pp^.extra_info^,extra_info_size,0);
+ pp^.extra_info^.check:=$12345678;
+ pp^.extra_info^.fillproc:=fill_extra_info_proc;
+ pp^.extra_info^.displayproc:=display_extra_info_proc;
+ if assigned(fill_extra_info_proc) then
+ begin
+ inside_trace_getmem:=true;
+ fill_extra_info_proc(@pp^.extra_info^.data);
+ inside_trace_getmem:=false;
+ end;
+ end
+ else
+ pp^.extra_info:=nil;
+ if add_tail then
+ begin
+ pl:=pointer(pp)+allocsize-pp^.extra_info_size-sizeof(ptrint);
+ pl^:=$DEADBEEF;
+ end;
+ { clear the memory }
+ fillchar(p^,size,#255);
+ { retrieve backtrace info }
+ bp:=get_caller_frame(get_frame);
+ for i:=1 to tracesize do
+ begin
+ pp^.calls[i]:=get_caller_addr(bp);
+ oldbp:=bp;
+ bp:=get_caller_frame(bp);
+ if (bp<oldbp) or (bp>(StackBottom + StackLength)) then
+ bp:=nil;
+ end;
+ { insert in the linked list }
+ if heap_mem_root<>nil then
+ heap_mem_root^.next:=pp;
+ pp^.previous:=heap_mem_root;
+ pp^.next:=nil;
+{$ifdef EXTRA}
+ pp^.prev_valid:=heap_valid_last;
+ heap_valid_last:=pp;
+ if not assigned(heap_valid_first) then
+ heap_valid_first:=pp;
+{$endif EXTRA}
+ heap_mem_root:=pp;
+ { must be changed before fill_extra_info is called
+ because checkpointer can be called from within
+ fill_extra_info PM }
+ inc(getmem_cnt);
+ { update the signature }
+ if usecrc then
+ pp^.sig:=calculate_sig(pp);
+ TraceGetmem:=p;
+end;
+
+
+{*****************************************************************************
+ TraceFreeMem
+*****************************************************************************}
+
+function TraceFreeMemSize(p:pointer;size:ptrint):ptrint;
+var
+ i,ppsize : ptrint;
+ bp : pointer;
+ pp : pheap_mem_info;
+{$ifdef EXTRA}
+ pp2 : pheap_mem_info;
+{$endif}
+ extra_size : ptrint;
+begin
+ inc(freemem_size,size);
+ inc(freemem8_size,((size+7) div 8)*8);
+ pp:=pheap_mem_info(p-sizeof(theap_mem_info));
+ ppsize:= size + sizeof(theap_mem_info)+pp^.extra_info_size;
+ if add_tail then
+ inc(ppsize,sizeof(ptrint));
+ if not quicktrace then
+ begin
+ if not(is_in_getmem_list(pp)) then
+ RunError(204);
+ end;
+ if (pp^.sig=$AAAAAAAA) and not usecrc then
+ begin
+ error_in_heap:=true;
+ dump_already_free(pp,ptext^);
+ if haltonerror then halt(1);
+ end
+ else if ((pp^.sig<>$DEADBEEF) or usecrc) and
+ ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
+ begin
+ error_in_heap:=true;
+ dump_error(pp,ptext^);
+{$ifdef EXTRA}
+ dump_error(pp,error_file);
+{$endif EXTRA}
+ { don't release anything in this case !! }
+ if haltonerror then halt(1);
+ exit;
+ end
+ else if pp^.size<>size then
+ begin
+ error_in_heap:=true;
+ dump_wrong_size(pp,size,ptext^);
+{$ifdef EXTRA}
+ dump_wrong_size(pp,size,error_file);
+{$endif EXTRA}
+ if haltonerror then halt(1);
+ { don't release anything in this case !! }
+ exit;
+ end;
+ { save old values }
+ extra_size:=pp^.extra_info_size;
+ { now it is released !! }
+ pp^.sig:=$AAAAAAAA;
+ if not keepreleased then
+ begin
+ if pp^.next<>nil then
+ pp^.next^.previous:=pp^.previous;
+ if pp^.previous<>nil then
+ pp^.previous^.next:=pp^.next;
+ if pp=heap_mem_root then
+ heap_mem_root:=heap_mem_root^.previous;
+ end
+ else
+ begin
+ bp:=get_caller_frame(get_frame);
+ for i:=(tracesize div 2)+1 to tracesize do
+ begin
+ pp^.calls[i]:=get_caller_addr(bp);
+ bp:=get_caller_frame(bp);
+ end;
+ end;
+ inc(freemem_cnt);
+ { clear the memory }
+ fillchar(p^,size,#240); { $F0 will lead to GFP if used as pointer ! }
+ { this way we keep all info about all released memory !! }
+ if keepreleased then
+ begin
+{$ifdef EXTRA}
+ { We want to check if the memory was changed after release !! }
+ pp^.release_sig:=calculate_release_sig(pp);
+ if pp=heap_valid_last then
+ begin
+ heap_valid_last:=pp^.prev_valid;
+ if pp=heap_valid_first then
+ heap_valid_first:=nil;
+ TraceFreememsize:=size;
+ exit;
+ end;
+ pp2:=heap_valid_last;
+ while assigned(pp2) do
+ begin
+ if pp2^.prev_valid=pp then
+ begin
+ pp2^.prev_valid:=pp^.prev_valid;
+ if pp=heap_valid_first then
+ heap_valid_first:=pp2;
+ TraceFreememsize:=size;
+ exit;
+ end
+ else
+ pp2:=pp2^.prev_valid;
+ end;
+{$endif EXTRA}
+ TraceFreememsize:=size;
+ exit;
+ end;
+ { release the normal memory at least }
+ i:=SysFreeMemSize(pp,ppsize);
+ { return the correct size }
+ dec(i,sizeof(theap_mem_info)+extra_size);
+ if add_tail then
+ dec(i,sizeof(ptrint));
+ TraceFreeMemSize:=i;
+end;
+
+
+function TraceMemSize(p:pointer):ptrint;
+var
+ pp : pheap_mem_info;
+begin
+ pp:=pheap_mem_info(p-sizeof(theap_mem_info));
+ TraceMemSize:=pp^.size;
+end;
+
+
+function TraceFreeMem(p:pointer):ptrint;
+var
+ l : ptrint;
+ pp : pheap_mem_info;
+begin
+ pp:=pheap_mem_info(p-sizeof(theap_mem_info));
+ l:=SysMemSize(pp);
+ dec(l,sizeof(theap_mem_info)+pp^.extra_info_size);
+ if add_tail then
+ dec(l,sizeof(ptrint));
+ { this can never happend normaly }
+ if pp^.size>l then
+ begin
+ dump_wrong_size(pp,l,ptext^);
+{$ifdef EXTRA}
+ dump_wrong_size(pp,l,error_file);
+{$endif EXTRA}
+ end;
+ TraceFreeMem:=TraceFreeMemSize(p,pp^.size);
+end;
+
+
+{*****************************************************************************
+ ReAllocMem
+*****************************************************************************}
+
+function TraceReAllocMem(var p:pointer;size:ptrint):Pointer;
+var
+ newP: pointer;
+ allocsize,
+ movesize,
+ i : ptrint;
+ bp : pointer;
+ pl : pdword;
+ pp : pheap_mem_info;
+ oldsize,
+ oldextrasize,
+ oldexactsize : ptrint;
+ old_fill_extra_info_proc : tfillextrainfoproc;
+ old_display_extra_info_proc : tdisplayextrainfoproc;
+begin
+{ Free block? }
+ if size=0 then
+ begin
+ if p<>nil then
+ TraceFreeMem(p);
+ p:=nil;
+ TraceReallocMem:=P;
+ exit;
+ end;
+{ Allocate a new block? }
+ if p=nil then
+ begin
+ p:=TraceGetMem(size);
+ TraceReallocMem:=P;
+ exit;
+ end;
+{ Resize block }
+ pp:=pheap_mem_info(p-sizeof(theap_mem_info));
+ { test block }
+ if ((pp^.sig<>$DEADBEEF) or usecrc) and
+ ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
+ begin
+ error_in_heap:=true;
+ dump_error(pp,ptext^);
+{$ifdef EXTRA}
+ dump_error(pp,error_file);
+{$endif EXTRA}
+ { don't release anything in this case !! }
+ if haltonerror then halt(1);
+ exit;
+ end;
+ { save info }
+ oldsize:=pp^.size;
+ oldextrasize:=pp^.extra_info_size;
+ oldexactsize:=pp^.exact_info_size;
+ if pp^.extra_info_size>0 then
+ begin
+ old_fill_extra_info_proc:=pp^.extra_info^.fillproc;
+ old_display_extra_info_proc:=pp^.extra_info^.displayproc;
+ end;
+ { Do the real ReAllocMem, but alloc also for the info block }
+ allocsize:=size+sizeof(theap_mem_info)+pp^.extra_info_size;
+ if add_tail then
+ inc(allocsize,sizeof(ptrint));
+ { Try to resize the block, if not possible we need to do a
+ getmem, move data, freemem }
+ if not SysTryResizeMem(pp,allocsize) then
+ begin
+ { get a new block }
+ newP := TraceGetMem(size);
+ { move the data }
+ if newP <> nil then
+ begin
+ movesize:=TraceMemSize(p);
+ {if the old size is larger than the new size,
+ move only the new size}
+ if movesize>size then
+ movesize:=size;
+ move(p^,newP^,movesize);
+ end;
+ { release p }
+ traceFreeMem(p);
+ { return the new pointer }
+ p:=newp;
+ traceReAllocMem := newp;
+ exit;
+ end;
+{ Recreate the info block }
+ pp^.sig:=$DEADBEEF;
+ pp^.size:=size;
+ pp^.extra_info_size:=oldextrasize;
+ pp^.exact_info_size:=oldexactsize;
+ { add the new extra_info and tail }
+ if pp^.extra_info_size>0 then
+ begin
+ pp^.extra_info:=pointer(pp)+allocsize-pp^.extra_info_size;
+ fillchar(pp^.extra_info^,extra_info_size,0);
+ pp^.extra_info^.check:=$12345678;
+ pp^.extra_info^.fillproc:=old_fill_extra_info_proc;
+ pp^.extra_info^.displayproc:=old_display_extra_info_proc;
+ if assigned(pp^.extra_info^.fillproc) then
+ pp^.extra_info^.fillproc(@pp^.extra_info^.data);
+ end
+ else
+ pp^.extra_info:=nil;
+ if add_tail then
+ begin
+ pl:=pointer(pp)+allocsize-pp^.extra_info_size-sizeof(ptrint);
+ pl^:=$DEADBEEF;
+ end;
+ { adjust like a freemem and then a getmem, so you get correct
+ results in the summary display }
+ inc(freemem_size,oldsize);
+ inc(freemem8_size,((oldsize+7) div 8)*8);
+ inc(getmem_size,size);
+ inc(getmem8_size,((size+7) div 8)*8);
+ { generate new backtrace }
+ bp:=get_caller_frame(get_frame);
+ for i:=1 to tracesize do
+ begin
+ pp^.calls[i]:=get_caller_addr(bp);
+ bp:=get_caller_frame(bp);
+ end;
+ { regenerate signature }
+ if usecrc then
+ pp^.sig:=calculate_sig(pp);
+ { return the pointer }
+ p:=pointer(pp)+sizeof(theap_mem_info);
+ TraceReAllocmem:=p;
+end;
+
+
+
+{*****************************************************************************
+ Check pointer
+*****************************************************************************}
+
+{$ifndef Unix}
+ {$S-}
+{$endif}
+
+{$ifdef go32v2}
+var
+ __stklen : longword;external name '__stklen';
+ __stkbottom : longword;external name '__stkbottom';
+ edata : longword; external name 'edata';
+{$endif go32v2}
+
+{$ifdef linux}
+var
+ etext: ptruint; external name '_etext';
+ edata : ptruint; external name '_edata';
+ eend : ptruint; external name '_end';
+{$endif}
+
+{$ifdef win32}
+var
+ sdata : ptruint; external name '__data_start__';
+ edata : ptruint; external name '__data_end__';
+ sbss : ptruint; external name '__bss_start__';
+ ebss : ptruint; external name '__bss_end__';
+{$endif}
+
+
+procedure CheckPointer(p : pointer);{$ifndef NOSAVEREGISTERS}saveregisters;{$endif}[public, alias : 'FPC_CHECKPOINTER'];
+var
+ i : ptrint;
+ pp : pheap_mem_info;
+{$ifdef go32v2}
+ get_ebp,stack_top : longword;
+ data_end : longword;
+{$endif go32v2}
+label
+ _exit;
+begin
+ if p=nil then
+ runerror(204);
+
+ i:=0;
+
+{$ifdef go32v2}
+ if ptruint(p)<$1000 then
+ runerror(216);
+ asm
+ movl %ebp,get_ebp
+ leal edata,%eax
+ movl %eax,data_end
+ end;
+ stack_top:=__stkbottom+__stklen;
+ { allow all between start of code and end of data }
+ if ptruint(p)<=data_end then
+ goto _exit;
+ { stack can be above heap !! }
+
+ if (ptruint(p)>=get_ebp) and (ptruint(p)<=stack_top) then
+ goto _exit;
+{$endif go32v2}
+
+ { I don't know where the stack is in other OS !! }
+{$ifdef win32}
+ { inside stack ? }
+ if (ptruint(p)>ptruint(get_frame)) and
+ (ptruint(p)<Win32StackTop) then
+ goto _exit;
+ { inside data ? }
+ if (ptruint(p)>=ptruint(@sdata)) and (ptruint(p)<ptruint(@edata)) then
+ goto _exit;
+
+ { inside bss ? }
+ if (ptruint(p)>=ptruint(@sbss)) and (ptruint(p)<ptruint(@ebss)) then
+ goto _exit;
+{$endif win32}
+
+{$ifdef linux}
+ { inside stack ? }
+ if (ptruint(p)>ptruint(get_frame)) and
+ (ptruint(p)<$c0000000) then //todo: 64bit!
+ goto _exit;
+ { inside data or bss ? }
+ if (ptruint(p)>=ptruint(@etext)) and (ptruint(p)<ptruint(@eend)) then
+ goto _exit;
+{$endif linux}
+
+ { first try valid list faster }
+
+{$ifdef EXTRA}
+ pp:=heap_valid_last;
+ while pp<>nil do
+ begin
+ { inside this valid block ! }
+ { we can be changing the extrainfo !! }
+ if (ptruint(p)>=ptruint(pp)+sizeof(theap_mem_info){+extra_info_size}) and
+ (ptruint(p)<=ptruint(pp)+sizeof(theap_mem_info)+extra_info_size+pp^.size) then
+ begin
+ { check allocated block }
+ if ((pp^.sig=$DEADBEEF) and not usecrc) or
+ ((pp^.sig=calculate_sig(pp)) and usecrc) or
+ { special case of the fill_extra_info call }
+ ((pp=heap_valid_last) and usecrc and (pp^.sig=$DEADBEEF)
+ and inside_trace_getmem) then
+ goto _exit
+ else
+ begin
+ writeln(ptext^,'corrupted heap_mem_info');
+ dump_error(pp,ptext^);
+ halt(1);
+ end;
+ end
+ else
+ pp:=pp^.prev_valid;
+ inc(i);
+ if i>getmem_cnt-freemem_cnt then
+ begin
+ writeln(ptext^,'error in linked list of heap_mem_info');
+ halt(1);
+ end;
+ end;
+ i:=0;
+{$endif EXTRA}
+ pp:=heap_mem_root;
+ while pp<>nil do
+ begin
+ { inside this block ! }
+ if (ptruint(p)>=ptruint(pp)+sizeof(theap_mem_info)+ptruint(extra_info_size)) and
+ (ptruint(p)<=ptruint(pp)+sizeof(theap_mem_info)+ptruint(extra_info_size)+ptruint(pp^.size)) then
+ { allocated block }
+ if ((pp^.sig=$DEADBEEF) and not usecrc) or
+ ((pp^.sig=calculate_sig(pp)) and usecrc) then
+ goto _exit
+ else
+ begin
+ writeln(ptext^,'pointer $',hexstr(ptrint(p),8),' points into invalid memory block');
+ dump_error(pp,ptext^);
+ runerror(204);
+ end;
+ pp:=pp^.previous;
+ inc(i);
+ if i>getmem_cnt then
+ begin
+ writeln(ptext^,'error in linked list of heap_mem_info');
+ halt(1);
+ end;
+ end;
+ writeln(ptext^,'pointer $',hexstr(ptrint(p),8),' does not point to valid memory block');
+ runerror(204);
+_exit:
+end;
+
+{*****************************************************************************
+ Dump Heap
+*****************************************************************************}
+
+procedure dumpheap;
+var
+ pp : pheap_mem_info;
+ i : ptrint;
+ ExpectedHeapFree : ptrint;
+{$ifdef HASGETFPCHEAPSTATUS}
+ status : TFPCHeapStatus;
+{$else HASGETFPCHEAPSTATUS}
+ status : THeapStatus;
+{$endif HASGETFPCHEAPSTATUS}
+begin
+ pp:=heap_mem_root;
+ Writeln(ptext^,'Heap dump by heaptrc unit');
+ Writeln(ptext^,getmem_cnt, ' memory blocks allocated : ',getmem_size,'/',getmem8_size);
+ Writeln(ptext^,freemem_cnt,' memory blocks freed : ',freemem_size,'/',freemem8_size);
+ Writeln(ptext^,getmem_cnt-freemem_cnt,' unfreed memory blocks : ',getmem_size-freemem_size);
+{$ifdef HASGETFPCHEAPSTATUS}
+ status:=SysGetFPCHeapStatus;
+{$else HASGETFPCHEAPSTATUS}
+ SysGetHeapStatus(status);
+{$endif HASGETFPCHEAPSTATUS}
+ Write(ptext^,'True heap size : ',status.CurrHeapSize);
+ if EntryMemUsed > 0 then
+ Writeln(ptext^,' (',EntryMemUsed,' used in System startup)')
+ else
+ Writeln(ptext^);
+ Writeln(ptext^,'True free heap : ',status.CurrHeapFree);
+ ExpectedHeapFree:=status.CurrHeapSize-(getmem8_size-freemem8_size)-
+ (getmem_cnt-freemem_cnt)*(sizeof(theap_mem_info)+extra_info_size)-EntryMemUsed;
+ If ExpectedHeapFree<>status.CurrHeapFree then
+ Writeln(ptext^,'Should be : ',ExpectedHeapFree);
+ i:=getmem_cnt-freemem_cnt;
+ while pp<>nil do
+ begin
+ if i<0 then
+ begin
+ Writeln(ptext^,'Error in heap memory list');
+ Writeln(ptext^,'More memory blocks than expected');
+ exit;
+ end;
+ if ((pp^.sig=$DEADBEEF) and not usecrc) or
+ ((pp^.sig=calculate_sig(pp)) and usecrc) then
+ begin
+ { this one was not released !! }
+ if exitcode<>203 then
+ call_stack(pp,ptext^);
+ dec(i);
+ end
+ else if pp^.sig<>$AAAAAAAA then
+ begin
+ dump_error(pp,ptext^);
+{$ifdef EXTRA}
+ dump_error(pp,error_file);
+{$endif EXTRA}
+ error_in_heap:=true;
+ end
+{$ifdef EXTRA}
+ else if pp^.release_sig<>calculate_release_sig(pp) then
+ begin
+ dump_change_after(pp,ptext^);
+ dump_change_after(pp,error_file);
+ error_in_heap:=true;
+ end
+{$endif EXTRA}
+ ;
+ pp:=pp^.previous;
+ end;
+end;
+
+
+procedure markheap;
+var
+ pp : pheap_mem_info;
+begin
+ pp:=heap_mem_root;
+ while pp<>nil do
+ begin
+ pp^.sig:=$AAAAAAAA;
+ pp:=pp^.previous;
+ end;
+end;
+
+
+{*****************************************************************************
+ AllocMem
+*****************************************************************************}
+
+function TraceAllocMem(size:ptrint):Pointer;
+begin
+ TraceAllocMem:=SysAllocMem(size);
+end;
+
+
+{*****************************************************************************
+ No specific tracing calls
+*****************************************************************************}
+
+{$ifdef HASGETFPCHEAPSTATUS}
+function TraceGetHeapStatus:THeapStatus;
+begin
+ TraceGetHeapStatus:=SysGetHeapStatus;
+end;
+
+function TraceGetFPCHeapStatus:TFPCHeapStatus;
+begin
+ TraceGetFPCHeapStatus:=SysGetFPCHeapStatus;
+end;
+{$else HASGETFPCHEAPSTATUS}
+procedure TraceGetHeapStatus(var status:THeapStatus);
+begin
+ SysGetHeapStatus(status);
+end;
+{$endif HASGETFPCHEAPSTATUS}
+
+
+{*****************************************************************************
+ Program Hooks
+*****************************************************************************}
+
+Procedure SetHeapTraceOutput(const name : string);
+var i : ptrint;
+begin
+ if ptext<>@stderr then
+ begin
+ ptext:=@stderr;
+ close(ownfile);
+ end;
+ assign(ownfile,name);
+{$I-}
+ append(ownfile);
+ if IOResult<>0 then
+ Rewrite(ownfile);
+{$I+}
+ ptext:=@ownfile;
+ for i:=0 to Paramcount do
+ write(ptext^,paramstr(i),' ');
+ writeln(ptext^);
+end;
+
+procedure SetHeapExtraInfo( size : ptrint;fillproc : tfillextrainfoproc;displayproc : tdisplayextrainfoproc);
+begin
+ { the total size must stay multiple of 8, also allocate 2 pointers for
+ the fill and display procvars }
+ exact_info_size:=size + sizeof(theap_extra_info);
+ extra_info_size:=((exact_info_size+7) div 8)*8;
+ fill_extra_info_proc:=fillproc;
+ display_extra_info_proc:=displayproc;
+end;
+
+
+{*****************************************************************************
+ Install MemoryManager
+*****************************************************************************}
+
+const
+ TraceManager:TMemoryManager=(
+ NeedLock : true;
+ Getmem : @TraceGetMem;
+ Freemem : @TraceFreeMem;
+ FreememSize : @TraceFreeMemSize;
+ AllocMem : @TraceAllocMem;
+ ReAllocMem : @TraceReAllocMem;
+ MemSize : @TraceMemSize;
+{$ifdef HASGETFPCHEAPSTATUS}
+ GetHeapStatus : @TraceGetHeapStatus;
+ GetFPCHeapStatus : @TraceGetFPCHeapStatus;
+{$else HASGETFPCHEAPSTATUS}
+ GetHeapStatus : @TraceGetHeapStatus;
+{$endif HASGETFPCHEAPSTATUS}
+ );
+
+
+procedure TraceInit;
+var
+{$ifdef HASGETFPCHEAPSTATUS}
+ initheapstatus : TFPCHeapStatus;
+{$else HASGETFPCHEAPSTATUS}
+ initheapstatus : THeapStatus;
+{$endif HASGETFPCHEAPSTATUS}
+begin
+{$ifdef HASGETFPCHEAPSTATUS}
+ initheapstatus:=SysGetFPCHeapStatus;
+{$else HASGETFPCHEAPSTATUS}
+ SysGetHeapStatus(initheapstatus);
+{$endif HASGETFPCHEAPSTATUS}
+ EntryMemUsed:=initheapstatus.CurrHeapUsed;
+ MakeCRC32Tbl;
+ SetMemoryManager(TraceManager);
+ ptext:=@stderr;
+ if outputstr <> '' then
+ SetHeapTraceOutput(outputstr);
+{$ifdef EXTRA}
+ Assign(error_file,'heap.err');
+ Rewrite(error_file);
+{$endif EXTRA}
+end;
+
+
+procedure TraceExit;
+begin
+ { no dump if error
+ because this gives long long listings }
+ { clear inoutres, in case the program that quit didn't }
+ ioresult;
+ if (exitcode<>0) and (erroraddr<>nil) then
+ begin
+ Writeln(ptext^,'No heap dump by heaptrc unit');
+ Writeln(ptext^,'Exitcode = ',exitcode);
+ if ptext<>@stderr then
+ begin
+ ptext:=@stderr;
+ close(ownfile);
+ end;
+ exit;
+ end;
+ if not error_in_heap then
+ Dumpheap;
+ if error_in_heap and (exitcode=0) then
+ exitcode:=203;
+{$ifdef EXTRA}
+ Close(error_file);
+{$endif EXTRA}
+ if ptext<>@stderr then
+ begin
+ ptext:=@stderr;
+ close(ownfile);
+ end;
+end;
+
+{$ifdef win32}
+ function GetEnvironmentStrings : pchar; stdcall;
+ external 'kernel32' name 'GetEnvironmentStringsA';
+ function FreeEnvironmentStrings(p : pchar) : longbool; stdcall;
+ external 'kernel32' name 'FreeEnvironmentStringsA';
+Function GetEnv(envvar: string): string;
+var
+ s : string;
+ i : ptrint;
+ hp,p : pchar;
+begin
+ getenv:='';
+ p:=GetEnvironmentStrings;
+ hp:=p;
+ while hp^<>#0 do
+ begin
+ s:=strpas(hp);
+ i:=pos('=',s);
+ if upcase(copy(s,1,i-1))=upcase(envvar) then
+ begin
+ getenv:=copy(s,i+1,length(s)-i);
+ break;
+ end;
+ { next string entry}
+ hp:=hp+strlen(hp)+1;
+ end;
+ FreeEnvironmentStrings(p);
+end;
+{$else}
+Function GetEnv(P:string):Pchar;
+{
+ Searches the environment for a string with name p and
+ returns a pchar to it's value.
+ A pchar is used to accomodate for strings of length > 255
+}
+var
+ ep : ppchar;
+ i : ptrint;
+ found : boolean;
+Begin
+ p:=p+'='; {Else HOST will also find HOSTNAME, etc}
+ ep:=envp;
+ found:=false;
+ if ep<>nil then
+ begin
+ while (not found) and (ep^<>nil) do
+ begin
+ found:=true;
+ for i:=1 to length(p) do
+ if p[i]<>ep^[i-1] then
+ begin
+ found:=false;
+ break;
+ end;
+ if not found then
+ inc(ep);
+ end;
+ end;
+ if found then
+ getenv:=ep^+length(p)
+ else
+ getenv:=nil;
+end;
+{$endif}
+
+procedure LoadEnvironment;
+var
+ i,j : ptrint;
+ s : string;
+begin
+ s:=Getenv('HEAPTRC');
+ if pos('keepreleased',s)>0 then
+ keepreleased:=true;
+ if pos('disabled',s)>0 then
+ useheaptrace:=false;
+ if pos('nohalt',s)>0 then
+ haltonerror:=false;
+ i:=pos('log=',s);
+ if i>0 then
+ begin
+ outputstr:=copy(s,i+4,255);
+ j:=pos(' ',outputstr);
+ if j=0 then
+ j:=length(outputstr)+1;
+ delete(outputstr,j,255);
+ end;
+end;
+
+
+Initialization
+ LoadEnvironment;
+ { heaptrc can be disabled from the environment }
+ if useheaptrace then
+ TraceInit;
+finalization
+ if useheaptrace then
+ TraceExit;
+end.
+{
+ $Log: heaptrc.pp,v $
+ Revision 1.44 2005/04/04 15:16:26 peter
+ * fixed crash in tracereallocmem statictics
+
+ Revision 1.43 2005/03/25 22:53:39 jonas
+ * fixed several warnings and notes about unused variables (mainly) or
+ uninitialised use of variables/function results (a few)
+
+ Revision 1.42 2005/03/10 20:36:31 florian
+ * fixed pointer checking for win32, thx to Martin Schreiber for the patch
+
+ Revision 1.41 2005/03/04 16:49:34 peter
+ * fix getheapstatus bootstrapping
+
+ Revision 1.40 2005/02/28 15:38:38 marco
+ * getFPCheapstatus (no, FPC HEAP, not FP CHEAP!)
+
+ Revision 1.39 2005/02/14 17:13:22 peter
+ * truncate log
+
+ Revision 1.38 2005/01/21 15:56:32 peter
+ * uses _eend instead of _edata in checkpointer, patch by
+ Martin Schreiber
+
+}