mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 10:19:52 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1483 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			1483 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
{ This unit is an extended heaptrc unit.
 | 
						|
}
 | 
						|
unit MemCheck;
 | 
						|
 | 
						|
{$MODE ObjFPC}
 | 
						|
 | 
						|
interface
 | 
						|
 | 
						|
{ 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) }
 | 
						|
 | 
						|
{$DEFINE Extra}
 | 
						|
 | 
						|
{$inline off}// inline off for stack traces
 | 
						|
 | 
						|
// additions for codetools
 | 
						|
{$DEFINE MC_Interface}
 | 
						|
{$i memcheck_laz.inc}
 | 
						|
{$UNDEF MC_Interface}
 | 
						|
 | 
						|
{$checkpointer off}
 | 
						|
{$goto on}
 | 
						|
 | 
						|
{$if defined(win32) or defined(wince)}
 | 
						|
  {$define windows}
 | 
						|
{$endif}
 | 
						|
 | 
						|
Procedure DumpHeap;
 | 
						|
 | 
						|
{ 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 : ptruint;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 = 32;
 | 
						|
{$else EXTRA}
 | 
						|
  tracesize = 16;
 | 
						|
{$endif EXTRA}
 | 
						|
  { install heaptrc memorymanager }
 | 
						|
  useheaptrace : boolean=true;
 | 
						|
  { less checking }
 | 
						|
  quicktrace : boolean=false;
 | 
						|
  { calls halt() on error by default !! }
 | 
						|
  HaltOnError : boolean = true;
 | 
						|
  { Halt on exit if any memory was not freed }
 | 
						|
  HaltOnNotReleased : boolean = false;
 | 
						|
 | 
						|
  { 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 one to test for writing into that part }
 | 
						|
  usecrc : boolean = true;
 | 
						|
 | 
						|
 | 
						|
implementation
 | 
						|
 | 
						|
// additions for codetools
 | 
						|
{$DEFINE MC_ImplementationStart}
 | 
						|
{$i memcheck_laz.inc}
 | 
						|
{$UNDEF MC_ImplementationStart}
 | 
						|
 | 
						|
const
 | 
						|
  { allows one to add custom info in heap_mem_info, this is the size that will
 | 
						|
    be allocated for this information }
 | 
						|
  extra_info_size : ptruint = 0;
 | 
						|
  exact_info_size : ptruint = 0;
 | 
						|
  EntryMemUsed    : ptruint = 0;
 | 
						|
  { function to fill this info up }
 | 
						|
  fill_extra_info_proc : TFillExtraInfoProc = nil;
 | 
						|
  display_extra_info_proc : TDisplayExtraInfoProc = nil;
 | 
						|
  { 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;
 | 
						|
 | 
						|
  ppheap_mem_info = ^pheap_mem_info;
 | 
						|
  pheap_mem_info = ^theap_mem_info;
 | 
						|
 | 
						|
  { 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 }
 | 
						|
  theap_mem_info = record
 | 
						|
    previous,
 | 
						|
    next     : pheap_mem_info;
 | 
						|
    todolist : ppheap_mem_info;
 | 
						|
    todonext : pheap_mem_info;
 | 
						|
    size     : ptruint;
 | 
						|
    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;
 | 
						|
 | 
						|
  pheap_info = ^theap_info;
 | 
						|
  theap_info = record
 | 
						|
{$ifdef EXTRA}
 | 
						|
    heap_valid_first,
 | 
						|
    heap_valid_last : pheap_mem_info;
 | 
						|
{$endif EXTRA}
 | 
						|
    heap_mem_root : pheap_mem_info;
 | 
						|
    heap_free_todo : pheap_mem_info;
 | 
						|
    getmem_cnt,
 | 
						|
    freemem_cnt   : ptruint;
 | 
						|
    getmem_size,
 | 
						|
    freemem_size  : ptruint;
 | 
						|
    getmem8_size,
 | 
						|
    freemem8_size : ptruint;
 | 
						|
    error_in_heap : boolean;
 | 
						|
    inside_trace_getmem : boolean;
 | 
						|
  end;
 | 
						|
 | 
						|
var
 | 
						|
  useownfile : boolean;
 | 
						|
  ownfile : text;
 | 
						|
{$ifdef EXTRA}
 | 
						|
  error_file : text;
 | 
						|
{$endif EXTRA}
 | 
						|
  main_orig_todolist: ppheap_mem_info;
 | 
						|
  main_relo_todolist: ppheap_mem_info;
 | 
						|
  orphaned_info: theap_info;
 | 
						|
  todo_lock: trtlcriticalsection;
 | 
						|
threadvar
 | 
						|
  heap_info: theap_info;
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                                   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:ptruint):longword;
 | 
						|
var
 | 
						|
  i : ptruint;
 | 
						|
  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 : pptruint;
 | 
						|
begin
 | 
						|
   crc:=cardinal($ffffffff);
 | 
						|
   crc:=UpdateCrc32(crc,p^.size,sizeof(ptruint));
 | 
						|
   crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(ptruint));
 | 
						|
   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(ptruint));
 | 
						|
     end;
 | 
						|
   calculate_sig:=crc;
 | 
						|
