heaptrc.pp 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by the Free Pascal development team.
  4. Heap tracer
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit heaptrc;
  12. interface
  13. {$inline on}
  14. {$ifdef FPC_HEAPTRC_EXTRA}
  15. {$define EXTRA}
  16. {$inline off}
  17. {$endif FPC_HEAPTRC_EXTRA}
  18. {$checkpointer off}
  19. {$goto on}
  20. {$TYPEDADDRESS on}
  21. {$if defined(win32) or defined(wince)}
  22. {$define windows}
  23. {$endif}
  24. Procedure DumpHeap;
  25. { define EXTRA to add more
  26. tests :
  27. - keep all memory after release and
  28. check by CRC value if not changed after release
  29. WARNING this needs extremely much memory (PM) }
  30. type
  31. tFillExtraInfoProc = procedure(p : pointer);
  32. tdisplayextrainfoProc = procedure (var ptext : text;p : pointer);
  33. { Allows to add info pre memory block, see ppheap.pas of the compiler
  34. for example source }
  35. procedure SetHeapExtraInfo(size : ptruint;fillproc : tfillextrainfoproc;displayproc : tdisplayextrainfoproc);
  36. { Redirection of the output to a file }
  37. procedure SetHeapTraceOutput(const name : string);
  38. const
  39. { tracing level
  40. splitted in two if memory is released !! }
  41. {$ifdef EXTRA}
  42. tracesize = 16;
  43. {$else EXTRA}
  44. tracesize = 8;
  45. {$endif EXTRA}
  46. { install heaptrc memorymanager }
  47. useheaptrace : boolean=true;
  48. { less checking }
  49. quicktrace : boolean=true;
  50. { calls halt() on error by default !! }
  51. HaltOnError : boolean = true;
  52. { Halt on exit if any memory was not freed }
  53. HaltOnNotReleased : boolean = false;
  54. { set this to true if you suspect that memory
  55. is freed several times }
  56. {$ifdef EXTRA}
  57. keepreleased : boolean=true;
  58. {$else EXTRA}
  59. keepreleased : boolean=false;
  60. {$endif EXTRA}
  61. { add a small footprint at the end of memory blocks, this
  62. can check for memory overwrites at the end of a block }
  63. add_tail : boolean = true;
  64. { put crc in sig
  65. this allows to test for writing into that part }
  66. usecrc : boolean = true;
  67. implementation
  68. const
  69. { allows to add custom info in heap_mem_info, this is the size that will
  70. be allocated for this information }
  71. extra_info_size : ptruint = 0;
  72. exact_info_size : ptruint = 0;
  73. EntryMemUsed : ptruint = 0;
  74. { function to fill this info up }
  75. fill_extra_info_proc : TFillExtraInfoProc = nil;
  76. display_extra_info_proc : TDisplayExtraInfoProc = nil;
  77. { indicates where the output will be redirected }
  78. { only set using environment variables }
  79. outputstr : shortstring = '';
  80. type
  81. pheap_extra_info = ^theap_extra_info;
  82. theap_extra_info = record
  83. check : cardinal; { used to check if the procvar is still valid }
  84. fillproc : tfillextrainfoProc;
  85. displayproc : tdisplayextrainfoProc;
  86. data : record
  87. end;
  88. end;
  89. ppheap_mem_info = ^pheap_mem_info;
  90. pheap_mem_info = ^theap_mem_info;
  91. { warning the size of theap_mem_info
  92. must be a multiple of 8
  93. because otherwise you will get
  94. problems when releasing the usual memory part !!
  95. sizeof(theap_mem_info = 16+tracesize*4 so
  96. tracesize must be even !! PM }
  97. theap_mem_info = record
  98. previous,
  99. next : pheap_mem_info;
  100. todolist : ppheap_mem_info;
  101. todonext : pheap_mem_info;
  102. size : ptruint;
  103. sig : longword;
  104. {$ifdef EXTRA}
  105. release_sig : longword;
  106. prev_valid : pheap_mem_info;
  107. {$endif EXTRA}
  108. calls : array [1..tracesize] of pointer;
  109. exact_info_size : word;
  110. extra_info_size : word;
  111. extra_info : pheap_extra_info;
  112. end;
  113. pheap_info = ^theap_info;
  114. theap_info = record
  115. {$ifdef EXTRA}
  116. heap_valid_first,
  117. heap_valid_last : pheap_mem_info;
  118. {$endif EXTRA}
  119. heap_mem_root : pheap_mem_info;
  120. heap_free_todo : pheap_mem_info;
  121. getmem_cnt,
  122. freemem_cnt : ptruint;
  123. getmem_size,
  124. freemem_size : ptruint;
  125. getmem8_size,
  126. freemem8_size : ptruint;
  127. error_in_heap : boolean;
  128. inside_trace_getmem : boolean;
  129. end;
  130. var
  131. useownfile : boolean;
  132. ownfile : text;
  133. {$ifdef EXTRA}
  134. error_file : text;
  135. {$endif EXTRA}
  136. main_orig_todolist: ppheap_mem_info;
  137. main_relo_todolist: ppheap_mem_info;
  138. orphaned_info: theap_info;
  139. todo_lock: trtlcriticalsection;
  140. threadvar
  141. heap_info: theap_info;
  142. {*****************************************************************************
  143. Crc 32
  144. *****************************************************************************}
  145. var
  146. Crc32Tbl : array[0..255] of longword;
  147. procedure MakeCRC32Tbl;
  148. var
  149. crc : longword;
  150. i,n : byte;
  151. begin
  152. for i:=0 to 255 do
  153. begin
  154. crc:=i;
  155. for n:=1 to 8 do
  156. if odd(crc) then
  157. crc:=(crc shr 1) xor $edb88320
  158. else
  159. crc:=crc shr 1;
  160. Crc32Tbl[i]:=crc;
  161. end;
  162. end;
  163. Function UpdateCrc32(InitCrc:longword;var InBuf;InLen:ptruint):longword;
  164. var
  165. i : ptruint;
  166. p : pchar;
  167. begin
  168. p:=@InBuf;
  169. for i:=1 to InLen do
  170. begin
  171. InitCrc:=Crc32Tbl[byte(InitCrc) xor byte(p^)] xor (InitCrc shr 8);
  172. inc(p);
  173. end;
  174. UpdateCrc32:=InitCrc;
  175. end;
  176. Function calculate_sig(p : pheap_mem_info) : longword;
  177. var
  178. crc : longword;
  179. pl : pptruint;
  180. begin
  181. crc:=cardinal($ffffffff);
  182. crc:=UpdateCrc32(crc,p^.size,sizeof(ptruint));
  183. crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(ptruint));
  184. if p^.extra_info_size>0 then
  185. crc:=UpdateCrc32(crc,p^.extra_info^,p^.exact_info_size);
  186. if add_tail then
  187. begin
  188. { Check also 4 bytes just after allocation !! }
  189. pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info)+p^.size;
  190. crc:=UpdateCrc32(crc,pl^,sizeof(ptruint));
  191. end;
  192. calculate_sig:=crc;
  193. end;
  194. {$ifdef EXTRA}
  195. Function calculate_release_sig(p : pheap_mem_info) : longword;
  196. var
  197. crc : longword;
  198. pl : pptruint;
  199. begin
  200. crc:=$ffffffff;
  201. crc:=UpdateCrc32(crc,p^.size,sizeof(ptruint));
  202. crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(ptruint));
  203. if p^.extra_info_size>0 then
  204. crc:=UpdateCrc32(crc,p^.extra_info^,p^.exact_info_size);
  205. { Check the whole of the whole allocation }
  206. pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info);
  207. crc:=UpdateCrc32(crc,pl^,p^.size);
  208. { Check also 4 bytes just after allocation !! }
  209. if add_tail then
  210. begin
  211. { Check also 4 bytes just after allocation !! }
  212. pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info)+p^.size;
  213. crc:=UpdateCrc32(crc,pl^,sizeof(ptruint));
  214. end;
  215. calculate_release_sig:=crc;
  216. end;
  217. {$endif EXTRA}
  218. {*****************************************************************************
  219. Helpers
  220. *****************************************************************************}
  221. function InternalFreeMemSize(loc_info: pheap_info; p: pointer; pp: pheap_mem_info;
  222. size: ptruint; release_todo_lock: boolean): ptruint; forward;
  223. function TraceFreeMem(p: pointer): ptruint; forward;
  224. procedure call_stack(pp : pheap_mem_info;var ptext : text);
  225. var
  226. i : ptruint;
  227. begin
  228. writeln(ptext,'Call trace for block $',hexstr(pointer(pp)+sizeof(theap_mem_info)),' size ',pp^.size);
  229. for i:=1 to tracesize do
  230. if pp^.calls[i]<>nil then
  231. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  232. { the check is done to be sure that the procvar is not overwritten }
  233. if assigned(pp^.extra_info) and
  234. (pp^.extra_info^.check=$12345678) and
  235. assigned(pp^.extra_info^.displayproc) then
  236. pp^.extra_info^.displayproc(ptext,@pp^.extra_info^.data);
  237. end;
  238. procedure call_free_stack(pp : pheap_mem_info;var ptext : text);
  239. var
  240. i : ptruint;
  241. begin
  242. writeln(ptext,'Call trace for block at $',hexstr(pointer(pp)+sizeof(theap_mem_info)),' size ',pp^.size);
  243. for i:=1 to tracesize div 2 do
  244. if pp^.calls[i]<>nil then
  245. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  246. writeln(ptext,' was released at ');
  247. for i:=(tracesize div 2)+1 to tracesize do
  248. if pp^.calls[i]<>nil then
  249. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  250. { the check is done to be sure that the procvar is not overwritten }
  251. if assigned(pp^.extra_info) and
  252. (pp^.extra_info^.check=$12345678) and
  253. assigned(pp^.extra_info^.displayproc) then
  254. pp^.extra_info^.displayproc(ptext,@pp^.extra_info^.data);
  255. end;
  256. procedure dump_already_free(p : pheap_mem_info;var ptext : text);
  257. begin
  258. Writeln(ptext,'Marked memory at $',HexStr(pointer(p)+sizeof(theap_mem_info)),' released');
  259. call_free_stack(p,ptext);
  260. Writeln(ptext,'freed again at');
  261. dump_stack(ptext,get_caller_frame(get_frame));
  262. end;
  263. procedure dump_error(p : pheap_mem_info;var ptext : text);
  264. begin
  265. Writeln(ptext,'Marked memory at $',HexStr(pointer(p)+sizeof(theap_mem_info)),' invalid');
  266. Writeln(ptext,'Wrong signature $',hexstr(p^.sig,8),' instead of ',hexstr(calculate_sig(p),8));
  267. dump_stack(ptext,get_caller_frame(get_frame));
  268. end;
  269. {$ifdef EXTRA}
  270. procedure dump_change_after(p : pheap_mem_info;var ptext : text);
  271. var pp : pchar;
  272. i : ptruint;
  273. begin
  274. Writeln(ptext,'Marked memory at $',HexStr(pointer(p)+sizeof(theap_mem_info)),' invalid');
  275. Writeln(ptext,'Wrong release CRC $',hexstr(p^.release_sig,8),' instead of ',hexstr(calculate_release_sig(p),8));
  276. Writeln(ptext,'This memory was changed after call to freemem !');
  277. call_free_stack(p,ptext);
  278. pp:=pointer(p)+sizeof(theap_mem_info);
  279. for i:=0 to p^.size-1 do
  280. if byte(pp[i])<>$F0 then
  281. Writeln(ptext,'offset',i,':$',hexstr(i,2*sizeof(pointer)),'"',pp[i],'"');
  282. end;
  283. {$endif EXTRA}
  284. procedure dump_wrong_size(p : pheap_mem_info;size : ptruint;var ptext : text);
  285. begin
  286. Writeln(ptext,'Marked memory at $',HexStr(pointer(p)+sizeof(theap_mem_info)),' invalid');
  287. Writeln(ptext,'Wrong size : ',p^.size,' allocated ',size,' freed');
  288. dump_stack(ptext,get_caller_frame(get_frame));
  289. { the check is done to be sure that the procvar is not overwritten }
  290. if assigned(p^.extra_info) and
  291. (p^.extra_info^.check=$12345678) and
  292. assigned(p^.extra_info^.displayproc) then
  293. p^.extra_info^.displayproc(ptext,@p^.extra_info^.data);
  294. call_stack(p,ptext);
  295. end;
  296. function is_in_getmem_list (loc_info: pheap_info; p : pheap_mem_info) : boolean;
  297. var
  298. i : ptruint;
  299. pp : pheap_mem_info;
  300. begin
  301. is_in_getmem_list:=false;
  302. pp:=loc_info^.heap_mem_root;
  303. i:=0;
  304. while pp<>nil do
  305. begin
  306. if ((pp^.sig<>$DEADBEEF) or usecrc) and
  307. ((pp^.sig<>calculate_sig(pp)) or not usecrc) and
  308. (pp^.sig <>$AAAAAAAA) then
  309. begin
  310. if useownfile then
  311. writeln(ownfile,'error in linked list of heap_mem_info')
  312. else
  313. writeln(stderr,'error in linked list of heap_mem_info');
  314. RunError(204);
  315. end;
  316. if pp=p then
  317. is_in_getmem_list:=true;
  318. pp:=pp^.previous;
  319. inc(i);
  320. if i>loc_info^.getmem_cnt-loc_info^.freemem_cnt then
  321. if useownfile then
  322. writeln(ownfile,'error in linked list of heap_mem_info')
  323. else
  324. writeln(stderr,'error in linked list of heap_mem_info');
  325. end;
  326. end;
  327. procedure finish_heap_free_todo_list(loc_info: pheap_info);
  328. var
  329. bp: pointer;
  330. pp: pheap_mem_info;
  331. list: ppheap_mem_info;
  332. begin
  333. list := @loc_info^.heap_free_todo;
  334. repeat
  335. pp := list^;
  336. list^ := list^^.todonext;
  337. bp := pointer(pp)+sizeof(theap_mem_info);
  338. InternalFreeMemSize(loc_info,bp,pp,pp^.size,false);
  339. until list^ = nil;
  340. end;
  341. procedure try_finish_heap_free_todo_list(loc_info: pheap_info);
  342. begin
  343. if loc_info^.heap_free_todo <> nil then
  344. begin
  345. entercriticalsection(todo_lock);
  346. finish_heap_free_todo_list(loc_info);
  347. leavecriticalsection(todo_lock);
  348. end;
  349. end;
  350. {*****************************************************************************
  351. TraceGetMem
  352. *****************************************************************************}
  353. Function TraceGetMem(size:ptruint):pointer;
  354. var
  355. allocsize,i : ptruint;
  356. oldbp,
  357. bp : pointer;
  358. pl : pdword;
  359. p : pointer;
  360. pp : pheap_mem_info;
  361. loc_info: pheap_info;
  362. begin
  363. loc_info := @heap_info;
  364. try_finish_heap_free_todo_list(loc_info);
  365. inc(loc_info^.getmem_size,size);
  366. inc(loc_info^.getmem8_size,(size+7) and not 7);
  367. { Do the real GetMem, but alloc also for the info block }
  368. {$ifdef cpuarm}
  369. allocsize:=(size + 3) and not 3+sizeof(theap_mem_info)+extra_info_size;
  370. {$else cpuarm}
  371. allocsize:=size+sizeof(theap_mem_info)+extra_info_size;
  372. {$endif cpuarm}
  373. if add_tail then
  374. inc(allocsize,sizeof(ptruint));
  375. { if ReturnNilIfGrowHeapFails is true
  376. SysGetMem can return nil }
  377. p:=SysGetMem(allocsize);
  378. if (p=nil) then
  379. begin
  380. TraceGetMem:=nil;
  381. exit;
  382. end;
  383. pp:=pheap_mem_info(p);
  384. inc(p,sizeof(theap_mem_info));
  385. { Create the info block }
  386. pp^.sig:=$DEADBEEF;
  387. pp^.todolist:=@loc_info^.heap_free_todo;
  388. pp^.todonext:=nil;
  389. pp^.size:=size;
  390. pp^.extra_info_size:=extra_info_size;
  391. pp^.exact_info_size:=exact_info_size;
  392. {
  393. the end of the block contains:
  394. <tail> 4 bytes
  395. <extra_info> X bytes
  396. }
  397. if extra_info_size>0 then
  398. begin
  399. pp^.extra_info:=pointer(pp)+allocsize-extra_info_size;
  400. fillchar(pp^.extra_info^,extra_info_size,0);
  401. pp^.extra_info^.check:=$12345678;
  402. pp^.extra_info^.fillproc:=fill_extra_info_proc;
  403. pp^.extra_info^.displayproc:=display_extra_info_proc;
  404. if assigned(fill_extra_info_proc) then
  405. begin
  406. loc_info^.inside_trace_getmem:=true;
  407. fill_extra_info_proc(@pp^.extra_info^.data);
  408. loc_info^.inside_trace_getmem:=false;
  409. end;
  410. end
  411. else
  412. pp^.extra_info:=nil;
  413. if add_tail then
  414. begin
  415. pl:=pointer(pp)+allocsize-pp^.extra_info_size-sizeof(ptruint);
  416. unaligned(pl^):=$DEADBEEF;
  417. end;
  418. { clear the memory }
  419. fillchar(p^,size,#255);
  420. { retrieve backtrace info }
  421. bp:=get_caller_frame(get_frame);
  422. { valid bp? }
  423. if (bp>=StackBottom) and (bp<(StackBottom + StackLength)) then
  424. for i:=1 to tracesize do
  425. begin
  426. pp^.calls[i]:=get_caller_addr(bp);
  427. oldbp:=bp;
  428. bp:=get_caller_frame(bp);
  429. if (bp<oldbp) or (bp>(StackBottom + StackLength)) then
  430. break;
  431. end;
  432. { insert in the linked list }
  433. if loc_info^.heap_mem_root<>nil then
  434. loc_info^.heap_mem_root^.next:=pp;
  435. pp^.previous:=loc_info^.heap_mem_root;
  436. pp^.next:=nil;
  437. {$ifdef EXTRA}
  438. pp^.prev_valid:=loc_info^.heap_valid_last;
  439. loc_info^.heap_valid_last:=pp;
  440. if not assigned(loc_info^.heap_valid_first) then
  441. loc_info^.heap_valid_first:=pp;
  442. {$endif EXTRA}
  443. loc_info^.heap_mem_root:=pp;
  444. { must be changed before fill_extra_info is called
  445. because checkpointer can be called from within
  446. fill_extra_info PM }
  447. inc(loc_info^.getmem_cnt);
  448. { update the signature }
  449. if usecrc then
  450. pp^.sig:=calculate_sig(pp);
  451. TraceGetmem:=p;
  452. end;
  453. {*****************************************************************************
  454. TraceFreeMem
  455. *****************************************************************************}
  456. function CheckFreeMemSize(loc_info: pheap_info; pp: pheap_mem_info;
  457. size, ppsize: ptruint): boolean; inline;
  458. var
  459. i: ptruint;
  460. bp : pointer;
  461. ptext : ^text;
  462. {$ifdef EXTRA}
  463. pp2 : pheap_mem_info;
  464. {$endif}
  465. begin
  466. if useownfile then
  467. ptext:=@ownfile
  468. else
  469. ptext:=@stderr;
  470. inc(loc_info^.freemem_size,size);
  471. inc(loc_info^.freemem8_size,(size+7) and not 7);
  472. if not quicktrace then
  473. begin
  474. if not(is_in_getmem_list(loc_info, pp)) then
  475. RunError(204);
  476. end;
  477. if (pp^.sig=$AAAAAAAA) and not usecrc then
  478. begin
  479. loc_info^.error_in_heap:=true;
  480. dump_already_free(pp,ptext^);
  481. if haltonerror then halt(1);
  482. end
  483. else if ((pp^.sig<>$DEADBEEF) or usecrc) and
  484. ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
  485. begin
  486. loc_info^.error_in_heap:=true;
  487. dump_error(pp,ptext^);
  488. {$ifdef EXTRA}
  489. dump_error(pp,error_file);
  490. {$endif EXTRA}
  491. { don't release anything in this case !! }
  492. if haltonerror then halt(1);
  493. exit;
  494. end
  495. else if pp^.size<>size then
  496. begin
  497. loc_info^.error_in_heap:=true;
  498. dump_wrong_size(pp,size,ptext^);
  499. {$ifdef EXTRA}
  500. dump_wrong_size(pp,size,error_file);
  501. {$endif EXTRA}
  502. if haltonerror then halt(1);
  503. { don't release anything in this case !! }
  504. exit;
  505. end;
  506. { now it is released !! }
  507. pp^.sig:=$AAAAAAAA;
  508. if not keepreleased then
  509. begin
  510. if pp^.next<>nil then
  511. pp^.next^.previous:=pp^.previous;
  512. if pp^.previous<>nil then
  513. pp^.previous^.next:=pp^.next;
  514. if pp=loc_info^.heap_mem_root then
  515. loc_info^.heap_mem_root:=loc_info^.heap_mem_root^.previous;
  516. end
  517. else
  518. begin
  519. bp:=get_caller_frame(get_frame);
  520. if (bp>=StackBottom) and (bp<(StackBottom + StackLength)) then
  521. for i:=(tracesize div 2)+1 to tracesize do
  522. begin
  523. pp^.calls[i]:=get_caller_addr(bp);
  524. bp:=get_caller_frame(bp);
  525. if not((bp>=StackBottom) and (bp<(StackBottom + StackLength))) then
  526. break;
  527. end;
  528. end;
  529. inc(loc_info^.freemem_cnt);
  530. { clear the memory, $F0 will lead to GFP if used as pointer ! }
  531. fillchar((pointer(pp)+sizeof(theap_mem_info))^,size,#240);
  532. { this way we keep all info about all released memory !! }
  533. if keepreleased then
  534. begin
  535. {$ifdef EXTRA}
  536. { We want to check if the memory was changed after release !! }
  537. pp^.release_sig:=calculate_release_sig(pp);
  538. if pp=loc_info^.heap_valid_last then
  539. begin
  540. loc_info^.heap_valid_last:=pp^.prev_valid;
  541. if pp=loc_info^.heap_valid_first then
  542. loc_info^.heap_valid_first:=nil;
  543. exit(false);
  544. end;
  545. pp2:=loc_info^.heap_valid_last;
  546. while assigned(pp2) do
  547. begin
  548. if pp2^.prev_valid=pp then
  549. begin
  550. pp2^.prev_valid:=pp^.prev_valid;
  551. if pp=loc_info^.heap_valid_first then
  552. loc_info^.heap_valid_first:=pp2;
  553. exit(false);
  554. end
  555. else
  556. pp2:=pp2^.prev_valid;
  557. end;
  558. {$endif EXTRA}
  559. exit(false);
  560. end;
  561. CheckFreeMemSize:=true;
  562. end;
  563. function InternalFreeMemSize(loc_info: pheap_info; p: pointer; pp: pheap_mem_info;
  564. size: ptruint; release_todo_lock: boolean): ptruint;
  565. var
  566. i,ppsize : ptruint;
  567. extra_size: ptruint;
  568. release_mem: boolean;
  569. begin
  570. { save old values }
  571. extra_size:=pp^.extra_info_size;
  572. ppsize:= size+sizeof(theap_mem_info)+pp^.extra_info_size;
  573. if add_tail then
  574. inc(ppsize,sizeof(ptruint));
  575. { do various checking }
  576. release_mem := CheckFreeMemSize(loc_info, pp, size, ppsize);
  577. if release_todo_lock then
  578. leavecriticalsection(todo_lock);
  579. if release_mem then
  580. begin
  581. { release the normal memory at least }
  582. i:=SysFreeMemSize(pp,ppsize);
  583. { return the correct size }
  584. dec(i,sizeof(theap_mem_info)+extra_size);
  585. if add_tail then
  586. dec(i,sizeof(ptruint));
  587. InternalFreeMemSize:=i;
  588. end else
  589. InternalFreeMemSize:=size;
  590. end;
  591. function TraceFreeMemSize(p:pointer;size:ptruint):ptruint;
  592. var
  593. loc_info: pheap_info;
  594. pp: pheap_mem_info;
  595. release_lock: boolean;
  596. begin
  597. if p=nil then
  598. begin
  599. TraceFreeMemSize:=0;
  600. exit;
  601. end;
  602. loc_info:=@heap_info;
  603. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  604. release_lock:=false;
  605. if @loc_info^.heap_free_todo <> pp^.todolist then
  606. begin
  607. if pp^.todolist = main_orig_todolist then
  608. pp^.todolist := main_relo_todolist;
  609. entercriticalsection(todo_lock);
  610. release_lock:=true;
  611. if pp^.todolist = @orphaned_info.heap_free_todo then
  612. begin
  613. loc_info := @orphaned_info;
  614. end else
  615. if pp^.todolist <> @loc_info^.heap_free_todo then
  616. begin
  617. { allocated in different heap, push to that todolist }
  618. pp^.todonext := pp^.todolist^;
  619. pp^.todolist^ := pp;
  620. TraceFreeMemSize := pp^.size;
  621. leavecriticalsection(todo_lock);
  622. exit;
  623. end;
  624. end;
  625. TraceFreeMemSize:=InternalFreeMemSize(loc_info,p,pp,size,release_lock);
  626. end;
  627. function TraceMemSize(p:pointer):ptruint;
  628. var
  629. pp : pheap_mem_info;
  630. begin
  631. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  632. TraceMemSize:=pp^.size;
  633. end;
  634. function TraceFreeMem(p:pointer):ptruint;
  635. var
  636. l : ptruint;
  637. pp : pheap_mem_info;
  638. begin
  639. if p=nil then
  640. begin
  641. TraceFreeMem:=0;
  642. exit;
  643. end;
  644. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  645. l:=SysMemSize(pp);
  646. dec(l,sizeof(theap_mem_info)+pp^.extra_info_size);
  647. if add_tail then
  648. dec(l,sizeof(ptruint));
  649. { this can never happend normaly }
  650. if pp^.size>l then
  651. begin
  652. if useownfile then
  653. dump_wrong_size(pp,l,ownfile)
  654. else
  655. dump_wrong_size(pp,l,stderr);
  656. {$ifdef EXTRA}
  657. dump_wrong_size(pp,l,error_file);
  658. {$endif EXTRA}
  659. end;
  660. TraceFreeMem:=TraceFreeMemSize(p,pp^.size);
  661. end;
  662. {*****************************************************************************
  663. ReAllocMem
  664. *****************************************************************************}
  665. function TraceReAllocMem(var p:pointer;size:ptruint):Pointer;
  666. var
  667. newP: pointer;
  668. allocsize,
  669. movesize,
  670. i : ptruint;
  671. oldbp,
  672. bp : pointer;
  673. pl : pdword;
  674. pp : pheap_mem_info;
  675. oldsize,
  676. oldextrasize,
  677. oldexactsize : ptruint;
  678. old_fill_extra_info_proc : tfillextrainfoproc;
  679. old_display_extra_info_proc : tdisplayextrainfoproc;
  680. loc_info: pheap_info;
  681. begin
  682. { Free block? }
  683. if size=0 then
  684. begin
  685. if p<>nil then
  686. TraceFreeMem(p);
  687. p:=nil;
  688. TraceReallocMem:=P;
  689. exit;
  690. end;
  691. { Allocate a new block? }
  692. if p=nil then
  693. begin
  694. p:=TraceGetMem(size);
  695. TraceReallocMem:=P;
  696. exit;
  697. end;
  698. { Resize block }
  699. loc_info:=@heap_info;
  700. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  701. { test block }
  702. if ((pp^.sig<>$DEADBEEF) or usecrc) and
  703. ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
  704. begin
  705. loc_info^.error_in_heap:=true;
  706. if useownfile then
  707. dump_error(pp,ownfile)
  708. else
  709. dump_error(pp,stderr);
  710. {$ifdef EXTRA}
  711. dump_error(pp,error_file);
  712. {$endif EXTRA}
  713. { don't release anything in this case !! }
  714. if haltonerror then halt(1);
  715. exit;
  716. end;
  717. { save info }
  718. oldsize:=pp^.size;
  719. oldextrasize:=pp^.extra_info_size;
  720. oldexactsize:=pp^.exact_info_size;
  721. if pp^.extra_info_size>0 then
  722. begin
  723. old_fill_extra_info_proc:=pp^.extra_info^.fillproc;
  724. old_display_extra_info_proc:=pp^.extra_info^.displayproc;
  725. end;
  726. { Do the real ReAllocMem, but alloc also for the info block }
  727. {$ifdef cpuarm}
  728. allocsize:=(size + 3) and not 3+sizeof(theap_mem_info)+pp^.extra_info_size;
  729. {$else cpuarm}
  730. allocsize:=size+sizeof(theap_mem_info)+pp^.extra_info_size;
  731. {$endif cpuarm}
  732. if add_tail then
  733. inc(allocsize,sizeof(ptruint));
  734. { Try to resize the block, if not possible we need to do a
  735. getmem, move data, freemem }
  736. if not SysTryResizeMem(pp,allocsize) then
  737. begin
  738. { get a new block }
  739. newP := TraceGetMem(size);
  740. { move the data }
  741. if newP <> nil then
  742. begin
  743. movesize:=TraceMemSize(p);
  744. {if the old size is larger than the new size,
  745. move only the new size}
  746. if movesize>size then
  747. movesize:=size;
  748. move(p^,newP^,movesize);
  749. end;
  750. { release p }
  751. traceFreeMem(p);
  752. { return the new pointer }
  753. p:=newp;
  754. traceReAllocMem := newp;
  755. exit;
  756. end;
  757. { Recreate the info block }
  758. pp^.sig:=$DEADBEEF;
  759. pp^.size:=size;
  760. pp^.extra_info_size:=oldextrasize;
  761. pp^.exact_info_size:=oldexactsize;
  762. { add the new extra_info and tail }
  763. if pp^.extra_info_size>0 then
  764. begin
  765. pp^.extra_info:=pointer(pp)+allocsize-pp^.extra_info_size;
  766. fillchar(pp^.extra_info^,extra_info_size,0);
  767. pp^.extra_info^.check:=$12345678;
  768. pp^.extra_info^.fillproc:=old_fill_extra_info_proc;
  769. pp^.extra_info^.displayproc:=old_display_extra_info_proc;
  770. if assigned(pp^.extra_info^.fillproc) then
  771. pp^.extra_info^.fillproc(@pp^.extra_info^.data);
  772. end
  773. else
  774. pp^.extra_info:=nil;
  775. if add_tail then
  776. begin
  777. pl:=pointer(pp)+allocsize-pp^.extra_info_size-sizeof(ptruint);
  778. unaligned(pl^):=$DEADBEEF;
  779. end;
  780. { adjust like a freemem and then a getmem, so you get correct
  781. results in the summary display }
  782. inc(loc_info^.freemem_size,oldsize);
  783. inc(loc_info^.freemem8_size,(oldsize+7) and not 7);
  784. inc(loc_info^.getmem_size,size);
  785. inc(loc_info^.getmem8_size,(size+7) and not 7);
  786. { generate new backtrace }
  787. bp:=get_caller_frame(get_frame);
  788. if (bp>=StackBottom) and (bp<(StackBottom + StackLength)) then
  789. for i:=1 to tracesize do
  790. begin
  791. pp^.calls[i]:=get_caller_addr(bp);
  792. oldbp:=bp;
  793. bp:=get_caller_frame(bp);
  794. if (bp<oldbp) or (bp>(StackBottom + StackLength)) then
  795. break;
  796. end;
  797. { regenerate signature }
  798. if usecrc then
  799. pp^.sig:=calculate_sig(pp);
  800. { return the pointer }
  801. p:=pointer(pp)+sizeof(theap_mem_info);
  802. TraceReAllocmem:=p;
  803. end;
  804. {*****************************************************************************
  805. Check pointer
  806. *****************************************************************************}
  807. {$ifndef Unix}
  808. {$S-}
  809. {$endif}
  810. {$ifdef go32v2}
  811. var
  812. __stklen : longword;external name '__stklen';
  813. __stkbottom : longword;external name '__stkbottom';
  814. ebss : longword; external name 'end';
  815. {$endif go32v2}
  816. {$ifdef linux}
  817. var
  818. etext: ptruint; external name '_etext';
  819. edata : ptruint; external name '_edata';
  820. eend : ptruint; external name '_end';
  821. {$endif}
  822. {$ifdef os2}
  823. (* Currently still EMX based - possibly to be changed in the future. *)
  824. var
  825. etext: ptruint; external name '_etext';
  826. edata : ptruint; external name '_edata';
  827. eend : ptruint; external name '_end';
  828. {$endif}
  829. {$ifdef windows}
  830. var
  831. sdata : ptruint; external name '__data_start__';
  832. edata : ptruint; external name '__data_end__';
  833. sbss : ptruint; external name '__bss_start__';
  834. ebss : ptruint; external name '__bss_end__';
  835. {$endif}
  836. {$ifdef BEOS}
  837. const
  838. B_ERROR = -1;
  839. type
  840. area_id = Longint;
  841. function area_for(addr : Pointer) : area_id;
  842. cdecl; external 'root' name 'area_for';
  843. {$endif BEOS}
  844. procedure CheckPointer(p : pointer); [public, alias : 'FPC_CHECKPOINTER'];
  845. var
  846. i : ptruint;
  847. pp : pheap_mem_info;
  848. loc_info: pheap_info;
  849. {$ifdef go32v2}
  850. get_ebp,stack_top : longword;
  851. bss_end : longword;
  852. {$endif go32v2}
  853. {$ifdef morphos}
  854. stack_top: longword;
  855. {$endif morphos}
  856. ptext : ^text;
  857. label
  858. _exit;
  859. begin
  860. if p=nil then
  861. runerror(204);
  862. i:=0;
  863. loc_info:=@heap_info;
  864. if useownfile then
  865. ptext:=@ownfile
  866. else
  867. ptext:=@stderr;
  868. {$ifdef go32v2}
  869. if ptruint(p)<$1000 then
  870. runerror(216);
  871. asm
  872. movl %ebp,get_ebp
  873. leal ebss,%eax
  874. movl %eax,bss_end
  875. end;
  876. stack_top:=__stkbottom+__stklen;
  877. { allow all between start of code and end of bss }
  878. if ptruint(p)<=bss_end then
  879. goto _exit;
  880. { stack can be above heap !! }
  881. if (ptruint(p)>=get_ebp) and (ptruint(p)<=stack_top) then
  882. goto _exit;
  883. {$endif go32v2}
  884. { I don't know where the stack is in other OS !! }
  885. {$ifdef windows}
  886. { inside stack ? }
  887. if (ptruint(p)>ptruint(get_frame)) and
  888. (p<StackTop) then
  889. goto _exit;
  890. { inside data ? }
  891. if (ptruint(p)>=ptruint(@sdata)) and (ptruint(p)<ptruint(@edata)) then
  892. goto _exit;
  893. { inside bss ? }
  894. if (ptruint(p)>=ptruint(@sbss)) and (ptruint(p)<ptruint(@ebss)) then
  895. goto _exit;
  896. {$endif windows}
  897. {$IFDEF OS2}
  898. { inside stack ? }
  899. if (PtrUInt (P) > PtrUInt (Get_Frame)) and
  900. (PtrUInt (P) < PtrUInt (StackTop)) then
  901. goto _exit;
  902. { inside data or bss ? }
  903. if (PtrUInt (P) >= PtrUInt (@etext)) and (PtrUInt (P) < PtrUInt (@eend)) then
  904. goto _exit;
  905. {$ENDIF OS2}
  906. {$ifdef linux}
  907. { inside stack ? }
  908. if (ptruint(p)>ptruint(get_frame)) and
  909. (ptruint(p)<$c0000000) then //todo: 64bit!
  910. goto _exit;
  911. { inside data or bss ? }
  912. if (ptruint(p)>=ptruint(@etext)) and (ptruint(p)<ptruint(@eend)) then
  913. goto _exit;
  914. {$endif linux}
  915. {$ifdef morphos}
  916. { inside stack ? }
  917. stack_top:=ptruint(StackBottom)+StackLength;
  918. if (ptruint(p)<stack_top) and (ptruint(p)>ptruint(StackBottom)) then
  919. goto _exit;
  920. { inside data or bss ? }
  921. {$WARNING data and bss checking missing }
  922. {$endif morphos}
  923. {$ifdef darwin}
  924. {$warning No checkpointer support yet for Darwin}
  925. exit;
  926. {$endif}
  927. {$ifdef BEOS}
  928. // if we find the address in a known area in our current process,
  929. // then it is a valid one
  930. if area_for(p) <> B_ERROR then
  931. goto _exit;
  932. {$endif BEOS}
  933. { first try valid list faster }
  934. {$ifdef EXTRA}
  935. pp:=loc_info^.heap_valid_last;
  936. while pp<>nil do
  937. begin
  938. { inside this valid block ! }
  939. { we can be changing the extrainfo !! }
  940. if (ptruint(p)>=ptruint(pp)+sizeof(theap_mem_info){+extra_info_size}) and
  941. (ptruint(p)<=ptruint(pp)+sizeof(theap_mem_info)+extra_info_size+pp^.size) then
  942. begin
  943. { check allocated block }
  944. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  945. ((pp^.sig=calculate_sig(pp)) and usecrc) or
  946. { special case of the fill_extra_info call }
  947. ((pp=loc_info^.heap_valid_last) and usecrc and (pp^.sig=$DEADBEEF)
  948. and loc_info^.inside_trace_getmem) then
  949. goto _exit
  950. else
  951. begin
  952. writeln(ptext^,'corrupted heap_mem_info');
  953. dump_error(pp,ptext^);
  954. halt(1);
  955. end;
  956. end
  957. else
  958. pp:=pp^.prev_valid;
  959. inc(i);
  960. if i>loc_info^.getmem_cnt-loc_info^.freemem_cnt then
  961. begin
  962. writeln(ptext^,'error in linked list of heap_mem_info');
  963. halt(1);
  964. end;
  965. end;
  966. i:=0;
  967. {$endif EXTRA}
  968. pp:=loc_info^.heap_mem_root;
  969. while pp<>nil do
  970. begin
  971. { inside this block ! }
  972. if (ptruint(p)>=ptruint(pp)+sizeof(theap_mem_info)+ptruint(extra_info_size)) and
  973. (ptruint(p)<=ptruint(pp)+sizeof(theap_mem_info)+ptruint(extra_info_size)+ptruint(pp^.size)) then
  974. { allocated block }
  975. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  976. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  977. goto _exit
  978. else
  979. begin
  980. writeln(ptext^,'pointer $',hexstr(p),' points into invalid memory block');
  981. dump_error(pp,ptext^);
  982. runerror(204);
  983. end;
  984. pp:=pp^.previous;
  985. inc(i);
  986. if i>loc_info^.getmem_cnt then
  987. begin
  988. writeln(ptext^,'error in linked list of heap_mem_info');
  989. halt(1);
  990. end;
  991. end;
  992. writeln(ptext^,'pointer $',hexstr(p),' does not point to valid memory block');
  993. dump_error(p,ptext^);
  994. runerror(204);
  995. _exit:
  996. end;
  997. {*****************************************************************************
  998. Dump Heap
  999. *****************************************************************************}
  1000. procedure dumpheap;
  1001. var
  1002. pp : pheap_mem_info;
  1003. i : ptrint;
  1004. ExpectedHeapFree : ptruint;
  1005. status : TFPCHeapStatus;
  1006. ptext : ^text;
  1007. loc_info: pheap_info;
  1008. begin
  1009. loc_info:=@heap_info;
  1010. if useownfile then
  1011. ptext:=@ownfile
  1012. else
  1013. ptext:=@stderr;
  1014. pp:=loc_info^.heap_mem_root;
  1015. Writeln(ptext^,'Heap dump by heaptrc unit');
  1016. Writeln(ptext^,loc_info^.getmem_cnt, ' memory blocks allocated : ',
  1017. loc_info^.getmem_size,'/',loc_info^.getmem8_size);
  1018. Writeln(ptext^,loc_info^.freemem_cnt,' memory blocks freed : ',
  1019. loc_info^.freemem_size,'/',loc_info^.freemem8_size);
  1020. Writeln(ptext^,loc_info^.getmem_cnt-loc_info^.freemem_cnt,
  1021. ' unfreed memory blocks : ',loc_info^.getmem_size-loc_info^.freemem_size);
  1022. status:=SysGetFPCHeapStatus;
  1023. Write(ptext^,'True heap size : ',status.CurrHeapSize);
  1024. if EntryMemUsed > 0 then
  1025. Writeln(ptext^,' (',EntryMemUsed,' used in System startup)')
  1026. else
  1027. Writeln(ptext^);
  1028. Writeln(ptext^,'True free heap : ',status.CurrHeapFree);
  1029. ExpectedHeapFree:=status.CurrHeapSize
  1030. -(loc_info^.getmem8_size-loc_info^.freemem8_size)
  1031. -(loc_info^.getmem_cnt-loc_info^.freemem_cnt)*(sizeof(theap_mem_info)+extra_info_size)
  1032. -EntryMemUsed;
  1033. If ExpectedHeapFree<>status.CurrHeapFree then
  1034. Writeln(ptext^,'Should be : ',ExpectedHeapFree);
  1035. i:=loc_info^.getmem_cnt-loc_info^.freemem_cnt;
  1036. while pp<>nil do
  1037. begin
  1038. if i<0 then
  1039. begin
  1040. Writeln(ptext^,'Error in heap memory list');
  1041. Writeln(ptext^,'More memory blocks than expected');
  1042. exit;
  1043. end;
  1044. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  1045. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  1046. begin
  1047. { this one was not released !! }
  1048. if exitcode<>203 then
  1049. call_stack(pp,ptext^);
  1050. dec(i);
  1051. end
  1052. else if pp^.sig<>$AAAAAAAA then
  1053. begin
  1054. dump_error(pp,ptext^);
  1055. {$ifdef EXTRA}
  1056. dump_error(pp,error_file);
  1057. {$endif EXTRA}
  1058. loc_info^.error_in_heap:=true;
  1059. end
  1060. {$ifdef EXTRA}
  1061. else if pp^.release_sig<>calculate_release_sig(pp) then
  1062. begin
  1063. dump_change_after(pp,ptext^);
  1064. dump_change_after(pp,error_file);
  1065. loc_info^.error_in_heap:=true;
  1066. end
  1067. {$endif EXTRA}
  1068. ;
  1069. pp:=pp^.previous;
  1070. end;
  1071. if HaltOnNotReleased and (loc_info^.getmem_cnt<>loc_info^.freemem_cnt) then
  1072. exitcode:=203;
  1073. end;
  1074. {*****************************************************************************
  1075. AllocMem
  1076. *****************************************************************************}
  1077. function TraceAllocMem(size:ptruint):Pointer;
  1078. begin
  1079. TraceAllocMem:=SysAllocMem(size);
  1080. end;
  1081. {*****************************************************************************
  1082. No specific tracing calls
  1083. *****************************************************************************}
  1084. procedure TraceInitThread;
  1085. var
  1086. loc_info: pheap_info;
  1087. begin
  1088. loc_info := @heap_info;
  1089. {$ifdef EXTRA}
  1090. loc_info^.heap_valid_first := nil;
  1091. loc_info^.heap_valid_last := nil;
  1092. {$endif}
  1093. loc_info^.heap_mem_root := nil;
  1094. loc_info^.getmem_cnt := 0;
  1095. loc_info^.freemem_cnt := 0;
  1096. loc_info^.getmem_size := 0;
  1097. loc_info^.freemem_size := 0;
  1098. loc_info^.getmem8_size := 0;
  1099. loc_info^.freemem8_size := 0;
  1100. loc_info^.error_in_heap := false;
  1101. loc_info^.inside_trace_getmem := false;
  1102. EntryMemUsed := SysGetFPCHeapStatus.CurrHeapUsed;
  1103. end;
  1104. procedure TraceRelocateHeap;
  1105. begin
  1106. main_relo_todolist := @heap_info.heap_free_todo;
  1107. initcriticalsection(todo_lock);
  1108. end;
  1109. procedure move_heap_info(src_info, dst_info: pheap_info);
  1110. var
  1111. heap_mem: pheap_mem_info;
  1112. begin
  1113. if src_info^.heap_free_todo <> nil then
  1114. finish_heap_free_todo_list(src_info);
  1115. if dst_info^.heap_free_todo <> nil then
  1116. finish_heap_free_todo_list(dst_info);
  1117. heap_mem := src_info^.heap_mem_root;
  1118. if heap_mem <> nil then
  1119. begin
  1120. repeat
  1121. heap_mem^.todolist := @dst_info^.heap_free_todo;
  1122. if heap_mem^.previous = nil then break;
  1123. heap_mem := heap_mem^.previous;
  1124. until false;
  1125. heap_mem^.previous := dst_info^.heap_mem_root;
  1126. if dst_info^.heap_mem_root <> nil then
  1127. dst_info^.heap_mem_root^.next := heap_mem;
  1128. dst_info^.heap_mem_root := src_info^.heap_mem_root;
  1129. end;
  1130. inc(dst_info^.getmem_cnt, src_info^.getmem_cnt);
  1131. inc(dst_info^.getmem_size, src_info^.getmem_size);
  1132. inc(dst_info^.getmem8_size, src_info^.getmem8_size);
  1133. inc(dst_info^.freemem_cnt, src_info^.freemem_cnt);
  1134. inc(dst_info^.freemem_size, src_info^.freemem_size);
  1135. inc(dst_info^.freemem8_size, src_info^.freemem8_size);
  1136. dst_info^.error_in_heap := dst_info^.error_in_heap or src_info^.error_in_heap;
  1137. {$ifdef EXTRA}
  1138. if assigned(dst_info^.heap_valid_first) then
  1139. dst_info^.heap_valid_first^.prev_valid := src_info^.heap_valid_last
  1140. else
  1141. dst_info^.heap_valid_last := src_info^.heap_valid_last;
  1142. dst_info^.heap_valid_first := src_info^.heap_valid_first;
  1143. {$endif}
  1144. end;
  1145. procedure TraceExitThread;
  1146. var
  1147. loc_info: pheap_info;
  1148. begin
  1149. loc_info := @heap_info;
  1150. entercriticalsection(todo_lock);
  1151. move_heap_info(loc_info, @orphaned_info);
  1152. leavecriticalsection(todo_lock);
  1153. end;
  1154. function TraceGetHeapStatus:THeapStatus;
  1155. begin
  1156. TraceGetHeapStatus:=SysGetHeapStatus;
  1157. end;
  1158. function TraceGetFPCHeapStatus:TFPCHeapStatus;
  1159. begin
  1160. TraceGetFPCHeapStatus:=SysGetFPCHeapStatus;
  1161. end;
  1162. {*****************************************************************************
  1163. Program Hooks
  1164. *****************************************************************************}
  1165. Procedure SetHeapTraceOutput(const name : string);
  1166. var i : ptruint;
  1167. begin
  1168. if useownfile then
  1169. begin
  1170. useownfile:=false;
  1171. close(ownfile);
  1172. end;
  1173. assign(ownfile,name);
  1174. {$I-}
  1175. append(ownfile);
  1176. if IOResult<>0 then
  1177. begin
  1178. Rewrite(ownfile);
  1179. if IOResult<>0 then
  1180. begin
  1181. Writeln(stderr,'[heaptrc] Unable to open "',name,'", writing output to stderr instead.');
  1182. useownfile:=false;
  1183. exit;
  1184. end;
  1185. end;
  1186. {$I+}
  1187. useownfile:=true;
  1188. for i:=0 to Paramcount do
  1189. write(ownfile,paramstr(i),' ');
  1190. writeln(ownfile);
  1191. end;
  1192. procedure SetHeapExtraInfo( size : ptruint;fillproc : tfillextrainfoproc;displayproc : tdisplayextrainfoproc);
  1193. begin
  1194. { the total size must stay multiple of 8, also allocate 2 pointers for
  1195. the fill and display procvars }
  1196. exact_info_size:=size + sizeof(theap_extra_info);
  1197. extra_info_size:=(exact_info_size+7) and not 7;
  1198. fill_extra_info_proc:=fillproc;
  1199. display_extra_info_proc:=displayproc;
  1200. end;
  1201. {*****************************************************************************
  1202. Install MemoryManager
  1203. *****************************************************************************}
  1204. const
  1205. TraceManager:TMemoryManager=(
  1206. NeedLock : true;
  1207. Getmem : @TraceGetMem;
  1208. Freemem : @TraceFreeMem;
  1209. FreememSize : @TraceFreeMemSize;
  1210. AllocMem : @TraceAllocMem;
  1211. ReAllocMem : @TraceReAllocMem;
  1212. MemSize : @TraceMemSize;
  1213. InitThread: @TraceInitThread;
  1214. DoneThread: @TraceExitThread;
  1215. RelocateHeap: @TraceRelocateHeap;
  1216. GetHeapStatus : @TraceGetHeapStatus;
  1217. GetFPCHeapStatus : @TraceGetFPCHeapStatus;
  1218. );
  1219. procedure TraceInit;
  1220. begin
  1221. MakeCRC32Tbl;
  1222. main_orig_todolist := @heap_info.heap_free_todo;
  1223. main_relo_todolist := nil;
  1224. TraceInitThread;
  1225. SetMemoryManager(TraceManager);
  1226. useownfile:=false;
  1227. if outputstr <> '' then
  1228. SetHeapTraceOutput(outputstr);
  1229. {$ifdef EXTRA}
  1230. {$i-}
  1231. Assign(error_file,'heap.err');
  1232. Rewrite(error_file);
  1233. {$i+}
  1234. if IOResult<>0 then
  1235. begin
  1236. writeln('[heaptrc] Unable to create heap.err extra log file, writing output to screen.');
  1237. Assign(error_file,'');
  1238. Rewrite(error_file);
  1239. end;
  1240. {$endif EXTRA}
  1241. { if multithreading was initialized before heaptrc gets initialized (this is currently
  1242. the case for windows dlls), then RelocateHeap gets never called and the lock
  1243. must be initialized already here
  1244. }
  1245. if IsMultithread then
  1246. TraceRelocateHeap;
  1247. end;
  1248. procedure TraceExit;
  1249. begin
  1250. { no dump if error
  1251. because this gives long long listings }
  1252. { clear inoutres, in case the program that quit didn't }
  1253. ioresult;
  1254. if (exitcode<>0) and (erroraddr<>nil) then
  1255. begin
  1256. if useownfile then
  1257. begin
  1258. Writeln(ownfile,'No heap dump by heaptrc unit');
  1259. Writeln(ownfile,'Exitcode = ',exitcode);
  1260. end
  1261. else
  1262. begin
  1263. Writeln(stderr,'No heap dump by heaptrc unit');
  1264. Writeln(stderr,'Exitcode = ',exitcode);
  1265. end;
  1266. if useownfile then
  1267. begin
  1268. useownfile:=false;
  1269. close(ownfile);
  1270. end;
  1271. exit;
  1272. end;
  1273. move_heap_info(@orphaned_info, @heap_info);
  1274. dumpheap;
  1275. if heap_info.error_in_heap and (exitcode=0) then
  1276. exitcode:=203;
  1277. if main_relo_todolist <> nil then
  1278. donecriticalsection(todo_lock);
  1279. {$ifdef EXTRA}
  1280. Close(error_file);
  1281. {$endif EXTRA}
  1282. if useownfile then
  1283. begin
  1284. useownfile:=false;
  1285. close(ownfile);
  1286. end;
  1287. end;
  1288. {$if defined(win32) or defined(win64)}
  1289. function GetEnvironmentStrings : pchar; stdcall;
  1290. external 'kernel32' name 'GetEnvironmentStringsA';
  1291. function FreeEnvironmentStrings(p : pchar) : longbool; stdcall;
  1292. external 'kernel32' name 'FreeEnvironmentStringsA';
  1293. Function GetEnv(envvar: string): string;
  1294. var
  1295. s : string;
  1296. i : ptruint;
  1297. hp,p : pchar;
  1298. begin
  1299. getenv:='';
  1300. p:=GetEnvironmentStrings;
  1301. hp:=p;
  1302. while hp^<>#0 do
  1303. begin
  1304. s:=strpas(hp);
  1305. i:=pos('=',s);
  1306. if upcase(copy(s,1,i-1))=upcase(envvar) then
  1307. begin
  1308. getenv:=copy(s,i+1,length(s)-i);
  1309. break;
  1310. end;
  1311. { next string entry}
  1312. hp:=hp+strlen(hp)+1;
  1313. end;
  1314. FreeEnvironmentStrings(p);
  1315. end;
  1316. {$else defined(win32) or defined(win64)}
  1317. {$ifdef wince}
  1318. Function GetEnv(P:string):Pchar;
  1319. begin
  1320. { WinCE does not have environment strings.
  1321. Add some way to specify heaptrc options? }
  1322. GetEnv:=nil;
  1323. end;
  1324. {$else wince}
  1325. Function GetEnv(P:string):Pchar;
  1326. {
  1327. Searches the environment for a string with name p and
  1328. returns a pchar to it's value.
  1329. A pchar is used to accomodate for strings of length > 255
  1330. }
  1331. var
  1332. ep : ppchar;
  1333. i : ptruint;
  1334. found : boolean;
  1335. Begin
  1336. p:=p+'='; {Else HOST will also find HOSTNAME, etc}
  1337. ep:=envp;
  1338. found:=false;
  1339. if ep<>nil then
  1340. begin
  1341. while (not found) and (ep^<>nil) do
  1342. begin
  1343. found:=true;
  1344. for i:=1 to length(p) do
  1345. if p[i]<>ep^[i-1] then
  1346. begin
  1347. found:=false;
  1348. break;
  1349. end;
  1350. if not found then
  1351. inc(ep);
  1352. end;
  1353. end;
  1354. if found then
  1355. getenv:=ep^+length(p)
  1356. else
  1357. getenv:=nil;
  1358. end;
  1359. {$endif wince}
  1360. {$endif win32}
  1361. procedure LoadEnvironment;
  1362. var
  1363. i,j : ptruint;
  1364. s : string;
  1365. begin
  1366. s:=Getenv('HEAPTRC');
  1367. if pos('keepreleased',s)>0 then
  1368. keepreleased:=true;
  1369. if pos('disabled',s)>0 then
  1370. useheaptrace:=false;
  1371. if pos('nohalt',s)>0 then
  1372. haltonerror:=false;
  1373. if pos('haltonnotreleased',s)>0 then
  1374. HaltOnNotReleased :=true;
  1375. i:=pos('log=',s);
  1376. if i>0 then
  1377. begin
  1378. outputstr:=copy(s,i+4,255);
  1379. j:=pos(' ',outputstr);
  1380. if j=0 then
  1381. j:=length(outputstr)+1;
  1382. delete(outputstr,j,255);
  1383. end;
  1384. end;
  1385. Initialization
  1386. LoadEnvironment;
  1387. { heaptrc can be disabled from the environment }
  1388. if useheaptrace then
  1389. TraceInit;
  1390. finalization
  1391. if useheaptrace then
  1392. TraceExit;
  1393. end.