heaptrc.pp 43 KB

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