end;
 | 
						|
 | 
						|
{$ifdef EXTRA}
 | 
						|
Function calculate_release_sig(p : pheap_mem_info) : longword;
 | 
						|
var
 | 
						|
   crc : longword;
 | 
						|
   pl : pptruint;
 | 
						|
begin
 | 
						|
   crc:=$ffffffff;
 | 
						|
   crc:=UpdateCrc32(crc,p^.size,sizeof(ptruint));
 | 
						|
   crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(ptruint));
 | 
						|
   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(ptruint));
 | 
						|
     end;
 | 
						|
   calculate_release_sig:=crc;
 | 
						|
end;
 | 
						|
{$endif EXTRA}
 | 
						|
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                                Helpers
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
function InternalFreeMemSize(loc_info: pheap_info; p: pointer; pp: pheap_mem_info;
 | 
						|
  size: ptruint; release_todo_lock: boolean): ptruint; forward;
 | 
						|
function TraceFreeMem(p: pointer): ptruint; forward;
 | 
						|
 | 
						|
procedure call_stack(pp : pheap_mem_info;var ptext : text);
 | 
						|
var
 | 
						|
  i  : ptruint;
 | 
						|
begin
 | 
						|
  writeln(ptext,'Call trace for block $',hexstr(pointer(pp)+sizeof(theap_mem_info)),' 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  : ptruint;
 | 
						|
begin
 | 
						|
  writeln(ptext,'Call trace for block at $',hexstr(pointer(pp)+sizeof(theap_mem_info)),' 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(pointer(p)+sizeof(theap_mem_info)),' 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(pointer(p)+sizeof(theap_mem_info)),' 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 : ptruint;
 | 
						|
begin
 | 
						|
  Writeln(ptext,'Marked memory at $',HexStr(pointer(p)+sizeof(theap_mem_info)),' 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,2*sizeof(pointer)),'"',pp[i],'"');
 | 
						|
end;
 | 
						|
{$endif EXTRA}
 | 
						|
 | 
						|
procedure dump_wrong_size(p : pheap_mem_info;size : ptruint;var ptext : text);
 | 
						|
begin
 | 
						|
  Writeln(ptext,'Marked memory at $',HexStr(pointer(p)+sizeof(theap_mem_info)),' 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 (loc_info: pheap_info; p : pheap_mem_info) : boolean;
 | 
						|
var
 | 
						|
  i  : ptruint;
 | 
						|
  pp : pheap_mem_info;
 | 
						|
begin
 | 
						|
  is_in_getmem_list:=false;
 | 
						|
  pp:=loc_info^.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
 | 
						|
        if useownfile then
 | 
						|
          writeln(ownfile,'error in linked list of heap_mem_info')
 | 
						|
        else
 | 
						|
          writeln(stderr,'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>loc_info^.getmem_cnt-loc_info^.freemem_cnt then begin
 | 
						|
       if useownfile then
 | 
						|
         writeln(ownfile,'error in linked list of heap_mem_info')
 | 
						|
       else
 | 
						|
         writeln(stderr,'error in linked list of heap_mem_info');
 | 
						|
       RunError(204);
 | 
						|
     end;
 | 
						|
   end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure finish_heap_free_todo_list(loc_info: pheap_info);
 | 
						|
var
 | 
						|
  bp: pointer;
 | 
						|
  pp: pheap_mem_info;
 | 
						|
  list: ppheap_mem_info;
 | 
						|
begin
 | 
						|
  list := @loc_info^.heap_free_todo;
 | 
						|
  repeat
 | 
						|
    pp := list^;
 | 
						|
    list^ := list^^.todonext;
 | 
						|
    bp := pointer(pp)+sizeof(theap_mem_info);
 | 
						|
    InternalFreeMemSize(loc_info,bp,pp,pp^.size,false);
 | 
						|
  until list^ = nil;
 | 
						|
end;
 | 
						|
 | 
						|
procedure try_finish_heap_free_todo_list(loc_info: pheap_info);
 | 
						|
begin
 | 
						|
  if loc_info^.heap_free_todo <> nil then
 | 
						|
  begin
 | 
						|
    entercriticalsection(todo_lock);
 | 
						|
    finish_heap_free_todo_list(loc_info);
 | 
						|
    leavecriticalsection(todo_lock);
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                               TraceGetMem
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
Function TraceGetMem(size:ptruint):pointer;
 | 
						|
var
 | 
						|
  allocsize,i : ptruint;
 | 
						|
  oldbp,
 | 
						|
  bp : pointer;
 | 
						|
  pl : pdword;
 | 
						|
  p  : pointer;
 | 
						|
  pp : pheap_mem_info;
 | 
						|
  loc_info: pheap_info;
 | 
						|
begin
 | 
						|
  loc_info := @heap_info;
 | 
						|
  try_finish_heap_free_todo_list(loc_info);
 | 
						|
  inc(loc_info^.getmem_size,size);
 | 
						|
  inc(loc_info^.getmem8_size,(size+7) and not 7);
 | 
						|
{ Do the real GetMem, but alloc also for the info block }
 | 
						|
{$ifdef cpuarm}
 | 
						|
  allocsize:=(size + 3) and not 3+sizeof(theap_mem_info)+extra_info_size;
 | 
						|
{$else cpuarm}
 | 
						|
  allocsize:=size+sizeof(theap_mem_info)+extra_info_size;
 | 
						|
{$endif cpuarm}
 | 
						|
  if add_tail then
 | 
						|
    inc(allocsize,sizeof(ptruint));
 | 
						|
  { if ReturnNilIfGrowHeapFails is true
 | 
						|
    SysGetMem can return nil }
 | 
						|
  p:=SysGetMem(allocsize);
 | 
						|
  if (p=nil) then
 | 
						|
    begin
 | 
						|
      TraceGetMem:=nil;
 | 
						|
      exit;
 | 
						|
    end;
 | 
						|
  pp:=pheap_mem_info(p);
 | 
						|
  inc(p,sizeof(theap_mem_info));
 | 
						|
{ Create the info block }
 | 
						|
  pp^.sig:=$DEADBEEF;
 | 
						|
  pp^.todolist:=@loc_info^.heap_free_todo;
 | 
						|
  pp^.todonext:=nil;
 | 
						|
  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
 | 
						|
        loc_info^.inside_trace_getmem:=true;
 | 
						|
        fill_extra_info_proc(@pp^.extra_info^.data);
 | 
						|
        loc_info^.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(ptruint);
 | 
						|
{$ifdef FPC_SUPPORTS_UNALIGNED}
 | 
						|
      unaligned(pl^):=$DEADBEEF;
 | 
						|
{$else FPC_SUPPORTS_UNALIGNED}
 | 
						|
      pl^:=$DEADBEEF;
 | 
						|
{$endif FPC_SUPPORTS_UNALIGNED}
 | 
						|
    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 loc_info^.heap_mem_root<>nil then
 | 
						|
   loc_info^.heap_mem_root^.next:=pp;
 | 
						|
  pp^.previous:=loc_info^.heap_mem_root;
 | 
						|
  pp^.next:=nil;
 | 
						|
{$ifdef EXTRA}
 | 
						|
  pp^.prev_valid:=loc_info^.heap_valid_last;
 | 
						|
  loc_info^.heap_valid_last:=pp;
 | 
						|
  if not assigned(loc_info^.heap_valid_first) then
 | 
						|
    loc_info^.heap_valid_first:=pp;
 | 
						|
{$endif EXTRA}
 | 
						|
  loc_info^.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(loc_info^.getmem_cnt);
 | 
						|
  { update the signature }
 | 
						|
  if usecrc then
 | 
						|
    pp^.sig:=calculate_sig(pp);
 | 
						|
  TraceGetmem:=p;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                                TraceFreeMem
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
function CheckFreeMemSize(loc_info: pheap_info; pp: pheap_mem_info;
 | 
						|
  size, ppsize: ptruint): boolean;
 | 
						|
var
 | 
						|
  i: ptruint;
 | 
						|
  bp : pointer;
 | 
						|
  ptext : ^text;
 | 
						|
{$ifdef EXTRA}
 | 
						|
  pp2 : pheap_mem_info;
 | 
						|
{$endif}
 | 
						|
begin
 | 
						|
  if useownfile then
 | 
						|
    ptext:=@ownfile
 | 
						|
  else
 | 
						|
    ptext:=@stderr;
 | 
						|
  inc(loc_info^.freemem_size,size);
 | 
						|
  inc(loc_info^.freemem8_size,(size+7) and not 7);
 | 
						|
  if not quicktrace then
 | 
						|
    begin
 | 
						|
      if not(is_in_getmem_list(loc_info, pp)) then
 | 
						|
       RunError(204);
 | 
						|
    end;
 | 
						|
  if (pp^.sig=$AAAAAAAA) and not usecrc then
 | 
						|
    begin
 | 
						|
       loc_info^.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
 | 
						|
       loc_info^.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
 | 
						|
       loc_info^.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;
 | 
						|
  { 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=loc_info^.heap_mem_root then
 | 
						|
         loc_info^.heap_mem_root:=loc_info^.heap_mem_root^.previous;
 | 
						|
    end
 | 
						|
  else
 | 
						|
    begin
 | 
						|
       bp:=get_caller_frame(get_frame);
 | 
						|
       for i:=(tracesize div 2)+1 to tracesize do
 | 
						|
        begin
 | 
						|
          if bp<>nil then begin
 | 
						|
            pp^.calls[i]:=get_caller_addr(bp);
 | 
						|
            bp:=get_caller_frame(bp);
 | 
						|
          end else begin
 | 
						|
            pp^.calls[i]:=nil;
 | 
						|
          end;
 | 
						|
        end;
 | 
						|
    end;
 | 
						|
  inc(loc_info^.freemem_cnt);
 | 
						|
  { clear the memory, $F0 will lead to GFP if used as pointer ! }
 | 
						|
  fillchar((pointer(pp)+sizeof(theap_mem_info))^,size,#240);
 | 
						|
  { 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=loc_info^.heap_valid_last then
 | 
						|
         begin
 | 
						|
            loc_info^.heap_valid_last:=pp^.prev_valid;
 | 
						|
            if pp=loc_info^.heap_valid_first then
 | 
						|
              loc_info^.heap_valid_first:=nil;
 | 
						|
            exit(false);
 | 
						|
         end;
 | 
						|
       pp2:=loc_info^.heap_valid_last;
 | 
						|
       while assigned(pp2) do
 | 
						|
         begin
 | 
						|
            if pp2^.prev_valid=pp then
 | 
						|
              begin
 | 
						|
                 pp2^.prev_valid:=pp^.prev_valid;
 | 
						|
                 if pp=loc_info^.heap_valid_first then
 | 
						|
                   loc_info^.heap_valid_first:=pp2;
 | 
						|
                 exit(false);
 | 
						|
              end
 | 
						|
            else
 | 
						|
              pp2:=pp2^.prev_valid;
 | 
						|
         end;
 | 
						|
{$endif EXTRA}
 | 
						|
       exit(false);
 | 
						|
    end;
 | 
						|
  CheckFreeMemSize:=true;
 | 
						|
end;
 | 
						|
 | 
						|
function InternalFreeMemSize(loc_info: pheap_info; p: pointer; pp: pheap_mem_info;
 | 
						|
  size: ptruint; release_todo_lock: boolean): ptruint;
 | 
						|
var
 | 
						|
  i,ppsize : ptruint;
 | 
						|
  extra_size: ptruint;
 | 
						|
  release_mem: boolean;
 | 
						|
begin
 | 
						|
  { save old values }
 | 
						|
  extra_size:=pp^.extra_info_size;
 | 
						|
  ppsize:= size+sizeof(theap_mem_info)+pp^.extra_info_size;
 | 
						|
  if add_tail then
 | 
						|
    inc(ppsize,sizeof(ptruint));
 | 
						|
  { do various checking }
 | 
						|
  release_mem := CheckFreeMemSize(loc_info, pp, size, ppsize);
 | 
						|
  if release_todo_lock then
 | 
						|
    leavecriticalsection(todo_lock);
 | 
						|
  if release_mem then
 | 
						|
  begin
 | 
						|
    { 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(ptruint));
 | 
						|
    InternalFreeMemSize:=i;
 | 
						|
  end else
 | 
						|
    InternalFreeMemSize:=size;
 | 
						|
end;
 | 
						|
 | 
						|
function TraceFreeMemSize(p:pointer;size:ptruint):ptruint;
 | 
						|
var
 | 
						|
  loc_info: pheap_info;
 | 
						|
  pp: pheap_mem_info;
 | 
						|
  release_lock: boolean;
 | 
						|
begin
 | 
						|
  if p=nil then
 | 
						|
    begin
 | 
						|
      TraceFreeMemSize:=0;
 | 
						|
      exit;
 | 
						|
    end;
 | 
						|
  loc_info:=@heap_info;
 | 
						|
  pp:=pheap_mem_info(p-sizeof(theap_mem_info));
 | 
						|
  release_lock:=false;
 | 
						|
  if @loc_info^.heap_free_todo <> pp^.todolist then
 | 
						|
  begin
 | 
						|
    if pp^.todolist = main_orig_todolist then
 | 
						|
      pp^.todolist := main_relo_todolist;
 | 
						|
    entercriticalsection(todo_lock);
 | 
						|
    release_lock:=true;
 | 
						|
    if pp^.todolist = @orphaned_info.heap_free_todo then
 | 
						|
    begin
 | 
						|
      loc_info := @orphaned_info;
 | 
						|
    end else
 | 
						|
    if pp^.todolist <> @loc_info^.heap_free_todo then
 | 
						|
    begin
 | 
						|
      { allocated in different heap, push to that todolist }
 | 
						|
      pp^.todonext := pp^.todolist^;
 | 
						|
      pp^.todolist^ := pp;
 | 
						|
      TraceFreeMemSize := pp^.size;
 | 
						|
      leavecriticalsection(todo_lock);
 | 
						|
      exit;
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
  TraceFreeMemSize:=InternalFreeMemSize(loc_info,p,pp,size,release_lock);
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
function TraceMemSize(p:pointer):ptruint;
 | 
						|
var
 | 
						|
  pp : pheap_mem_info;
 | 
						|
begin
 | 
						|
  pp:=pheap_mem_info(p-sizeof(theap_mem_info));
 | 
						|
  TraceMemSize:=pp^.size;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
function TraceFreeMem(p:pointer):ptruint;
 | 
						|
var
 | 
						|
  l  : ptruint;
 | 
						|
  pp : pheap_mem_info;
 | 
						|
begin
 | 
						|
  if p=nil then
 | 
						|
    begin
 | 
						|
      TraceFreeMem:=0;
 | 
						|
      exit;
 | 
						|
    end;
 | 
						|
  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(ptruint));
 | 
						|
  { this can never happend normaly }
 | 
						|
  if pp^.size>l then
 | 
						|
   begin
 | 
						|
     if useownfile then
 | 
						|
       dump_wrong_size(pp,l,ownfile)
 | 
						|
     else
 | 
						|
       dump_wrong_size(pp,l,stderr);
 | 
						|
 | 
						|
{$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:ptruint):Pointer;
 | 
						|
var
 | 
						|
  newP: pointer;
 | 
						|
  allocsize,
 | 
						|
  movesize,
 | 
						|
  i  : ptruint;
 | 
						|
  oldbp,
 | 
						|
  bp : pointer;
 | 
						|
  pl : pdword;
 | 
						|
  pp : pheap_mem_info;
 | 
						|
  oldsize,
 | 
						|
  oldextrasize,
 | 
						|
  oldexactsize : ptruint;
 | 
						|
  old_fill_extra_info_proc : tfillextrainfoproc;
 | 
						|
  old_display_extra_info_proc : tdisplayextrainfoproc;
 | 
						|
  loc_info: pheap_info;
 | 
						|
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 }
 | 
						|
  loc_info:=@heap_info;
 | 
						|
  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
 | 
						|
     loc_info^.error_in_heap:=true;
 | 
						|
     if useownfile then
 | 
						|
       dump_error(pp,ownfile)
 | 
						|
     else
 | 
						|
       dump_error(pp,stderr);
 | 
						|
{$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 }
 | 
						|
{$ifdef cpuarm}
 | 
						|
  allocsize:=(size + 3) and not 3+sizeof(theap_mem_info)+pp^.extra_info_size;
 | 
						|
{$else cpuarm}
 | 
						|
  allocsize:=size+sizeof(theap_mem_info)+pp^.extra_info_size;
 | 
						|
{$endif cpuarm}
 | 
						|
  if add_tail then
 | 
						|
   inc(allocsize,sizeof(ptruint));
 | 
						|
  { 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(ptruint);
 | 
						|
{$ifdef FPC_SUPPORTS_UNALIGNED}
 | 
						|
      unaligned(pl^):=$DEADBEEF;
 | 
						|
{$else FPC_SUPPORTS_UNALIGNED}
 | 
						|
      pl^:=$DEADBEEF;
 | 
						|
{$endif FPC_SUPPORTS_UNALIGNED}
 | 
						|
    end;
 | 
						|
  { adjust like a freemem and then a getmem, so you get correct
 | 
						|
    results in the summary display }
 | 
						|
  inc(loc_info^.freemem_size,oldsize);
 | 
						|
  inc(loc_info^.freemem8_size,(oldsize+7) and not 7);
 | 
						|
  inc(loc_info^.getmem_size,size);
 | 
						|
  inc(loc_info^.getmem8_size,(size+7) and not 7);
 | 
						|
  { generate new backtrace }
 | 
						|
  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;
 | 
						|
  { 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';
 | 
						|
   eend : ptruint; external name '_end';
 | 
						|
{$endif}
 | 
						|
 | 
						|
{$ifdef os2}
 | 
						|
(* Currently still EMX based - possibly to be changed in the future. *)
 | 
						|
var
 | 
						|
   etext: ptruint; external name '_etext';
 | 
						|
   edata : ptruint; external name '_edata';
 | 
						|
   eend : ptruint; external name '_end';
 | 
						|
{$endif}
 | 
						|
 | 
						|
{$ifdef windows}
 | 
						|
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); [public, alias : 'FPC_CHECKPOINTER'];
 | 
						|
var
 | 
						|
  i  : ptruint;
 | 
						|
  pp : pheap_mem_info;
 | 
						|
  loc_info: pheap_info;
 | 
						|
{$ifdef go32v2}
 | 
						|
  get_ebp,stack_top : longword;
 | 
						|
  data_end : longword;
 | 
						|
{$endif go32v2}
 | 
						|
{$ifdef morphos}
 | 
						|
  stack_top: longword;
 | 
						|
{$endif morphos}
 | 
						|
  ptext : ^text;
 | 
						|
label
 | 
						|
  _exit;
 | 
						|
begin
 | 
						|
  if p=nil then
 | 
						|
    runerror(204);
 | 
						|
 | 
						|
  i:=0;
 | 
						|
  loc_info:=@heap_info;
 | 
						|
  if useownfile then
 | 
						|
    ptext:=@ownfile
 | 
						|
  else
 | 
						|
    ptext:=@stderr;
 | 
						|
 | 
						|
{$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 windows}
 | 
						|
  { inside stack ? }
 | 
						|
  if (ptruint(p)>ptruint(get_frame)) and
 | 
						|
     (p<StackTop) 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 windows}
 | 
						|
 | 
						|
{$IFDEF OS2}
 | 
						|
  { inside stack ? }
 | 
						|
  if (PtrUInt (P) > PtrUInt (Get_Frame)) and
 | 
						|
     (PtrUInt (P) < PtrUInt (StackTop)) then
 | 
						|
    goto _exit;
 | 
						|
  { inside data or bss ? }
 | 
						|
  if (PtrUInt (P) >= PtrUInt (@etext)) and (PtrUInt (P) < PtrUInt (@eend)) then
 | 
						|
    goto _exit;
 | 
						|
{$ENDIF OS2}
 | 
						|
 | 
						|
{$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}
 | 
						|
 | 
						|
{$ifdef morphos}
 | 
						|
  { inside stack ? }
 | 
						|
  stack_top:=ptruint(StackBottom)+StackLength;
 | 
						|
  if (ptruint(p)<stack_top) and (ptruint(p)>ptruint(StackBottom)) then
 | 
						|
    goto _exit;
 | 
						|
  { inside data or bss ? }
 | 
						|
  {$WARNING data and bss checking missing }
 | 
						|
{$endif morphos}
 | 
						|
 | 
						|
  {$ifdef darwin}
 | 
						|
  {$warning No checkpointer support yet for Darwin}
 | 
						|
  exit;
 | 
						|
  {$endif}
 | 
						|
 | 
						|
  { first try valid list faster }
 | 
						|
 | 
						|
{$ifdef EXTRA}
 | 
						|
  pp:=loc_info^.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=loc_info^.heap_valid_last) and usecrc and (pp^.sig=$DEADBEEF)
 | 
						|
              and loc_info^.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>loc_info^.getmem_cnt-loc_info^.freemem_cnt then
 | 
						|
      begin
 | 
						|
         writeln(ptext^,'error in linked list of heap_mem_info');
 | 
						|
         halt(1);
 | 
						|
      end;
 | 
						|
   end;
 | 
						|
  i:=0;
 | 
						|
{$endif EXTRA}
 | 
						|
  pp:=loc_info^.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(p),' points into invalid memory block');
 | 
						|
            dump_error(pp,ptext^);
 | 
						|
            runerror(204);
 | 
						|
         end;
 | 
						|
     pp:=pp^.previous;
 | 
						|
     inc(i);
 | 
						|
     if i>loc_info^.getmem_cnt then
 | 
						|
      begin
 | 
						|
         writeln(ptext^,'error in linked list of heap_mem_info');
 | 
						|
         halt(1);
 | 
						|
      end;
 | 
						|
   end;
 | 
						|
  writeln(ptext^,'pointer $',hexstr(p),' does not point to valid memory block');
 | 
						|
  dump_error(p,ptext^);
 | 
						|
  runerror(204);
 | 
						|
_exit:
 | 
						|
end;
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                              Dump Heap
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
procedure dumpheap;
 | 
						|
var
 | 
						|
  pp : pheap_mem_info;
 | 
						|
  i : ptrint;
 | 
						|
  ExpectedHeapFree : ptruint;
 | 
						|
  status : TFPCHeapStatus;
 | 
						|
  ptext : ^text;
 | 
						|
  loc_info: pheap_info;
 | 
						|
begin
 | 
						|
  loc_info:=@heap_info;
 | 
						|
  if useownfile then
 | 
						|
    ptext:=@ownfile
 | 
						|
  else
 | 
						|
    ptext:=@stderr;
 | 
						|
  pp:=loc_info^.heap_mem_root;
 | 
						|
  Writeln(ptext^,'Heap dump by heaptrc unit');
 | 
						|
  Writeln(ptext^,loc_info^.getmem_cnt, ' memory blocks allocated : ',
 | 
						|
    loc_info^.getmem_size,'/',loc_info^.getmem8_size);
 | 
						|
  Writeln(ptext^,loc_info^.freemem_cnt,' memory blocks freed     : ',
 | 
						|
    loc_info^.freemem_size,'/',loc_info^.freemem8_size);
 | 
						|
  Writeln(ptext^,loc_info^.getmem_cnt-loc_info^.freemem_cnt,
 | 
						|
    ' unfreed memory blocks : ',loc_info^.getmem_size-loc_info^.freemem_size);
 | 
						|
  status:=SysGetFPCHeapStatus;
 | 
						|
  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
 | 
						|
    -(loc_info^.getmem8_size-loc_info^.freemem8_size)
 | 
						|
    -(loc_info^.getmem_cnt-loc_info^.freemem_cnt)*(sizeof(theap_mem_info)+extra_info_size)
 | 
						|
    -EntryMemUsed;
 | 
						|
  If ExpectedHeapFree<>status.CurrHeapFree then
 | 
						|
    Writeln(ptext^,'Should be : ',ExpectedHeapFree);
 | 
						|
  i:=loc_info^.getmem_cnt-loc_info^.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}
 | 
						|
          loc_info^.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);
 | 
						|
          loc_info^.error_in_heap:=true;
 | 
						|
       end
 | 
						|
{$endif EXTRA}
 | 
						|
       ;
 | 
						|
     pp:=pp^.previous;
 | 
						|
   end;
 | 
						|
  if HaltOnNotReleased and (loc_info^.getmem_cnt<>loc_info^.freemem_cnt) then
 | 
						|
    exitcode:=203;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                                AllocMem
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
function TraceAllocMem(size:ptruint):Pointer;
 | 
						|
begin
 | 
						|
  TraceAllocMem:=SysAllocMem(size);
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                            No specific tracing calls
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
procedure TraceInitThread;
 | 
						|
var
 | 
						|
  loc_info: pheap_info;
 | 
						|
begin
 | 
						|
  loc_info := @heap_info;
 | 
						|
{$ifdef EXTRA}
 | 
						|
  loc_info^.heap_valid_first := nil;
 | 
						|
  loc_info^.heap_valid_last := nil;
 | 
						|
{$endif}
 | 
						|
  loc_info^.heap_mem_root := nil;
 | 
						|
  loc_info^.getmem_cnt := 0;
 | 
						|
  loc_info^.freemem_cnt := 0;
 | 
						|
  loc_info^.getmem_size := 0;
 | 
						|
  loc_info^.freemem_size := 0;
 | 
						|
  loc_info^.getmem8_size := 0;
 | 
						|
  loc_info^.freemem8_size := 0;
 | 
						|
  loc_info^.error_in_heap := false;
 | 
						|
  loc_info^.inside_trace_getmem := false;
 | 
						|
  EntryMemUsed := SysGetFPCHeapStatus.CurrHeapUsed;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TraceRelocateHeap;
 | 
						|
begin
 | 
						|
  main_relo_todolist := @heap_info.heap_free_todo;
 | 
						|
  initcriticalsection(todo_lock);
 | 
						|
end;
 | 
						|
 | 
						|
procedure move_heap_info(src_info, dst_info: pheap_info);
 | 
						|
var
 | 
						|
  heap_mem: pheap_mem_info;
 | 
						|
begin
 | 
						|
  if src_info^.heap_free_todo <> nil then
 | 
						|
    finish_heap_free_todo_list(src_info);
 | 
						|
  if dst_info^.heap_free_todo <> nil then
 | 
						|
    finish_heap_free_todo_list(dst_info);
 | 
						|
  heap_mem := src_info^.heap_mem_root;
 | 
						|
  if heap_mem <> nil then
 | 
						|
  begin
 | 
						|
    repeat
 | 
						|
      heap_mem^.todolist := @dst_info^.heap_free_todo;
 | 
						|
      if heap_mem^.previous = nil then break;
 | 
						|
      heap_mem := heap_mem^.previous;
 | 
						|
    until false;
 | 
						|
    heap_mem^.previous := dst_info^.heap_mem_root;
 | 
						|
    if dst_info^.heap_mem_root <> nil then
 | 
						|
      dst_info^.heap_mem_root^.next := heap_mem;
 | 
						|
    dst_info^.heap_mem_root := src_info^.heap_mem_root;
 | 
						|
  end;
 | 
						|
  inc(dst_info^.getmem_cnt, src_info^.getmem_cnt);
 | 
						|
  inc(dst_info^.getmem_size, src_info^.getmem_size);
 | 
						|
  inc(dst_info^.getmem8_size, src_info^.getmem8_size);
 | 
						|
  inc(dst_info^.freemem_cnt, src_info^.freemem_cnt);
 | 
						|
  inc(dst_info^.freemem_size, src_info^.freemem_size);
 | 
						|
  inc(dst_info^.freemem8_size, src_info^.freemem8_size);
 | 
						|
  dst_info^.error_in_heap := dst_info^.error_in_heap or src_info^.error_in_heap;
 | 
						|
{$ifdef EXTRA}
 | 
						|
  if assigned(dst_info^.heap_valid_first) then
 | 
						|
    dst_info^.heap_valid_first^.prev_valid := src_info^.heap_valid_last
 | 
						|
  else
 | 
						|
    dst_info^.heap_valid_last := src_info^.heap_valid_last;
 | 
						|
  dst_info^.heap_valid_first := src_info^.heap_valid_first;
 | 
						|
{$endif}
 | 
						|
end;
 | 
						|
 | 
						|
procedure TraceExitThread;
 | 
						|
var
 | 
						|
  loc_info: pheap_info;
 | 
						|
begin
 | 
						|
  loc_info := @heap_info;
 | 
						|
  entercriticalsection(todo_lock);
 | 
						|
  move_heap_info(loc_info, @orphaned_info);
 | 
						|
  leavecriticalsection(todo_lock);
 | 
						|
end;
 | 
						|
 | 
						|
function TraceGetHeapStatus:THeapStatus;
 | 
						|
begin
 | 
						|
  TraceGetHeapStatus:=SysGetHeapStatus;
 | 
						|
end;
 | 
						|
 | 
						|
function TraceGetFPCHeapStatus:TFPCHeapStatus;
 | 
						|
begin
 | 
						|
    TraceGetFPCHeapStatus:=SysGetFPCHeapStatus;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{*****************************************************************************
 | 
						|
                             Program Hooks
 | 
						|
*****************************************************************************}
 | 
						|
 | 
						|
Procedure SetHeapTraceOutput(const name : string);
 | 
						|
var i : ptruint;
 | 
						|
begin
 | 
						|
   if useownfile then
 | 
						|
     begin
 | 
						|
       useownfile:=false;
 | 
						|
       close(ownfile);
 | 
						|
     end;
 | 
						|
   assign(ownfile,name);
 | 
						|
{$I-}
 | 
						|
   append(ownfile);
 | 
						|
   if IOResult<>0 then
 | 
						|
     Rewrite(ownfile);
 | 
						|
{$I+}
 | 
						|
   useownfile:=true;
 | 
						|
   for i:=0 to Paramcount do
 | 
						|
     write(ownfile,ParamStr(i),' ');
 | 
						|
   writeln(ownfile);
 | 
						|
end;
 | 
						|
 | 
						|
procedure SetHeapExtraInfo( size : ptruint;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) and not 7;
 | 
						|
  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;
 | 
						|
    InitThread: @TraceInitThread;
 | 
						|
    DoneThread: @TraceExitThread;
 | 
						|
    RelocateHeap: @TraceRelocateHeap;
 | 
						|
    GetHeapStatus : @TraceGetHeapStatus;
 | 
						|
    GetFPCHeapStatus : @TraceGetFPCHeapStatus;
 | 
						|
  );
 | 
						|
 | 
						|
procedure TraceInit;
 | 
						|
begin
 | 
						|
  MakeCRC32Tbl;
 | 
						|
  main_orig_todolist := @heap_info.heap_free_todo;
 | 
						|
  main_relo_todolist := nil;
 | 
						|
  TraceInitThread;
 | 
						|
  SetMemoryManager(TraceManager);
 | 
						|
  useownfile:=false;
 | 
						|
  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
 | 
						|
       if useownfile then
 | 
						|
         begin
 | 
						|
           Writeln(ownfile,'No heap dump by heaptrc unit');
 | 
						|
           Writeln(ownfile,'Exitcode = ',exitcode);
 | 
						|
         end
 | 
						|
       else
 | 
						|
         begin
 | 
						|
           Writeln(stderr,'No heap dump by heaptrc unit');
 | 
						|
           Writeln(stderr,'Exitcode = ',exitcode);
 | 
						|
         end;
 | 
						|
       if useownfile then
 | 
						|
         begin
 | 
						|
           useownfile:=false;
 | 
						|
           close(ownfile);
 | 
						|
         end;
 | 
						|
       exit;
 | 
						|
    end;
 | 
						|
  move_heap_info(@orphaned_info, @heap_info);
 | 
						|
  dumpheap;
 | 
						|
  if heap_info.error_in_heap and (exitcode=0) then
 | 
						|
    exitcode:=203;
 | 
						|
  if main_relo_todolist <> nil then
 | 
						|
    donecriticalsection(todo_lock);
 | 
						|
{$ifdef EXTRA}
 | 
						|
  Close(error_file);
 | 
						|
{$endif EXTRA}
 | 
						|
   if useownfile then
 | 
						|
     begin
 | 
						|
       useownfile:=false;
 | 
						|
       close(ownfile);
 | 
						|
     end;
 | 
						|
end;
 | 
						|
 | 
						|
{$if defined(win32) or defined(win64)}
 | 
						|
   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 : ptruint;
 | 
						|
   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 defined(win32) or defined(win64)}
 | 
						|
 | 
						|
{$ifdef wince}
 | 
						|
