heaptrc.pp 48 KB

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