* heap manager: handle freeing of block owned by exited thread

* heap trace: ditto

git-svn-id: trunk@7649 -
This commit is contained in:
micha 2007-06-13 20:27:42 +00:00
parent c7037df254
commit c226f6fd44
2 changed files with 485 additions and 228 deletions

View File

@ -134,8 +134,10 @@ type
poschunk = ^toschunk; poschunk = ^toschunk;
toschunk = record toschunk = record
size : ptrint; size : ptrint;
next : poschunk; next_free : poschunk;
used : ptrint; prev_any : poschunk;
next_any : poschunk;
used : ptrint; { 0: free, >0: fixed, -1: var }
freelists : pfreelists; freelists : pfreelists;
{ padding inserted automatically by alloc_oschunk } { padding inserted automatically by alloc_oschunk }
end; end;
@ -178,8 +180,9 @@ type
tfixedfreelists = array[1..maxblockindex] of pmemchunk_fixed; tfixedfreelists = array[1..maxblockindex] of pmemchunk_fixed;
tfreelists = record tfreelists = record
oslist : poschunk; oslist : poschunk; { os chunks free, available for use }
oscount : dword; oscount : dword; { number of os chunks on oslist }
oslist_all : poschunk; { all os chunks allocated }
fixedlists : tfixedfreelists; fixedlists : tfixedfreelists;
varlist : pmemchunk_var; varlist : pmemchunk_var;
{ chunks waiting to be freed from other thread } { chunks waiting to be freed from other thread }
@ -205,6 +208,8 @@ const
var var
main_orig_freelists : pfreelists; main_orig_freelists : pfreelists;
main_relo_freelists : pfreelists; main_relo_freelists : pfreelists;
orphaned_freelists : tfreelists;
orphaned_oslist_lock : trtlcriticalsection;
threadvar threadvar
freelists : tfreelists; freelists : tfreelists;
@ -398,6 +403,14 @@ end;
{$endif} {$endif}
{*****************************************************************************
Forwards
*****************************************************************************}
procedure finish_waitfixedlist(loc_freelists: pfreelists); forward;
procedure finish_waitvarlist(loc_freelists: pfreelists); forward;
function try_finish_waitfixedlist(loc_freelists: pfreelists): boolean; forward;
procedure try_finish_waitvarlist(loc_freelists: pfreelists); forward;
{***************************************************************************** {*****************************************************************************
List adding/removal List adding/removal
@ -453,12 +466,19 @@ begin
freelists.varlist := pmc^.next_var; freelists.varlist := pmc^.next_var;
end; end;
procedure remove_all_from_list_fixed(chunksize: ptrint; poc: poschunk); procedure remove_freed_fixed_chunks(poc: poschunk);
{ remove all fixed chunks from the fixed free list, as this os chunk
is going to be used for other purpose }
var var
pmc, pmc_end: pmemchunk_fixed; pmc, pmc_end: pmemchunk_fixed;
fixedlist: ppmemchunk_fixed; fixedlist: ppmemchunk_fixed;
chunksize: ptrint;
begin begin
{ exit if this is a var size os chunk, function only applicable to fixed size }
if poc^.used < 0 then
exit;
pmc := pmemchunk_fixed(pointer(poc)+fixedfirstoffset); pmc := pmemchunk_fixed(pointer(poc)+fixedfirstoffset);
chunksize := pmc^.size and fixedsizemask;
pmc_end := pmemchunk_fixed(pointer(poc)+(poc^.size and sizemask)-chunksize); pmc_end := pmemchunk_fixed(pointer(poc)+(poc^.size and sizemask)-chunksize);
fixedlist := @poc^.freelists^.fixedlists[chunksize shr blockshift]; fixedlist := @poc^.freelists^.fixedlists[chunksize shr blockshift];
repeat repeat
@ -467,9 +487,24 @@ begin
until pmc > pmc_end; until pmc > pmc_end;
end; end;
procedure append_to_oslist(poc: poschunk; chunksize: ptrint); procedure free_oschunk(loc_freelists: pfreelists; poc: poschunk);
var var
pocsize: ptrint; pocsize: ptrint;
begin
remove_freed_fixed_chunks(poc);
if assigned(poc^.prev_any) then
poc^.prev_any^.next_any := poc^.next_any
else
loc_freelists^.oslist_all := poc^.next_any;
if assigned(poc^.next_any) then
poc^.next_any^.prev_any := poc^.prev_any;
pocsize := poc^.size and sizemask;
dec(loc_freelists^.internal_status.currheapsize, pocsize);
SysOSFree(poc, pocsize);
end;
procedure append_to_oslist(poc: poschunk);
var
loc_freelists: pfreelists; loc_freelists: pfreelists;
begin begin
loc_freelists := poc^.freelists; loc_freelists := poc^.freelists;
@ -482,32 +517,22 @@ begin
end; end;
{ decide whether to free block or add to list } { decide whether to free block or add to list }
{$ifdef HAS_SYSOSFREE} {$ifdef HAS_SYSOSFREE}
pocsize := poc^.size and sizemask;
if (loc_freelists^.oscount >= MaxKeptOSChunks) or if (loc_freelists^.oscount >= MaxKeptOSChunks) or
(pocsize > growheapsize2) then ((poc^.size and sizemask) > growheapsize2) then
begin begin
if chunksize <> 0 then free_oschunk(loc_freelists, poc);
remove_all_from_list_fixed(chunksize, poc);
dec(loc_freelists^.internal_status.currheapsize, pocsize);
SysOSFree(poc, pocsize);
end end
else else
begin begin
{$endif} {$endif}
poc^.next := loc_freelists^.oslist; poc^.next_free := loc_freelists^.oslist;
loc_freelists^.oslist := poc; loc_freelists^.oslist := poc;
inc(loc_freelists^.oscount); inc(loc_freelists^.oscount);
{$ifdef HAS_SYSOSFREE} {$ifdef HAS_SYSOSFREE}
end; end;
{$endif} {$endif}
end; end;
procedure clear_oschunk_on_freelist_fixed_flag(poc: poschunk); inline;
{ prevent thinking this os chunk is on the fixed freelists }
begin
pmemchunk_fixed(pointer(poc) + fixedfirstoffset)^.size := 0;
end;
procedure append_to_oslist_var(pmc: pmemchunk_var); procedure append_to_oslist_var(pmc: pmemchunk_var);
var var
poc: poschunk; poc: poschunk;
@ -515,8 +540,42 @@ begin
// block eligable for freeing // block eligable for freeing
poc := pointer(pmc)-varfirstoffset; poc := pointer(pmc)-varfirstoffset;
remove_from_list_var(pmc); remove_from_list_var(pmc);
clear_oschunk_on_freelist_fixed_flag(poc); append_to_oslist(poc);
append_to_oslist(poc, 0); end;
procedure modify_oschunk_freelists(poc: poschunk; new_freelists: pfreelists);
var
pmcv: pmemchunk_var;
begin
poc^.freelists := new_freelists;
{ only if oschunk contains var memchunks, we need additional assignments }
if poc^.used <> -1 then exit;
pmcv := pmemchunk_var(pointer(poc)+varfirstoffset);
repeat
pmcv^.freelists := new_freelists;
if (pmcv^.size and lastblockflag) <> 0 then
break;
pmcv := pmemchunk_var(pointer(pmcv)+(pmcv^.size and sizemask));
until false;
end;
function modify_freelists(loc_freelists, new_freelists: pfreelists): poschunk;
var
poc: poschunk;
begin
poc := loc_freelists^.oslist_all;
if assigned(poc) then
begin
repeat
{ fixed and var freelist for orphaned freelists do not need maintenance }
{ we assume the heap is not severely fragmented at thread exit }
modify_oschunk_freelists(poc, new_freelists);
if not assigned(poc^.next_any) then
exit(poc);
poc := poc^.next_any;
until false;
end;
modify_freelists := nil;
end; end;
{***************************************************************************** {*****************************************************************************
@ -627,6 +686,47 @@ end;
Grow Heap Grow Heap
*****************************************************************************} *****************************************************************************}
function find_free_oschunk(loc_freelists: pfreelists;
minsize, maxsize: ptrint; var size: ptrint): poschunk;
var
pmc: pmemchunk_fixed;
prev_poc, poc: poschunk;
pocsize: ptrint;
begin
poc := loc_freelists^.oslist;
prev_poc := nil;
while poc <> nil do
begin
if (poc^.size and ocrecycleflag) <> 0 then
begin
{ oops! we recycled this chunk; remove it from list }
poc^.size := poc^.size and not ocrecycleflag;
poc := poc^.next_free;
if prev_poc = nil then
loc_freelists^.oslist := poc
else
prev_poc^.next_free := poc;
continue;
end;
pocsize := poc^.size and sizemask;
if (pocsize >= minsize) and
(pocsize <= maxsize) then
begin
size := pocsize;
if prev_poc = nil then
loc_freelists^.oslist := poc^.next_free
else
prev_poc^.next_free := poc^.next_free;
dec(loc_freelists^.oscount);
remove_freed_fixed_chunks(poc);
break;
end;
prev_poc := poc;
poc := poc^.next_free;
end;
result := poc;
end;
function alloc_oschunk(loc_freelists: pfreelists; chunkindex, size: ptrint): pointer; function alloc_oschunk(loc_freelists: pfreelists; chunkindex, size: ptrint): pointer;
var var
pmc, pmc,
@ -650,38 +750,35 @@ begin
else else
maxsize := high(ptrint); maxsize := high(ptrint);
{ blocks available in freelist? } { blocks available in freelist? }
poc := loc_freelists^.oslist; poc := find_free_oschunk(loc_freelists, minsize, maxsize, size);
prev_poc := nil; if not assigned(poc) and (assigned(orphaned_freelists.waitfixed)
while poc <> nil do or assigned(orphaned_freelists.waitvar) or (orphaned_freelists.oscount > 0)) then
begin begin
if (poc^.size and ocrecycleflag) <> 0 then entercriticalsection(orphaned_oslist_lock);
begin try_finish_waitfixedlist(@orphaned_freelists);
{ oops! we recycled this chunk; remove it from list } try_finish_waitvarlist(@orphaned_freelists);
poc^.size := poc^.size and not ocrecycleflag; if orphaned_freelists.oscount > 0 then
poc := poc^.next;
if prev_poc = nil then
loc_freelists^.oslist := poc
else
prev_poc^.next := poc;
continue;
end;
pocsize := poc^.size and sizemask;
if (pocsize >= minsize) and
(pocsize <= maxsize) then
begin begin
size := pocsize; { blocks available in orphaned freelist ? }
if prev_poc = nil then poc := find_free_oschunk(@orphaned_freelists, minsize, maxsize, size);
loc_freelists^.oslist := poc^.next if assigned(poc) then
else begin
prev_poc^.next := poc^.next; { adopt this os chunk }
dec(loc_freelists^.oscount); poc^.freelists := loc_freelists;
pmc := pmemchunk_fixed(pointer(poc)+fixedfirstoffset); if assigned(poc^.prev_any) then
if pmc^.size <> 0 then poc^.prev_any^.next_any := poc^.next_any
remove_all_from_list_fixed(pmc^.size and fixedsizemask, poc); else
break; orphaned_freelists.oslist_all := poc^.next_any;
if assigned(poc^.next_any) then
poc^.next_any^.prev_any := poc^.prev_any;
poc^.next_any := loc_freelists^.oslist_all;
if assigned(loc_freelists^.oslist_all) then
loc_freelists^.oslist_all^.prev_any := poc;
poc^.prev_any := nil;
loc_freelists^.oslist_all := poc;
end;
end; end;
prev_poc := poc; leavecriticalsection(orphaned_oslist_lock);
poc := poc^.next;
end; end;
if poc = nil then if poc = nil then
begin begin
@ -730,10 +827,13 @@ begin
HandleError(203); HandleError(203);
end; end;
end; end;
{ prevent thinking this os chunk is on some freelist } poc^.next_free := nil;
clear_oschunk_on_freelist_fixed_flag(poc);
poc^.next := nil;
poc^.freelists := loc_freelists; poc^.freelists := loc_freelists;
poc^.prev_any := nil;
poc^.next_any := loc_freelists^.oslist_all;
if assigned(loc_freelists^.oslist_all) then
loc_freelists^.oslist_all^.prev_any := poc;
loc_freelists^.oslist_all := poc;
{ set the total new heap size } { set the total new heap size }
status := @loc_freelists^.internal_status; status := @loc_freelists^.internal_status;
inc(status^.currheapsize, size); inc(status^.currheapsize, size);
@ -741,10 +841,10 @@ begin
status^.maxheapsize := status^.currheapsize; status^.maxheapsize := status^.currheapsize;
end; end;
{ initialize os-block } { initialize os-block }
poc^.used := 0;
poc^.size := size; poc^.size := size;
if chunkindex<>0 then if chunkindex<>0 then
begin begin
poc^.used := 0;
{ chop os chunk in fixedsize parts, { chop os chunk in fixedsize parts,
maximum of $ffff elements are allowed, otherwise maximum of $ffff elements are allowed, otherwise
there will be an overflow } there will be an overflow }
@ -774,6 +874,7 @@ begin
end end
else else
begin begin
poc^.used := -1;
{ we need to align the user pointers to 8 byte at least for { we need to align the user pointers to 8 byte at least for
mmx/sse and doubles on sparc, align to 16 bytes } mmx/sse and doubles on sparc, align to 16 bytes }
result := pointer(poc)+varfirstoffset; result := pointer(poc)+varfirstoffset;
@ -789,9 +890,6 @@ end;
SysGetMem SysGetMem
*****************************************************************************} *****************************************************************************}
function finish_waitfixedlist(loc_freelists: pfreelists): boolean; forward;
procedure finish_waitvarlist(loc_freelists: pfreelists); forward;
function SysGetMem_Fixed(chunksize: ptrint): pointer; function SysGetMem_Fixed(chunksize: ptrint): pointer;
var var
pmc, pmc_next: pmemchunk_fixed; pmc, pmc_next: pmemchunk_fixed;
@ -814,7 +912,7 @@ begin
dec(loc_freelists^.oscount); dec(loc_freelists^.oscount);
end; end;
end end
else if finish_waitfixedlist(loc_freelists) then else if try_finish_waitfixedlist(loc_freelists) then
{ freed some to-be freed chunks, retry allocation } { freed some to-be freed chunks, retry allocation }
exit(SysGetMem_Fixed(chunksize)) exit(SysGetMem_Fixed(chunksize))
else else
@ -856,7 +954,7 @@ begin
result:=nil; result:=nil;
{ free pending items } { free pending items }
loc_freelists := @freelists; loc_freelists := @freelists;
finish_waitvarlist(loc_freelists); try_finish_waitvarlist(loc_freelists);
pbest := nil; pbest := nil;
pcurr := loc_freelists^.varlist; pcurr := loc_freelists^.varlist;
iter := high(longint); iter := high(longint);
@ -893,7 +991,6 @@ begin
size := split_block(pcurr, size); size := split_block(pcurr, size);
{ flag block as used } { flag block as used }
pcurr^.size := pcurr^.size or usedflag; pcurr^.size := pcurr^.size or usedflag;
pcurr^.freelists := loc_freelists;
{ statistics } { statistics }
with loc_freelists^.internal_status do with loc_freelists^.internal_status do
begin begin
@ -999,10 +1096,10 @@ begin
if poc^.used <= 0 then if poc^.used <= 0 then
begin begin
{ decrease used blocks count } { decrease used blocks count }
if poc^.used=-1 then if poc^.used<0 then
HandleError(204); HandleError(204);
{ osblock can be freed? } { osblock can be freed? }
append_to_oslist(poc, chunksize); append_to_oslist(poc);
end; end;
result := chunksize; result := chunksize;
end; end;
@ -1064,14 +1161,11 @@ begin
result := sysfreemem_fixed(pmc); result := sysfreemem_fixed(pmc);
end; end;
function finish_waitfixedlist(loc_freelists: pfreelists): boolean; procedure finish_waitfixedlist(loc_freelists: pfreelists);
{ free to-be-freed chunks, return whether we freed anything } { free to-be-freed chunks, return whether we freed anything }
var var
pmc: pmemchunk_fixed; pmc: pmemchunk_fixed;
begin begin
if loc_freelists^.waitfixed = nil then
exit(false);
entercriticalsection(loc_freelists^.lockfixed);
while loc_freelists^.waitfixed <> nil do while loc_freelists^.waitfixed <> nil do
begin begin
{ keep next_fixed, might be destroyed } { keep next_fixed, might be destroyed }
@ -1079,6 +1173,14 @@ begin
loc_freelists^.waitfixed := pmc^.next_fixed; loc_freelists^.waitfixed := pmc^.next_fixed;
SysFreeMem_Fixed(pmc); SysFreeMem_Fixed(pmc);
end; end;
end;
function try_finish_waitfixedlist(loc_freelists: pfreelists): boolean;
begin
if loc_freelists^.waitfixed = nil then
exit(false);
entercriticalsection(loc_freelists^.lockfixed);
finish_waitfixedlist(loc_freelists);
leavecriticalsection(loc_freelists^.lockfixed); leavecriticalsection(loc_freelists^.lockfixed);
result := true; result := true;
end; end;
@ -1088,10 +1190,6 @@ procedure finish_waitvarlist(loc_freelists: pfreelists);
var var
pmcv: pmemchunk_var; pmcv: pmemchunk_var;
begin begin
loc_freelists := @freelists;
if loc_freelists^.waitvar = nil then
exit;
entercriticalsection(loc_freelists^.lockvar);
while loc_freelists^.waitvar <> nil do while loc_freelists^.waitvar <> nil do
begin begin
{ keep next_var, might be destroyed } { keep next_var, might be destroyed }
@ -1099,6 +1197,14 @@ begin
loc_freelists^.waitvar := pmcv^.next_var; loc_freelists^.waitvar := pmcv^.next_var;
SysFreeMem_Var(pmcv); SysFreeMem_Var(pmcv);
end; end;
end;
procedure try_finish_waitvarlist(loc_freelists: pfreelists);
begin
if loc_freelists^.waitvar = nil then
exit;
entercriticalsection(loc_freelists^.lockvar);
finish_waitvarlist(loc_freelists);
leavecriticalsection(loc_freelists^.lockvar); leavecriticalsection(loc_freelists^.lockvar);
end; end;
@ -1344,6 +1450,7 @@ begin
not loaded yet } not loaded yet }
loc_freelists := @freelists; loc_freelists := @freelists;
fillchar(loc_freelists^,sizeof(tfreelists),0); fillchar(loc_freelists^,sizeof(tfreelists),0);
fillchar(orphaned_freelists,sizeof(orphaned_freelists),0);
{ main freelist will be copied in memory } { main freelist will be copied in memory }
main_orig_freelists := loc_freelists; main_orig_freelists := loc_freelists;
end; end;
@ -1358,21 +1465,64 @@ begin
main_relo_freelists := loc_freelists; main_relo_freelists := loc_freelists;
initcriticalsection(loc_freelists^.lockfixed); initcriticalsection(loc_freelists^.lockfixed);
initcriticalsection(loc_freelists^.lockvar); initcriticalsection(loc_freelists^.lockvar);
initcriticalsection(orphaned_freelists.lockfixed);
initcriticalsection(orphaned_freelists.lockvar);
initcriticalsection(orphaned_oslist_lock);
modify_freelists(loc_freelists, main_relo_freelists);
if MemoryManager.RelocateHeap <> nil then if MemoryManager.RelocateHeap <> nil then
MemoryManager.RelocateHeap(); MemoryManager.RelocateHeap();
end; end;
procedure FinalizeHeap; procedure FinalizeHeap;
var var
poc : poschunk; poc, poc_next: poschunk;
i : longint; i : longint;
loc_freelists: pfreelists; loc_freelists: pfreelists;
begin begin
loc_freelists := @freelists; loc_freelists := @freelists;
finish_waitfixedlist(loc_freelists);
finish_waitvarlist(loc_freelists);
if main_relo_freelists <> nil then if main_relo_freelists <> nil then
begin begin
entercriticalsection(loc_freelists^.lockfixed);
finish_waitfixedlist(loc_freelists);
entercriticalsection(loc_freelists^.lockvar);
finish_waitvarlist(loc_freelists);
{$ifdef HAS_SYSOSFREE}
end;
poc := loc_freelists^.oslist;
while assigned(poc) do
begin
poc_next := poc^.next_free;
{ check if this os chunk was 'recycled' i.e. taken in use again }
if (poc^.size and ocrecycleflag) = 0 then
free_oschunk(loc_freelists, poc);
poc := poc_next;
end;
loc_freelists^.oslist := nil;
loc_freelists^.oscount := 0;
if main_relo_freelists <> nil then
begin
{$endif HAS_SYSOSFREE}
if main_relo_freelists = loc_freelists then
begin
donecriticalsection(orphaned_freelists.lockfixed);
donecriticalsection(orphaned_freelists.lockvar);
donecriticalsection(orphaned_oslist_lock);
end else begin
entercriticalsection(orphaned_oslist_lock);
entercriticalsection(orphaned_freelists.lockfixed);
entercriticalsection(orphaned_freelists.lockvar);
poc := modify_freelists(loc_freelists, @orphaned_freelists);
if assigned(poc) then
begin
poc^.next_any := orphaned_freelists.oslist_all;
if assigned(orphaned_freelists.oslist_all) then
orphaned_freelists.oslist_all^.prev_any := poc;
orphaned_freelists.oslist_all := loc_freelists^.oslist_all;
end;
leavecriticalsection(orphaned_freelists.lockvar);
leavecriticalsection(orphaned_freelists.lockfixed);
leavecriticalsection(orphaned_oslist_lock);
end;
donecriticalsection(loc_freelists^.lockfixed); donecriticalsection(loc_freelists^.lockfixed);
donecriticalsection(loc_freelists^.lockvar); donecriticalsection(loc_freelists^.lockvar);
end; end;
@ -1388,15 +1538,6 @@ begin
writeln('size >', sizeusagesize, ' usage ', maxsizeusage[sizeusageindex]); writeln('size >', sizeusagesize, ' usage ', maxsizeusage[sizeusageindex]);
flush(output); flush(output);
{$endif} {$endif}
{$ifdef HAS_SYSOSFREE}
while assigned(loc_freelists^.oslist) do
begin
poc:=loc_freelists^.oslist^.next;
SysOSFree(loc_freelists^.oslist, loc_freelists^.oslist^.size and sizemask);
dec(loc_freelists^.oscount);
loc_freelists^.oslist:=poc;
end;
{$endif HAS_SYSOSFREE}
end; end;
{$endif HAS_MEMORYMANAGER} {$endif HAS_MEMORYMANAGER}

