heaptrc.pp 50 KB

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