Function GetEnv(P:string):Pchar;
 | 
						|
begin
 | 
						|
  { WinCE does not have environment strings.
 | 
						|
    Add some way to specify heaptrc options? }
 | 
						|
  GetEnv:=nil;
 | 
						|
end;
 | 
						|
{$else wince}
 | 
						|
 | 
						|
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     : ptruint;
 | 
						|
  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 wince}
 | 
						|
{$endif win32}
 | 
						|
 | 
						|
procedure LoadEnvironment;
 | 
						|
var
 | 
						|
  i,j : ptruint;
 | 
						|
  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;
 | 
						|
  if pos('haltonnotreleased',s)>0 then
 | 
						|
   HaltOnNotReleased :=true;
 | 
						|
  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;
 | 
						|
 | 
						|
// additions for codetools
 | 
						|
{$DEFINE MC_ImplementationEnd}
 | 
						|
{$i memcheck_laz.inc}
 | 
						|
{$UNDEF MC_ImplementationEnd}
 | 
						|
 | 
						|
Initialization
 | 
						|
  LoadEnvironment;
 | 
						|
  { heaptrc can be disabled from the environment }
 | 
						|
  if useheaptrace then
 | 
						|
   TraceInit;
 | 
						|
  CheckHeapWrtMemCnt('memcheck.pas Initialization');
 | 
						|
finalization
 | 
						|
  if useheaptrace then
 | 
						|
   TraceExit;
 | 
						|
end.
 | 
						|
 |