heaptrc.pp 49 KB

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