heaptrc.pp 50 KB

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