View File

@ -21,6 +21,7 @@ interface
{$checkpointer off} {$checkpointer off}
{$goto on} {$goto on}
{$typedpointer on}
{$if defined(win32) or defined(wince)} {$if defined(win32) or defined(wince)}
{$define windows} {$define windows}
@ -105,6 +106,7 @@ type
end; end;
end; end;
ppheap_mem_info = ^pheap_mem_info;
pheap_mem_info = ^theap_mem_info; pheap_mem_info = ^theap_mem_info;
pheap_todo = ^theap_todo; pheap_todo = ^theap_todo;
@ -136,6 +138,24 @@ type
extra_info : pheap_extra_info; extra_info : pheap_extra_info;
end; 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 : theap_todo;
getmem_cnt,
freemem_cnt : ptrint;
getmem_size,
freemem_size : ptrint;
getmem8_size,
freemem8_size : ptrint;
error_in_heap : boolean;
inside_trace_getmem : boolean;
end;
var var
useownfile : boolean; useownfile : boolean;
ownfile : text; ownfile : text;
@ -144,22 +164,9 @@ var
{$endif EXTRA} {$endif EXTRA}
main_orig_todolist: pheap_todo; main_orig_todolist: pheap_todo;
main_relo_todolist: pheap_todo; main_relo_todolist: pheap_todo;
orphaned_info: theap_info;
threadvar threadvar
{$ifdef EXTRA} heap_info: theap_info;
heap_valid_first,
heap_valid_last : pheap_mem_info;
{$endif EXTRA}
heap_mem_root : pheap_mem_info;
heap_free_todo : theap_todo;
getmem_cnt,
freemem_cnt : ptrint;
getmem_size,
freemem_size : ptrint;
getmem8_size,
freemem8_size : ptrint;
error_in_heap : boolean;
inside_trace_getmem : boolean;
{***************************************************************************** {*****************************************************************************
Crc 32 Crc 32
@ -249,6 +256,8 @@ end;
Helpers Helpers
*****************************************************************************} *****************************************************************************}
function InternalFreeMemSize(loc_info: pheap_info; p: pointer; pp: pheap_mem_info;
size: ptrint; release_orphaned_lock: boolean): ptrint; forward;
function TraceFreeMem(p: pointer): ptrint; forward; function TraceFreeMem(p: pointer): ptrint; forward;
procedure call_stack(pp : pheap_mem_info;var ptext : text); procedure call_stack(pp : pheap_mem_info;var ptext : text);
@ -331,13 +340,13 @@ begin
call_stack(p,ptext); call_stack(p,ptext);
end; end;
function is_in_getmem_list (p : pheap_mem_info) : boolean; function is_in_getmem_list (loc_info: pheap_info; p : pheap_mem_info) : boolean;
var var
i : ptrint; i : ptrint;
pp : pheap_mem_info; pp : pheap_mem_info;
begin begin
is_in_getmem_list:=false; is_in_getmem_list:=false;
pp:=heap_mem_root; pp:=loc_info^.heap_mem_root;
i:=0; i:=0;
while pp<>nil do while pp<>nil do
begin begin
@ -355,7 +364,7 @@ begin
is_in_getmem_list:=true; is_in_getmem_list:=true;
pp:=pp^.previous; pp:=pp^.previous;
inc(i); inc(i);
if i>getmem_cnt-freemem_cnt then if i>loc_info^.getmem_cnt-loc_info^.freemem_cnt then
if useownfile then if useownfile then
writeln(ownfile,'error in linked list of heap_mem_info') writeln(ownfile,'error in linked list of heap_mem_info')
else else
@ -363,21 +372,31 @@ begin
end; end;
end; end;
procedure finish_heap_free_todo_list; procedure finish_heap_free_todo_list(loc_info: pheap_info);
var var
bp: pointer; bp: pointer;
loc_list: pheap_todo; pp: pheap_mem_info;
list: ppheap_mem_info;
begin begin
loc_list := @heap_free_todo; list := @loc_info^.heap_free_todo.list;
if loc_list^.list <> nil then repeat
pp := list^;
list^ := list^^.todonext;
bp := pointer(pp)+sizeof(theap_mem_info);
InternalFreeMemSize(loc_info,bp,pp,pp^.size,false);
//TraceFreeMem(bp);
until list^ = nil;
end;
procedure try_finish_heap_free_todo_list(loc_info: pheap_info);
var
bp: pointer;
begin
if loc_info^.heap_free_todo.list <> nil then
begin begin
entercriticalsection(loc_list^.lock); entercriticalsection(loc_info^.heap_free_todo.lock);
repeat finish_heap_free_todo_list(loc_info);
bp := pointer(loc_list^.list)+sizeof(theap_mem_info); leavecriticalsection(loc_info^.heap_free_todo.lock);
loc_list^.list := loc_list^.list^.todonext;
TraceFreeMem(bp);
until loc_list^.list = nil;
leavecriticalsection(loc_list^.lock);
end; end;
end; end;
@ -394,10 +413,12 @@ var
pl : pdword; pl : pdword;
p : pointer; p : pointer;
pp : pheap_mem_info; pp : pheap_mem_info;
loc_info: pheap_info;
begin begin
finish_heap_free_todo_list; loc_info := @heap_info;
inc(getmem_size,size); try_finish_heap_free_todo_list(loc_info);
inc(getmem8_size,((size+7) div 8)*8); inc(loc_info^.getmem_size,size);
inc(loc_info^.getmem8_size,((size+7) div 8)*8);
{ Do the real GetMem, but alloc also for the info block } { Do the real GetMem, but alloc also for the info block }
{$ifdef cpuarm} {$ifdef cpuarm}
allocsize:=(size + 3) and not 3+sizeof(theap_mem_info)+extra_info_size; allocsize:=(size + 3) and not 3+sizeof(theap_mem_info)+extra_info_size;
@ -418,7 +439,7 @@ begin
inc(p,sizeof(theap_mem_info)); inc(p,sizeof(theap_mem_info));
{ Create the info block } { Create the info block }
pp^.sig:=$DEADBEEF; pp^.sig:=$DEADBEEF;
pp^.todolist:=@heap_free_todo; pp^.todolist:=@loc_info^.heap_free_todo;
pp^.todonext:=nil; pp^.todonext:=nil;
pp^.size:=size; pp^.size:=size;
pp^.extra_info_size:=extra_info_size; pp^.extra_info_size:=extra_info_size;
@ -437,9 +458,9 @@ begin
pp^.extra_info^.displayproc:=display_extra_info_proc; pp^.extra_info^.displayproc:=display_extra_info_proc;
if assigned(fill_extra_info_proc) then if assigned(fill_extra_info_proc) then
begin begin
inside_trace_getmem:=true; loc_info^.inside_trace_getmem:=true;
fill_extra_info_proc(@pp^.extra_info^.data); fill_extra_info_proc(@pp^.extra_info^.data);
inside_trace_getmem:=false; loc_info^.inside_trace_getmem:=false;
end; end;
end end
else else
@ -466,21 +487,21 @@ begin
bp:=nil; bp:=nil;
end; end;
{ insert in the linked list } { insert in the linked list }
if heap_mem_root<>nil then if loc_info^.heap_mem_root<>nil then
heap_mem_root^.next:=pp; loc_info^.heap_mem_root^.next:=pp;
pp^.previous:=heap_mem_root; pp^.previous:=loc_info^.heap_mem_root;
pp^.next:=nil; pp^.next:=nil;
{$ifdef EXTRA} {$ifdef EXTRA}
pp^.prev_valid:=heap_valid_last; pp^.prev_valid:=loc_info^.heap_valid_last;
heap_valid_last:=pp; loc_info^.heap_valid_last:=pp;
if not assigned(heap_valid_first) then if not assigned(loc_info^.heap_valid_first) then
heap_valid_first:=pp; loc_info^.heap_valid_first:=pp;
{$endif EXTRA} {$endif EXTRA}
heap_mem_root:=pp; loc_info^.heap_mem_root:=pp;
{ must be changed before fill_extra_info is called { must be changed before fill_extra_info is called
because checkpointer can be called from within because checkpointer can be called from within
fill_extra_info PM } fill_extra_info PM }
inc(getmem_cnt); inc(loc_info^.getmem_cnt);
{ update the signature } { update the signature }
if usecrc then if usecrc then
pp^.sig:=calculate_sig(pp); pp^.sig:=calculate_sig(pp);
@ -492,60 +513,37 @@ end;
TraceFreeMem TraceFreeMem
*****************************************************************************} *****************************************************************************}
function TraceFreeMemSize(p:pointer;size:ptrint):ptrint; function CheckFreeMemSize(loc_info: pheap_info; pp: pheap_mem_info;
size, ppsize: ptrint): boolean; inline;
var var
i,ppsize : ptrint; i: ptrint;
bp : pointer; bp : pointer;
pp : pheap_mem_info; ptext : ^text;
{$ifdef EXTRA} {$ifdef EXTRA}
pp2 : pheap_mem_info; pp2 : pheap_mem_info;
{$endif} {$endif}
extra_size : ptrint;
ptext : ^text;
begin begin
if p=nil then
begin
TraceFreeMemSize:=0;
exit;
end;
pp:=pheap_mem_info(p-sizeof(theap_mem_info));
if @heap_free_todo <> pp^.todolist then
begin
if pp^.todolist = main_orig_todolist then
pp^.todolist := main_relo_todolist;
if @heap_free_todo <> pp^.todolist then
begin
entercriticalsection(pp^.todolist^.lock);
pp^.todonext := pp^.todolist^.list;
pp^.todolist^.list := pp;
leavecriticalsection(pp^.todolist^.lock);
exit(pp^.size);
end;
end;
if useownfile then if useownfile then
ptext:=@ownfile ptext:=@ownfile
else else
ptext:=@stderr; ptext:=@stderr;
inc(freemem_size,size); inc(loc_info^.freemem_size,size);
inc(freemem8_size,((size+7) div 8)*8); inc(loc_info^.freemem8_size,((size+7) div 8)*8);
ppsize:= size + sizeof(theap_mem_info)+pp^.extra_info_size;
if add_tail then
inc(ppsize,sizeof(ptrint));
if not quicktrace then if not quicktrace then
begin begin
if not(is_in_getmem_list(pp)) then if not(is_in_getmem_list(loc_info, pp)) then
RunError(204); RunError(204);
end; end;
if (pp^.sig=$AAAAAAAA) and not usecrc then if (pp^.sig=$AAAAAAAA) and not usecrc then
begin begin
error_in_heap:=true; loc_info^.error_in_heap:=true;
dump_already_free(pp,ptext^); dump_already_free(pp,ptext^);
if haltonerror then halt(1); if haltonerror then halt(1);
end end
else if ((pp^.sig<>$DEADBEEF) or usecrc) and else if ((pp^.sig<>$DEADBEEF) or usecrc) and
((pp^.sig<>calculate_sig(pp)) or not usecrc) then ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
begin begin
error_in_heap:=true; loc_info^.error_in_heap:=true;
dump_error(pp,ptext^); dump_error(pp,ptext^);
{$ifdef EXTRA} {$ifdef EXTRA}
dump_error(pp,error_file); dump_error(pp,error_file);
@ -556,7 +554,7 @@ begin
end end
else if pp^.size<>size then else if pp^.size<>size then
begin begin
error_in_heap:=true; loc_info^.error_in_heap:=true;
dump_wrong_size(pp,size,ptext^); dump_wrong_size(pp,size,ptext^);
{$ifdef EXTRA} {$ifdef EXTRA}
dump_wrong_size(pp,size,error_file); dump_wrong_size(pp,size,error_file);
@ -565,8 +563,6 @@ begin
{ don't release anything in this case !! } { don't release anything in this case !! }
exit; exit;
end; end;
{ save old values }
extra_size:=pp^.extra_info_size;
{ now it is released !! } { now it is released !! }
pp^.sig:=$AAAAAAAA; pp^.sig:=$AAAAAAAA;
if not keepreleased then if not keepreleased then
@ -575,8 +571,8 @@ begin
pp^.next^.previous:=pp^.previous; pp^.next^.previous:=pp^.previous;
if pp^.previous<>nil then if pp^.previous<>nil then
pp^.previous^.next:=pp^.next; pp^.previous^.next:=pp^.next;
if pp=heap_mem_root then if pp=loc_info^.heap_mem_root then
heap_mem_root:=heap_mem_root^.previous; loc_info^.heap_mem_root:=loc_info^.heap_mem_root^.previous;
end end
else else
begin begin
@ -587,48 +583,102 @@ begin
bp:=get_caller_frame(bp); bp:=get_caller_frame(bp);
end; end;
end; end;
inc(freemem_cnt); inc(loc_info^.freemem_cnt);
{ clear the memory } { clear the memory, $F0 will lead to GFP if used as pointer ! }
fillchar(p^,size,#240); { $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 !! } { this way we keep all info about all released memory !! }
if keepreleased then if keepreleased then
begin begin
{$ifdef EXTRA} {$ifdef EXTRA}
{ We want to check if the memory was changed after release !! } { We want to check if the memory was changed after release !! }
pp^.release_sig:=calculate_release_sig(pp); pp^.release_sig:=calculate_release_sig(pp);
if pp=heap_valid_last then if pp=loc_info^.heap_valid_last then
begin begin
heap_valid_last:=pp^.prev_valid; loc_info^.heap_valid_last:=pp^.prev_valid;
if pp=heap_valid_first then if pp=loc_info^.heap_valid_first then
heap_valid_first:=nil; loc_info^.heap_valid_first:=nil;
TraceFreememsize:=size; exit(false);
exit;
end; end;
pp2:=heap_valid_last; pp2:=loc_info^.heap_valid_last;
while assigned(pp2) do while assigned(pp2) do
begin begin
if pp2^.prev_valid=pp then if pp2^.prev_valid=pp then
begin begin
pp2^.prev_valid:=pp^.prev_valid; pp2^.prev_valid:=pp^.prev_valid;
if pp=heap_valid_first then if pp=loc_info^.heap_valid_first then
heap_valid_first:=pp2; loc_info^.heap_valid_first:=pp2;
TraceFreememsize:=size; exit(false);
exit;
end end
else else
pp2:=pp2^.prev_valid; pp2:=pp2^.prev_valid;
end; end;
{$endif EXTRA} {$endif EXTRA}
TraceFreememsize:=size; exit(false);
exit;
end; end;
{ release the normal memory at least } CheckFreeMemSize:=true;
i:=SysFreeMemSize(pp,ppsize); end;
{ return the correct size }
dec(i,sizeof(theap_mem_info)+extra_size); function InternalFreeMemSize(loc_info: pheap_info; p: pointer; pp: pheap_mem_info;
if add_tail then size: ptrint; release_orphaned_lock: boolean): ptrint;
dec(i,sizeof(ptrint)); var
TraceFreeMemSize:=i; i,ppsize : ptrint;
bp : pointer;
extra_size: ptrint;
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(ptrint));
{ do various checking }
release_mem := CheckFreeMemSize(loc_info, pp, size, ppsize);
if release_orphaned_lock then
leavecriticalsection(orphaned_info.heap_free_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(ptrint));
InternalFreeMemSize:=i;
end else
InternalFreeMemSize:=size;
end;
function TraceFreeMemSize(p:pointer;size:ptrint):ptrint;
var
loc_info: pheap_info;
pp: pheap_mem_info;
begin
if p=nil then
begin
TraceFreeMemSize:=0;
exit;
end;
loc_info:=@heap_info;
pp:=pheap_mem_info(p-sizeof(theap_mem_info));
if @loc_info^.heap_free_todo <> pp^.todolist then
begin
if pp^.todolist = main_orig_todolist then
pp^.todolist := main_relo_todolist;
entercriticalsection(pp^.todolist^.lock);
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^.list;
pp^.todolist^.list := pp;
leavecriticalsection(pp^.todolist^.lock);
exit(pp^.size);
end;
end;
TraceFreeMemSize:=InternalFreeMemSize(loc_info,p,pp,size,loc_info = @orphaned_info);
end; end;
@ -691,6 +741,7 @@ var
oldexactsize : ptrint; oldexactsize : ptrint;
old_fill_extra_info_proc : tfillextrainfoproc; old_fill_extra_info_proc : tfillextrainfoproc;
old_display_extra_info_proc : tdisplayextrainfoproc; old_display_extra_info_proc : tdisplayextrainfoproc;
loc_info: pheap_info;
begin begin
{ Free block? } { Free block? }
if size=0 then if size=0 then
@ -709,12 +760,13 @@ begin
exit; exit;
end; end;
{ Resize block } { Resize block }
loc_info:=@heap_info;
pp:=pheap_mem_info(p-sizeof(theap_mem_info)); pp:=pheap_mem_info(p-sizeof(theap_mem_info));
{ test block } { test block }
if ((pp^.sig<>$DEADBEEF) or usecrc) and if ((pp^.sig<>$DEADBEEF) or usecrc) and
((pp^.sig<>calculate_sig(pp)) or not usecrc) then ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
begin begin
error_in_heap:=true; loc_info^.error_in_heap:=true;
if useownfile then if useownfile then
dump_error(pp,ownfile) dump_error(pp,ownfile)
else else
@ -795,10 +847,10 @@ begin
end; end;
{ adjust like a freemem and then a getmem, so you get correct { adjust like a freemem and then a getmem, so you get correct
results in the summary display } results in the summary display }
inc(freemem_size,oldsize); inc(loc_info^.freemem_size,oldsize);
inc(freemem8_size,((oldsize+7) div 8)*8); inc(loc_info^.freemem8_size,((oldsize+7) div 8)*8);
inc(getmem_size,size); inc(loc_info^.getmem_size,size);
inc(getmem8_size,((size+7) div 8)*8); inc(loc_info^.getmem8_size,((size+7) div 8)*8);
{ generate new backtrace } { generate new backtrace }
bp:=get_caller_frame(get_frame); bp:=get_caller_frame(get_frame);
for i:=1 to tracesize do for i:=1 to tracesize do
@ -862,6 +914,7 @@ procedure CheckPointer(p : pointer); [public, alias : 'FPC_CHECKPOINTER'];
var var
i : ptrint; i : ptrint;
pp : pheap_mem_info; pp : pheap_mem_info;
loc_info: pheap_info;
{$ifdef go32v2} {$ifdef go32v2}
get_ebp,stack_top : longword; get_ebp,stack_top : longword;
data_end : longword; data_end : longword;
@ -877,7 +930,7 @@ begin
runerror(204); runerror(204);
i:=0; i:=0;
loc_info:=@heap_info;
if useownfile then if useownfile then
ptext:=@ownfile ptext:=@ownfile
else else
@ -953,7 +1006,7 @@ begin
{ first try valid list faster } { first try valid list faster }
{$ifdef EXTRA} {$ifdef EXTRA}
pp:=heap_valid_last; pp:=loc_info^.heap_valid_last;
while pp<>nil do while pp<>nil do
begin begin
{ inside this valid block ! } { inside this valid block ! }
@ -965,8 +1018,8 @@ begin
if ((pp^.sig=$DEADBEEF) and not usecrc) or if ((pp^.sig=$DEADBEEF) and not usecrc) or
((pp^.sig=calculate_sig(pp)) and usecrc) or ((pp^.sig=calculate_sig(pp)) and usecrc) or
{ special case of the fill_extra_info call } { special case of the fill_extra_info call }
((pp=heap_valid_last) and usecrc and (pp^.sig=$DEADBEEF) ((pp=loc_info^.heap_valid_last) and usecrc and (pp^.sig=$DEADBEEF)
and inside_trace_getmem) then and loc_info^.inside_trace_getmem) then
goto _exit goto _exit
else else
begin begin
@ -978,7 +1031,7 @@ begin
else else
pp:=pp^.prev_valid; pp:=pp^.prev_valid;
inc(i); inc(i);
if i>getmem_cnt-freemem_cnt then if i>loc_info^.getmem_cnt-loc_info^.freemem_cnt then
begin begin
writeln(ptext^,'error in linked list of heap_mem_info'); writeln(ptext^,'error in linked list of heap_mem_info');
halt(1); halt(1);
@ -986,7 +1039,7 @@ begin
end; end;
i:=0; i:=0;
{$endif EXTRA} {$endif EXTRA}
pp:=heap_mem_root; pp:=loc_info^.heap_mem_root;
while pp<>nil do while pp<>nil do
begin begin
{ inside this block ! } { inside this block ! }
@ -1004,7 +1057,7 @@ begin
end; end;
pp:=pp^.previous; pp:=pp^.previous;
inc(i); inc(i);
if i>getmem_cnt then if i>loc_info^.getmem_cnt then
begin begin
writeln(ptext^,'error in linked list of heap_mem_info'); writeln(ptext^,'error in linked list of heap_mem_info');
halt(1); halt(1);
@ -1027,16 +1080,21 @@ var
ExpectedHeapFree : ptrint; ExpectedHeapFree : ptrint;
status : TFPCHeapStatus; status : TFPCHeapStatus;
ptext : ^text; ptext : ^text;
loc_info: pheap_info;
begin begin
loc_info:=@heap_info;
if useownfile then if useownfile then
ptext:=@ownfile ptext:=@ownfile
else else
ptext:=@stderr; ptext:=@stderr;
pp:=heap_mem_root; pp:=loc_info^.heap_mem_root;
Writeln(ptext^,'Heap dump by heaptrc unit'); Writeln(ptext^,'Heap dump by heaptrc unit');
Writeln(ptext^,getmem_cnt, ' memory blocks allocated : ',getmem_size,'/',getmem8_size); Writeln(ptext^,loc_info^.getmem_cnt, ' memory blocks allocated : ',
Writeln(ptext^,freemem_cnt,' memory blocks freed : ',freemem_size,'/',freemem8_size); loc_info^.getmem_size,'/',loc_info^.getmem8_size);
Writeln(ptext^,getmem_cnt-freemem_cnt,' unfreed memory blocks : ',getmem_size-freemem_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; status:=SysGetFPCHeapStatus;
Write(ptext^,'True heap size : ',status.CurrHeapSize); Write(ptext^,'True heap size : ',status.CurrHeapSize);
if EntryMemUsed > 0 then if EntryMemUsed > 0 then
@ -1044,11 +1102,13 @@ begin
else else
Writeln(ptext^); Writeln(ptext^);
Writeln(ptext^,'True free heap : ',status.CurrHeapFree); Writeln(ptext^,'True free heap : ',status.CurrHeapFree);
ExpectedHeapFree:=status.CurrHeapSize-(getmem8_size-freemem8_size)- ExpectedHeapFree:=status.CurrHeapSize
(getmem_cnt-freemem_cnt)*(sizeof(theap_mem_info)+extra_info_size)-EntryMemUsed; -(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 If ExpectedHeapFree<>status.CurrHeapFree then
Writeln(ptext^,'Should be : ',ExpectedHeapFree); Writeln(ptext^,'Should be : ',ExpectedHeapFree);
i:=getmem_cnt-freemem_cnt; i:=loc_info^.getmem_cnt-loc_info^.freemem_cnt;
while pp<>nil do while pp<>nil do
begin begin
if i<0 then if i<0 then
@ -1071,20 +1131,20 @@ begin
{$ifdef EXTRA} {$ifdef EXTRA}
dump_error(pp,error_file); dump_error(pp,error_file);
{$endif EXTRA} {$endif EXTRA}
error_in_heap:=true; loc_info^.error_in_heap:=true;
end end
{$ifdef EXTRA} {$ifdef EXTRA}
else if pp^.release_sig<>calculate_release_sig(pp) then else if pp^.release_sig<>calculate_release_sig(pp) then
begin begin
dump_change_after(pp,ptext^); dump_change_after(pp,ptext^);
dump_change_after(pp,error_file); dump_change_after(pp,error_file);
error_in_heap:=true; loc_info^.error_in_heap:=true;
end end
{$endif EXTRA} {$endif EXTRA}
; ;
pp:=pp^.previous; pp:=pp^.previous;
end; end;
if HaltOnNotReleased and (getmem_cnt<>freemem_cnt) then if HaltOnNotReleased and (loc_info^.getmem_cnt<>loc_info^.freemem_cnt) then
exitcode:=203; exitcode:=203;
end; end;
@ -1104,38 +1164,93 @@ end;
*****************************************************************************} *****************************************************************************}
procedure TraceInitThread; procedure TraceInitThread;
var
loc_info: pheap_info;
begin begin
loc_info := @heap_info;
{$ifdef EXTRA} {$ifdef EXTRA}
heap_valid_first := nil; loc_info^.heap_valid_first := nil;
heap_valid_last := nil; loc_info^.heap_valid_last := nil;
{$endif} {$endif}
heap_mem_root := nil; loc_info^.heap_mem_root := nil;
getmem_cnt := 0; loc_info^.getmem_cnt := 0;
freemem_cnt := 0; loc_info^.freemem_cnt := 0;
getmem_size := 0; loc_info^.getmem_size := 0;
freemem_size := 0; loc_info^.freemem_size := 0;
getmem8_size := 0; loc_info^.getmem8_size := 0;
freemem8_size := 0; loc_info^.freemem8_size := 0;
error_in_heap := false; loc_info^.error_in_heap := false;
inside_trace_getmem := false; loc_info^.inside_trace_getmem := false;
EntryMemUsed := SysGetFPCHeapStatus.CurrHeapUsed; EntryMemUsed := SysGetFPCHeapStatus.CurrHeapUsed;
if main_relo_todolist <> nil then if main_relo_todolist <> nil then
initcriticalsection(heap_free_todo.lock); initcriticalsection(loc_info^.heap_free_todo.lock);
end; end;
procedure TraceRelocateHeap; procedure TraceRelocateHeap;
begin begin
main_relo_todolist := @heap_free_todo; main_relo_todolist := @heap_info.heap_free_todo;
initcriticalsection(main_relo_todolist^.lock); initcriticalsection(main_relo_todolist^.lock);
initcriticalsection(orphaned_info.heap_free_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.list <> nil then
finish_heap_free_todo_list(src_info);
if dst_info^.heap_free_todo.list <> 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; end;
procedure TraceExitThread; procedure TraceExitThread;
var
loc_info: pheap_info;
heap_mem: pheap_mem_info;
begin begin
finish_heap_free_todo_list; loc_info := @heap_info;
if main_relo_todolist <> nil then entercriticalsection(loc_info^.heap_free_todo.lock);
donecriticalsection(heap_free_todo.lock); entercriticalsection(orphaned_info.heap_free_todo.lock);
if not error_in_heap then { if not main thread exiting, move bookkeeping to orphaned heap }
if (@loc_info^.heap_free_todo <> main_orig_todolist)
and (@loc_info^.heap_free_todo <> main_relo_todolist) then
begin
move_heap_info(loc_info, @orphaned_info);
end else
if not loc_info^.error_in_heap then
begin
move_heap_info(@orphaned_info, loc_info);
Dumpheap; Dumpheap;
end;
leavecriticalsection(orphaned_info.heap_free_todo.lock);
donecriticalsection(loc_info^.heap_free_todo.lock);
end; end;
function TraceGetHeapStatus:THeapStatus; function TraceGetHeapStatus:THeapStatus;
@ -1207,7 +1322,7 @@ const
procedure TraceInit; procedure TraceInit;
begin begin
MakeCRC32Tbl; MakeCRC32Tbl;
main_orig_todolist := @heap_free_todo; main_orig_todolist := @heap_info.heap_free_todo;
main_relo_todolist := nil; main_relo_todolist := nil;
TraceInitThread; TraceInitThread;
SetMemoryManager(TraceManager); SetMemoryManager(TraceManager);
@ -1246,8 +1361,9 @@ begin
exit; exit;
end; end;
TraceExitThread; TraceExitThread;
if error_in_heap and (exitcode=0) then if heap_info.error_in_heap and (exitcode=0) then
exitcode:=203; exitcode:=203;
donecriticalsection(orphaned_info.heap_free_todo.lock);
{$ifdef EXTRA} {$ifdef EXTRA}
Close(error_file); Close(error_file);
{$endif EXTRA} {$endif EXTRA}