mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-20 04:59:26 +02:00
* heap manager: handle freeing of block owned by exited thread
* heap trace: ditto git-svn-id: trunk@7649 -
This commit is contained in:
parent
c7037df254
commit
c226f6fd44
307
rtl/inc/heap.inc
307
rtl/inc/heap.inc
@ -134,8 +134,10 @@ type
|
||||
poschunk = ^toschunk;
|
||||
toschunk = record
|
||||
size : ptrint;
|
||||
next : poschunk;
|
||||
used : ptrint;
|
||||
next_free : poschunk;
|
||||
prev_any : poschunk;
|
||||
next_any : poschunk;
|
||||
used : ptrint; { 0: free, >0: fixed, -1: var }
|
||||
freelists : pfreelists;
|
||||
{ padding inserted automatically by alloc_oschunk }
|
||||
end;
|
||||
@ -178,8 +180,9 @@ type
|
||||
tfixedfreelists = array[1..maxblockindex] of pmemchunk_fixed;
|
||||
|
||||
tfreelists = record
|
||||
oslist : poschunk;
|
||||
oscount : dword;
|
||||
oslist : poschunk; { os chunks free, available for use }
|
||||
oscount : dword; { number of os chunks on oslist }
|
||||
oslist_all : poschunk; { all os chunks allocated }
|
||||
fixedlists : tfixedfreelists;
|
||||
varlist : pmemchunk_var;
|
||||
{ chunks waiting to be freed from other thread }
|
||||
@ -205,6 +208,8 @@ const
|
||||
var
|
||||
main_orig_freelists : pfreelists;
|
||||
main_relo_freelists : pfreelists;
|
||||
orphaned_freelists : tfreelists;
|
||||
orphaned_oslist_lock : trtlcriticalsection;
|
||||
threadvar
|
||||
freelists : tfreelists;
|
||||
|
||||
@ -398,6 +403,14 @@ end;
|
||||
{$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
|
||||
@ -453,12 +466,19 @@ begin
|
||||
freelists.varlist := pmc^.next_var;
|
||||
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
|
||||
pmc, pmc_end: pmemchunk_fixed;
|
||||
fixedlist: ppmemchunk_fixed;
|
||||
chunksize: ptrint;
|
||||
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);
|
||||
chunksize := pmc^.size and fixedsizemask;
|
||||
pmc_end := pmemchunk_fixed(pointer(poc)+(poc^.size and sizemask)-chunksize);
|
||||
fixedlist := @poc^.freelists^.fixedlists[chunksize shr blockshift];
|
||||
repeat
|
||||
@ -467,9 +487,24 @@ begin
|
||||
until pmc > pmc_end;
|
||||
end;
|
||||
|
||||
procedure append_to_oslist(poc: poschunk; chunksize: ptrint);
|
||||
procedure free_oschunk(loc_freelists: pfreelists; poc: poschunk);
|
||||
var
|
||||
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;
|
||||
begin
|
||||
loc_freelists := poc^.freelists;
|
||||
@ -482,32 +517,22 @@ begin
|
||||
end;
|
||||
{ decide whether to free block or add to list }
|
||||
{$ifdef HAS_SYSOSFREE}
|
||||
pocsize := poc^.size and sizemask;
|
||||
if (loc_freelists^.oscount >= MaxKeptOSChunks) or
|
||||
(pocsize > growheapsize2) then
|
||||
((poc^.size and sizemask) > growheapsize2) then
|
||||
begin
|
||||
if chunksize <> 0 then
|
||||
remove_all_from_list_fixed(chunksize, poc);
|
||||
dec(loc_freelists^.internal_status.currheapsize, pocsize);
|
||||
SysOSFree(poc, pocsize);
|
||||
free_oschunk(loc_freelists, poc);
|
||||
end
|
||||
else
|
||||
begin
|
||||
{$endif}
|
||||
poc^.next := loc_freelists^.oslist;
|
||||
poc^.next_free := loc_freelists^.oslist;
|
||||
loc_freelists^.oslist := poc;
|
||||
inc(loc_freelists^.oscount);
|
||||
{$ifdef HAS_SYSOSFREE}
|
||||
end;
|
||||
end;
|
||||
{$endif}
|
||||
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);
|
||||
var
|
||||
poc: poschunk;
|
||||
@ -515,8 +540,42 @@ begin
|
||||
// block eligable for freeing
|
||||
poc := pointer(pmc)-varfirstoffset;
|
||||
remove_from_list_var(pmc);
|
||||
clear_oschunk_on_freelist_fixed_flag(poc);
|
||||
append_to_oslist(poc, 0);
|
||||
append_to_oslist(poc);
|
||||
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;
|
||||
|
||||
{*****************************************************************************
|
||||
@ -627,6 +686,47 @@ end;
|
||||
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;
|
||||
var
|
||||
pmc,
|
||||
@ -650,38 +750,35 @@ begin
|
||||
else
|
||||
maxsize := high(ptrint);
|
||||
{ blocks available in freelist? }
|
||||
poc := loc_freelists^.oslist;
|
||||
prev_poc := nil;
|
||||
while poc <> nil do
|
||||
poc := find_free_oschunk(loc_freelists, minsize, maxsize, size);
|
||||
if not assigned(poc) and (assigned(orphaned_freelists.waitfixed)
|
||||
or assigned(orphaned_freelists.waitvar) or (orphaned_freelists.oscount > 0)) then
|
||||
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;
|
||||
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
|
||||
entercriticalsection(orphaned_oslist_lock);
|
||||
try_finish_waitfixedlist(@orphaned_freelists);
|
||||
try_finish_waitvarlist(@orphaned_freelists);
|
||||
if orphaned_freelists.oscount > 0 then
|
||||
begin
|
||||
size := pocsize;
|
||||
if prev_poc = nil then
|
||||
loc_freelists^.oslist := poc^.next
|
||||
else
|
||||
prev_poc^.next := poc^.next;
|
||||
dec(loc_freelists^.oscount);
|
||||
pmc := pmemchunk_fixed(pointer(poc)+fixedfirstoffset);
|
||||
if pmc^.size <> 0 then
|
||||
remove_all_from_list_fixed(pmc^.size and fixedsizemask, poc);
|
||||
break;
|
||||
{ blocks available in orphaned freelist ? }
|
||||
poc := find_free_oschunk(@orphaned_freelists, minsize, maxsize, size);
|
||||
if assigned(poc) then
|
||||
begin
|
||||
{ adopt this os chunk }
|
||||
poc^.freelists := loc_freelists;
|
||||
if assigned(poc^.prev_any) then
|
||||
poc^.prev_any^.next_any := poc^.next_any
|
||||
else
|
||||
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;
|
||||
prev_poc := poc;
|
||||
poc := poc^.next;
|
||||
leavecriticalsection(orphaned_oslist_lock);
|
||||
end;
|
||||
if poc = nil then
|
||||
begin
|
||||
@ -730,10 +827,13 @@ begin
|
||||
HandleError(203);
|
||||
end;
|
||||
end;
|
||||
{ prevent thinking this os chunk is on some freelist }
|
||||
clear_oschunk_on_freelist_fixed_flag(poc);
|
||||
poc^.next := nil;
|
||||
poc^.next_free := nil;
|
||||
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 }
|
||||
status := @loc_freelists^.internal_status;
|
||||
inc(status^.currheapsize, size);
|
||||
@ -741,10 +841,10 @@ begin
|
||||
status^.maxheapsize := status^.currheapsize;
|
||||
end;
|
||||
{ initialize os-block }
|
||||
poc^.used := 0;
|
||||
poc^.size := size;
|
||||
if chunkindex<>0 then
|
||||
begin
|
||||
poc^.used := 0;
|
||||
{ chop os chunk in fixedsize parts,
|
||||
maximum of $ffff elements are allowed, otherwise
|
||||
there will be an overflow }
|
||||
@ -774,6 +874,7 @@ begin
|
||||
end
|
||||
else
|
||||
begin
|
||||
poc^.used := -1;
|
||||
{ we need to align the user pointers to 8 byte at least for
|
||||
mmx/sse and doubles on sparc, align to 16 bytes }
|
||||
result := pointer(poc)+varfirstoffset;
|
||||
@ -789,9 +890,6 @@ end;
|
||||
SysGetMem
|
||||
*****************************************************************************}
|
||||
|
||||
function finish_waitfixedlist(loc_freelists: pfreelists): boolean; forward;
|
||||
procedure finish_waitvarlist(loc_freelists: pfreelists); forward;
|
||||
|
||||
function SysGetMem_Fixed(chunksize: ptrint): pointer;
|
||||
var
|
||||
pmc, pmc_next: pmemchunk_fixed;
|
||||
@ -814,7 +912,7 @@ begin
|
||||
dec(loc_freelists^.oscount);
|
||||
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 }
|
||||
exit(SysGetMem_Fixed(chunksize))
|
||||
else
|
||||
@ -856,7 +954,7 @@ begin
|
||||
result:=nil;
|
||||
{ free pending items }
|
||||
loc_freelists := @freelists;
|
||||
finish_waitvarlist(loc_freelists);
|
||||
try_finish_waitvarlist(loc_freelists);
|
||||
pbest := nil;
|
||||
pcurr := loc_freelists^.varlist;
|
||||
iter := high(longint);
|
||||
@ -893,7 +991,6 @@ begin
|
||||
size := split_block(pcurr, size);
|
||||
{ flag block as used }
|
||||
pcurr^.size := pcurr^.size or usedflag;
|
||||
pcurr^.freelists := loc_freelists;
|
||||
{ statistics }
|
||||
with loc_freelists^.internal_status do
|
||||
begin
|
||||
@ -999,10 +1096,10 @@ begin
|
||||
if poc^.used <= 0 then
|
||||
begin
|
||||
{ decrease used blocks count }
|
||||
if poc^.used=-1 then
|
||||
if poc^.used<0 then
|
||||
HandleError(204);
|
||||
{ osblock can be freed? }
|
||||
append_to_oslist(poc, chunksize);
|
||||
append_to_oslist(poc);
|
||||
end;
|
||||
result := chunksize;
|
||||
end;
|
||||
@ -1064,14 +1161,11 @@ begin
|
||||
result := sysfreemem_fixed(pmc);
|
||||
end;
|
||||
|
||||
function finish_waitfixedlist(loc_freelists: pfreelists): boolean;
|
||||
procedure finish_waitfixedlist(loc_freelists: pfreelists);
|
||||
{ free to-be-freed chunks, return whether we freed anything }
|
||||
var
|
||||
pmc: pmemchunk_fixed;
|
||||
begin
|
||||
if loc_freelists^.waitfixed = nil then
|
||||
exit(false);
|
||||
entercriticalsection(loc_freelists^.lockfixed);
|
||||
while loc_freelists^.waitfixed <> nil do
|
||||
begin
|
||||
{ keep next_fixed, might be destroyed }
|
||||
@ -1079,6 +1173,14 @@ begin
|
||||
loc_freelists^.waitfixed := pmc^.next_fixed;
|
||||
SysFreeMem_Fixed(pmc);
|
||||
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);
|
||||
result := true;
|
||||
end;
|
||||
@ -1088,10 +1190,6 @@ procedure finish_waitvarlist(loc_freelists: pfreelists);
|
||||
var
|
||||
pmcv: pmemchunk_var;
|
||||
begin
|
||||
loc_freelists := @freelists;
|
||||
if loc_freelists^.waitvar = nil then
|
||||
exit;
|
||||
entercriticalsection(loc_freelists^.lockvar);
|
||||
while loc_freelists^.waitvar <> nil do
|
||||
begin
|
||||
{ keep next_var, might be destroyed }
|
||||
@ -1099,6 +1197,14 @@ begin
|
||||
loc_freelists^.waitvar := pmcv^.next_var;
|
||||
SysFreeMem_Var(pmcv);
|
||||
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);
|
||||
end;
|
||||
|
||||
@ -1344,6 +1450,7 @@ begin
|
||||
not loaded yet }
|
||||
loc_freelists := @freelists;
|
||||
fillchar(loc_freelists^,sizeof(tfreelists),0);
|
||||
fillchar(orphaned_freelists,sizeof(orphaned_freelists),0);
|
||||
{ main freelist will be copied in memory }
|
||||
main_orig_freelists := loc_freelists;
|
||||
end;
|
||||
@ -1358,21 +1465,64 @@ begin
|
||||
main_relo_freelists := loc_freelists;
|
||||
initcriticalsection(loc_freelists^.lockfixed);
|
||||
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
|
||||
MemoryManager.RelocateHeap();
|
||||
end;
|
||||
|
||||
procedure FinalizeHeap;
|
||||
var
|
||||
poc : poschunk;
|
||||
poc, poc_next: poschunk;
|
||||
i : longint;
|
||||
loc_freelists: pfreelists;
|
||||
begin
|
||||
loc_freelists := @freelists;
|
||||
finish_waitfixedlist(loc_freelists);
|
||||
finish_waitvarlist(loc_freelists);
|
||||
if main_relo_freelists <> nil then
|
||||
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^.lockvar);
|
||||
end;
|
||||
@ -1388,15 +1538,6 @@ begin
|
||||
writeln('size >', sizeusagesize, ' usage ', maxsizeusage[sizeusageindex]);
|
||||
flush(output);
|
||||
{$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;
|
||||
|
||||
{$endif HAS_MEMORYMANAGER}
|
||||
|
@ -21,6 +21,7 @@ interface
|
||||
|
||||
{$checkpointer off}
|
||||
{$goto on}
|
||||
{$typedpointer on}
|
||||
|
||||
{$if defined(win32) or defined(wince)}
|
||||
{$define windows}
|
||||
@ -105,6 +106,7 @@ type
|
||||
end;
|
||||
end;
|
||||
|
||||
ppheap_mem_info = ^pheap_mem_info;
|
||||
pheap_mem_info = ^theap_mem_info;
|
||||
|
||||
pheap_todo = ^theap_todo;
|
||||
@ -136,6 +138,24 @@ type
|
||||
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 : 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
|
||||
useownfile : boolean;
|
||||
ownfile : text;
|
||||
@ -144,22 +164,9 @@ var
|
||||
{$endif EXTRA}
|
||||
main_orig_todolist: pheap_todo;
|
||||
main_relo_todolist: pheap_todo;
|
||||
orphaned_info: theap_info;
|
||||
threadvar
|
||||
{$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;
|
||||
|
||||
heap_info: theap_info;
|
||||
|
||||
{*****************************************************************************
|
||||
Crc 32
|
||||
@ -249,6 +256,8 @@ end;
|
||||
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;
|
||||
|
||||
procedure call_stack(pp : pheap_mem_info;var ptext : text);
|
||||
@ -331,13 +340,13 @@ begin
|
||||
call_stack(p,ptext);
|
||||
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
|
||||
i : ptrint;
|
||||
pp : pheap_mem_info;
|
||||
begin
|
||||
is_in_getmem_list:=false;
|
||||
pp:=heap_mem_root;
|
||||
pp:=loc_info^.heap_mem_root;
|
||||
i:=0;
|
||||
while pp<>nil do
|
||||
begin
|
||||
@ -355,7 +364,7 @@ begin
|
||||
is_in_getmem_list:=true;
|
||||
pp:=pp^.previous;
|
||||
inc(i);
|
||||
if i>getmem_cnt-freemem_cnt then
|
||||
if i>loc_info^.getmem_cnt-loc_info^.freemem_cnt then
|
||||
if useownfile then
|
||||
writeln(ownfile,'error in linked list of heap_mem_info')
|
||||
else
|
||||
@ -363,21 +372,31 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure finish_heap_free_todo_list;
|
||||
procedure finish_heap_free_todo_list(loc_info: pheap_info);
|
||||
var
|
||||
bp: pointer;
|
||||
loc_list: pheap_todo;
|
||||
pp: pheap_mem_info;
|
||||
list: ppheap_mem_info;
|
||||
begin
|
||||
loc_list := @heap_free_todo;
|
||||
if loc_list^.list <> nil then
|
||||
list := @loc_info^.heap_free_todo.list;
|
||||
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
|
||||
entercriticalsection(loc_list^.lock);
|
||||
repeat
|
||||
bp := pointer(loc_list^.list)+sizeof(theap_mem_info);
|
||||
loc_list^.list := loc_list^.list^.todonext;
|
||||
TraceFreeMem(bp);
|
||||
until loc_list^.list = nil;
|
||||
leavecriticalsection(loc_list^.lock);
|
||||
entercriticalsection(loc_info^.heap_free_todo.lock);
|
||||
finish_heap_free_todo_list(loc_info);
|
||||
leavecriticalsection(loc_info^.heap_free_todo.lock);
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -394,10 +413,12 @@ var
|
||||
pl : pdword;
|
||||
p : pointer;
|
||||
pp : pheap_mem_info;
|
||||
loc_info: pheap_info;
|
||||
begin
|
||||
finish_heap_free_todo_list;
|
||||
inc(getmem_size,size);
|
||||
inc(getmem8_size,((size+7) div 8)*8);
|
||||
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) div 8)*8);
|
||||
{ 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;
|
||||
@ -418,7 +439,7 @@ begin
|
||||
inc(p,sizeof(theap_mem_info));
|
||||
{ Create the info block }
|
||||
pp^.sig:=$DEADBEEF;
|
||||
pp^.todolist:=@heap_free_todo;
|
||||
pp^.todolist:=@loc_info^.heap_free_todo;
|
||||
pp^.todonext:=nil;
|
||||
pp^.size:=size;
|
||||
pp^.extra_info_size:=extra_info_size;
|
||||
@ -437,9 +458,9 @@ begin
|
||||
pp^.extra_info^.displayproc:=display_extra_info_proc;
|
||||
if assigned(fill_extra_info_proc) then
|
||||
begin
|
||||
inside_trace_getmem:=true;
|
||||
loc_info^.inside_trace_getmem:=true;
|
||||
fill_extra_info_proc(@pp^.extra_info^.data);
|
||||
inside_trace_getmem:=false;
|
||||
loc_info^.inside_trace_getmem:=false;
|
||||
end;
|
||||
end
|
||||
else
|
||||
@ -466,21 +487,21 @@ begin
|
||||
bp:=nil;
|
||||
end;
|
||||
{ insert in the linked list }
|
||||
if heap_mem_root<>nil then
|
||||
heap_mem_root^.next:=pp;
|
||||
pp^.previous:=heap_mem_root;
|
||||
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:=heap_valid_last;
|
||||
heap_valid_last:=pp;
|
||||
if not assigned(heap_valid_first) then
|
||||
heap_valid_first:=pp;
|
||||
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}
|
||||
heap_mem_root:=pp;
|
||||
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(getmem_cnt);
|
||||
inc(loc_info^.getmem_cnt);
|
||||
{ update the signature }
|
||||
if usecrc then
|
||||
pp^.sig:=calculate_sig(pp);
|
||||
@ -492,60 +513,37 @@ end;
|
||||
TraceFreeMem
|
||||
*****************************************************************************}
|
||||
|
||||
function TraceFreeMemSize(p:pointer;size:ptrint):ptrint;
|
||||
function CheckFreeMemSize(loc_info: pheap_info; pp: pheap_mem_info;
|
||||
size, ppsize: ptrint): boolean; inline;
|
||||
var
|
||||
i,ppsize : ptrint;
|
||||
i: ptrint;
|
||||
bp : pointer;
|
||||
pp : pheap_mem_info;
|
||||
ptext : ^text;
|
||||
{$ifdef EXTRA}
|
||||
pp2 : pheap_mem_info;
|
||||
{$endif}
|
||||
extra_size : ptrint;
|
||||
ptext : ^text;
|
||||
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
|
||||
ptext:=@ownfile
|
||||
else
|
||||
ptext:=@stderr;
|
||||
inc(freemem_size,size);
|
||||
inc(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));
|
||||
inc(loc_info^.freemem_size,size);
|
||||
inc(loc_info^.freemem8_size,((size+7) div 8)*8);
|
||||
if not quicktrace then
|
||||
begin
|
||||
if not(is_in_getmem_list(pp)) then
|
||||
if not(is_in_getmem_list(loc_info, pp)) then
|
||||
RunError(204);
|
||||
end;
|
||||
if (pp^.sig=$AAAAAAAA) and not usecrc then
|
||||
begin
|
||||
error_in_heap:=true;
|
||||
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
|
||||
error_in_heap:=true;
|
||||
loc_info^.error_in_heap:=true;
|
||||
dump_error(pp,ptext^);
|
||||
{$ifdef EXTRA}
|
||||
dump_error(pp,error_file);
|
||||
@ -556,7 +554,7 @@ begin
|
||||
end
|
||||
else if pp^.size<>size then
|
||||
begin
|
||||
error_in_heap:=true;
|
||||
loc_info^.error_in_heap:=true;
|
||||
dump_wrong_size(pp,size,ptext^);
|
||||
{$ifdef EXTRA}
|
||||
dump_wrong_size(pp,size,error_file);
|
||||
@ -565,8 +563,6 @@ begin
|
||||
{ don't release anything in this case !! }
|
||||
exit;
|
||||
end;
|
||||
{ save old values }
|
||||
extra_size:=pp^.extra_info_size;
|
||||
{ now it is released !! }
|
||||
pp^.sig:=$AAAAAAAA;
|
||||
if not keepreleased then
|
||||
@ -575,8 +571,8 @@ begin
|
||||
pp^.next^.previous:=pp^.previous;
|
||||
if pp^.previous<>nil then
|
||||
pp^.previous^.next:=pp^.next;
|
||||
if pp=heap_mem_root then
|
||||
heap_mem_root:=heap_mem_root^.previous;
|
||||
if pp=loc_info^.heap_mem_root then
|
||||
loc_info^.heap_mem_root:=loc_info^.heap_mem_root^.previous;
|
||||
end
|
||||
else
|
||||
begin
|
||||
@ -587,48 +583,102 @@ begin
|
||||
bp:=get_caller_frame(bp);
|
||||
end;
|
||||
end;
|
||||
inc(freemem_cnt);
|
||||
{ clear the memory }
|
||||
fillchar(p^,size,#240); { $F0 will lead to GFP if used as pointer ! }
|
||||
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=heap_valid_last then
|
||||
if pp=loc_info^.heap_valid_last then
|
||||
begin
|
||||
heap_valid_last:=pp^.prev_valid;
|
||||
if pp=heap_valid_first then
|
||||
heap_valid_first:=nil;
|
||||
TraceFreememsize:=size;
|
||||
exit;
|
||||
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:=heap_valid_last;
|
||||
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=heap_valid_first then
|
||||
heap_valid_first:=pp2;
|
||||
TraceFreememsize:=size;
|
||||
exit;
|
||||
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}
|
||||
TraceFreememsize:=size;
|
||||
exit;
|
||||
exit(false);
|
||||
end;
|
||||
{ release the normal memory at least }
|
||||
i:=SysFreeMemSize(pp,ppsize);
|
||||
{ return the correct size }
|
||||
dec(i,sizeof(theap_mem_info)+extra_size);
|
||||
if add_tail then
|
||||
dec(i,sizeof(ptrint));
|
||||
TraceFreeMemSize:=i;
|
||||
CheckFreeMemSize:=true;
|
||||
end;
|
||||
|
||||
function InternalFreeMemSize(loc_info: pheap_info; p: pointer; pp: pheap_mem_info;
|
||||
size: ptrint; release_orphaned_lock: boolean): ptrint;
|
||||
var
|
||||
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;
|
||||
|
||||
|
||||
@ -691,6 +741,7 @@ var
|
||||
oldexactsize : ptrint;
|
||||
old_fill_extra_info_proc : tfillextrainfoproc;
|
||||
old_display_extra_info_proc : tdisplayextrainfoproc;
|
||||
loc_info: pheap_info;
|
||||
begin
|
||||
{ Free block? }
|
||||
if size=0 then
|
||||
@ -709,12 +760,13 @@ begin
|
||||
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
|
||||
error_in_heap:=true;
|
||||
loc_info^.error_in_heap:=true;
|
||||
if useownfile then
|
||||
dump_error(pp,ownfile)
|
||||
else
|
||||
@ -795,10 +847,10 @@ begin
|
||||
end;
|
||||
{ adjust like a freemem and then a getmem, so you get correct
|
||||
results in the summary display }
|
||||
inc(freemem_size,oldsize);
|
||||
inc(freemem8_size,((oldsize+7) div 8)*8);
|
||||
inc(getmem_size,size);
|
||||
inc(getmem8_size,((size+7) div 8)*8);
|
||||
inc(loc_info^.freemem_size,oldsize);
|
||||
inc(loc_info^.freemem8_size,((oldsize+7) div 8)*8);
|
||||
inc(loc_info^.getmem_size,size);
|
||||
inc(loc_info^.getmem8_size,((size+7) div 8)*8);
|
||||
{ generate new backtrace }
|
||||
bp:=get_caller_frame(get_frame);
|
||||
for i:=1 to tracesize do
|
||||
@ -862,6 +914,7 @@ procedure CheckPointer(p : pointer); [public, alias : 'FPC_CHECKPOINTER'];
|
||||
var
|
||||
i : ptrint;
|
||||
pp : pheap_mem_info;
|
||||
loc_info: pheap_info;
|
||||
{$ifdef go32v2}
|
||||
get_ebp,stack_top : longword;
|
||||
data_end : longword;
|
||||
@ -877,7 +930,7 @@ begin
|
||||
runerror(204);
|
||||
|
||||
i:=0;
|
||||
|
||||
loc_info:=@heap_info;
|
||||
if useownfile then
|
||||
ptext:=@ownfile
|
||||
else
|
||||
@ -953,7 +1006,7 @@ begin
|
||||
{ first try valid list faster }
|
||||
|
||||
{$ifdef EXTRA}
|
||||
pp:=heap_valid_last;
|
||||
pp:=loc_info^.heap_valid_last;
|
||||
while pp<>nil do
|
||||
begin
|
||||
{ inside this valid block ! }
|
||||
@ -965,8 +1018,8 @@ begin
|
||||
if ((pp^.sig=$DEADBEEF) and not usecrc) or
|
||||
((pp^.sig=calculate_sig(pp)) and usecrc) or
|
||||
{ special case of the fill_extra_info call }
|
||||
((pp=heap_valid_last) and usecrc and (pp^.sig=$DEADBEEF)
|
||||
and inside_trace_getmem) then
|
||||
((pp=loc_info^.heap_valid_last) and usecrc and (pp^.sig=$DEADBEEF)
|
||||
and loc_info^.inside_trace_getmem) then
|
||||
goto _exit
|
||||
else
|
||||
begin
|
||||
@ -978,7 +1031,7 @@ begin
|
||||
else
|
||||
pp:=pp^.prev_valid;
|
||||
inc(i);
|
||||
if i>getmem_cnt-freemem_cnt then
|
||||
if i>loc_info^.getmem_cnt-loc_info^.freemem_cnt then
|
||||
begin
|
||||
writeln(ptext^,'error in linked list of heap_mem_info');
|
||||
halt(1);
|
||||
@ -986,7 +1039,7 @@ begin
|
||||
end;
|
||||
i:=0;
|
||||
{$endif EXTRA}
|
||||
pp:=heap_mem_root;
|
||||
pp:=loc_info^.heap_mem_root;
|
||||
while pp<>nil do
|
||||
begin
|
||||
{ inside this block ! }
|
||||
@ -1004,7 +1057,7 @@ begin
|
||||
end;
|
||||
pp:=pp^.previous;
|
||||
inc(i);
|
||||
if i>getmem_cnt then
|
||||
if i>loc_info^.getmem_cnt then
|
||||
begin
|
||||
writeln(ptext^,'error in linked list of heap_mem_info');
|
||||
halt(1);
|
||||
@ -1027,16 +1080,21 @@ var
|
||||
ExpectedHeapFree : ptrint;
|
||||
status : TFPCHeapStatus;
|
||||
ptext : ^text;
|
||||
loc_info: pheap_info;
|
||||
begin
|
||||
loc_info:=@heap_info;
|
||||
if useownfile then
|
||||
ptext:=@ownfile
|
||||
else
|
||||
ptext:=@stderr;
|
||||
pp:=heap_mem_root;
|
||||
pp:=loc_info^.heap_mem_root;
|
||||
Writeln(ptext^,'Heap dump by heaptrc unit');
|
||||
Writeln(ptext^,getmem_cnt, ' memory blocks allocated : ',getmem_size,'/',getmem8_size);
|
||||
Writeln(ptext^,freemem_cnt,' memory blocks freed : ',freemem_size,'/',freemem8_size);
|
||||
Writeln(ptext^,getmem_cnt-freemem_cnt,' unfreed memory blocks : ',getmem_size-freemem_size);
|
||||
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
|
||||
@ -1044,11 +1102,13 @@ begin
|
||||
else
|
||||
Writeln(ptext^);
|
||||
Writeln(ptext^,'True free heap : ',status.CurrHeapFree);
|
||||
ExpectedHeapFree:=status.CurrHeapSize-(getmem8_size-freemem8_size)-
|
||||
(getmem_cnt-freemem_cnt)*(sizeof(theap_mem_info)+extra_info_size)-EntryMemUsed;
|
||||
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:=getmem_cnt-freemem_cnt;
|
||||
i:=loc_info^.getmem_cnt-loc_info^.freemem_cnt;
|
||||
while pp<>nil do
|
||||
begin
|
||||
if i<0 then
|
||||
@ -1071,20 +1131,20 @@ begin
|
||||
{$ifdef EXTRA}
|
||||
dump_error(pp,error_file);
|
||||
{$endif EXTRA}
|
||||
error_in_heap:=true;
|
||||
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);
|
||||
error_in_heap:=true;
|
||||
loc_info^.error_in_heap:=true;
|
||||
end
|
||||
{$endif EXTRA}
|
||||
;
|
||||
pp:=pp^.previous;
|
||||
end;
|
||||
if HaltOnNotReleased and (getmem_cnt<>freemem_cnt) then
|
||||
if HaltOnNotReleased and (loc_info^.getmem_cnt<>loc_info^.freemem_cnt) then
|
||||
exitcode:=203;
|
||||
end;
|
||||
|
||||
@ -1104,38 +1164,93 @@ end;
|
||||
*****************************************************************************}
|
||||
|
||||
procedure TraceInitThread;
|
||||
var
|
||||
loc_info: pheap_info;
|
||||
begin
|
||||
loc_info := @heap_info;
|
||||
{$ifdef EXTRA}
|
||||
heap_valid_first := nil;
|
||||
heap_valid_last := nil;
|
||||
loc_info^.heap_valid_first := nil;
|
||||
loc_info^.heap_valid_last := nil;
|
||||
{$endif}
|
||||
heap_mem_root := nil;
|
||||
getmem_cnt := 0;
|
||||
freemem_cnt := 0;
|
||||
getmem_size := 0;
|
||||
freemem_size := 0;
|
||||
getmem8_size := 0;
|
||||
freemem8_size := 0;
|
||||
error_in_heap := false;
|
||||
inside_trace_getmem := false;
|
||||
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;
|
||||
if main_relo_todolist <> nil then
|
||||
initcriticalsection(heap_free_todo.lock);
|
||||
initcriticalsection(loc_info^.heap_free_todo.lock);
|
||||
end;
|
||||
|
||||
procedure TraceRelocateHeap;
|
||||
begin
|
||||
main_relo_todolist := @heap_free_todo;
|
||||
main_relo_todolist := @heap_info.heap_free_todo;
|
||||
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;
|
||||
|
||||
procedure TraceExitThread;
|
||||
var
|
||||
loc_info: pheap_info;
|
||||
heap_mem: pheap_mem_info;
|
||||
begin
|
||||
finish_heap_free_todo_list;
|
||||
if main_relo_todolist <> nil then
|
||||
donecriticalsection(heap_free_todo.lock);
|
||||
if not error_in_heap then
|
||||
loc_info := @heap_info;
|
||||
entercriticalsection(loc_info^.heap_free_todo.lock);
|
||||
entercriticalsection(orphaned_info.heap_free_todo.lock);
|
||||
{ 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;
|
||||
end;
|
||||
leavecriticalsection(orphaned_info.heap_free_todo.lock);
|
||||
donecriticalsection(loc_info^.heap_free_todo.lock);
|
||||
end;
|
||||
|
||||
function TraceGetHeapStatus:THeapStatus;
|
||||
@ -1207,7 +1322,7 @@ const
|
||||
procedure TraceInit;
|
||||
begin
|
||||
MakeCRC32Tbl;
|
||||
main_orig_todolist := @heap_free_todo;
|
||||
main_orig_todolist := @heap_info.heap_free_todo;
|
||||
main_relo_todolist := nil;
|
||||
TraceInitThread;
|
||||
SetMemoryManager(TraceManager);
|
||||
@ -1246,8 +1361,9 @@ begin
|
||||
exit;
|
||||
end;
|
||||
TraceExitThread;
|
||||
if error_in_heap and (exitcode=0) then
|
||||
if heap_info.error_in_heap and (exitcode=0) then
|
||||
exitcode:=203;
|
||||
donecriticalsection(orphaned_info.heap_free_todo.lock);
|
||||
{$ifdef EXTRA}
|
||||
Close(error_file);
|
||||
{$endif EXTRA}
|
||||
|
Loading…
Reference in New Issue
Block a user