heaptrc.pp 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832
  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. if not assigned(p) then
  716. exit(0);
  717. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  718. TraceMemSize:=pp^.size;
  719. end;
  720. function TraceFreeMem(p:pointer):ptruint;
  721. var
  722. l : ptruint;
  723. pp : pheap_mem_info;
  724. begin
  725. if p=nil then
  726. begin
  727. TraceFreeMem:=0;
  728. exit;
  729. end;
  730. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  731. l:=SysMemSize(pp);
  732. dec(l,sizeof(theap_mem_info)+pp^.extra_info_size);
  733. if add_tail then
  734. dec(l,tail_size);
  735. { this can never happend normaly }
  736. if pp^.size>l then
  737. begin
  738. if useownfile then
  739. dump_wrong_size(pp,l,ownfile)
  740. else
  741. dump_wrong_size(pp,l,textoutput^);
  742. {$ifdef EXTRA}
  743. dump_wrong_size(pp,l,error_file);
  744. {$endif EXTRA}
  745. end;
  746. TraceFreeMem:=TraceFreeMemSize(p,pp^.size);
  747. end;
  748. {*****************************************************************************
  749. ReAllocMem
  750. *****************************************************************************}
  751. function TraceReAllocMem(var p:pointer;size:ptruint):Pointer;
  752. var
  753. newP: pointer;
  754. i, allocsize,
  755. movesize : ptruint;
  756. pl : pdword;
  757. pp,prevpp{$ifdef EXTRA},ppv{$endif} : pheap_mem_info;
  758. oldsize,
  759. oldextrasize,
  760. oldexactsize : ptruint;
  761. old_fill_extra_info_proc : tfillextrainfoproc;
  762. old_display_extra_info_proc : tdisplayextrainfoproc;
  763. loc_info: pheap_info;
  764. begin
  765. { Free block? }
  766. if size=0 then
  767. begin
  768. if p<>nil then
  769. TraceFreeMem(p);
  770. p:=nil;
  771. TraceReallocMem:=P;
  772. exit;
  773. end;
  774. { Allocate a new block? }
  775. if p=nil then
  776. begin
  777. p:=TraceGetMem(size);
  778. TraceReallocMem:=P;
  779. exit;
  780. end;
  781. { Resize block }
  782. loc_info:=@heap_info;
  783. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  784. { test block }
  785. if ((pp^.sig<>longword(AllocateSig)) or usecrc) and
  786. ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
  787. begin
  788. loc_info^.error_in_heap:=true;
  789. if useownfile then
  790. dump_error(pp,ownfile)
  791. else
  792. dump_error(pp,textoutput^);
  793. {$ifdef EXTRA}
  794. dump_error(pp,error_file);
  795. {$endif EXTRA}
  796. { don't release anything in this case !! }
  797. if haltonerror then halt(1);
  798. exit;
  799. end;
  800. { save info }
  801. oldsize:=pp^.size;
  802. oldextrasize:=pp^.extra_info_size;
  803. oldexactsize:=pp^.exact_info_size;
  804. if pp^.extra_info_size>0 then
  805. begin
  806. old_fill_extra_info_proc:=pp^.extra_info^.fillproc;
  807. old_display_extra_info_proc:=pp^.extra_info^.displayproc;
  808. end;
  809. { Do the real ReAllocMem, but alloc also for the info block }
  810. {$ifdef cpuarm}
  811. allocsize:=(size + 3) and not 3+sizeof(theap_mem_info)+pp^.extra_info_size;
  812. {$else cpuarm}
  813. allocsize:=size+sizeof(theap_mem_info)+pp^.extra_info_size;
  814. {$endif cpuarm}
  815. if add_tail then
  816. inc(allocsize,tail_size);
  817. { Try to resize the block, if not possible we need to do a
  818. getmem, move data, freemem }
  819. prevpp:=pp;
  820. if not SysTryResizeMem(pp,allocsize) then
  821. begin
  822. { get a new block }
  823. newP := TraceGetMem(size);
  824. { move the data }
  825. if newP <> nil then
  826. begin
  827. movesize:=TraceMemSize(p);
  828. {if the old size is larger than the new size,
  829. move only the new size}
  830. if movesize>size then
  831. movesize:=size;
  832. move(p^,newP^,movesize);
  833. end;
  834. { release p }
  835. traceFreeMem(p);
  836. { return the new pointer }
  837. p:=newp;
  838. traceReAllocMem := newp;
  839. exit;
  840. end
  841. else
  842. begin
  843. if (pp<>prevpp) then
  844. begin
  845. { We need to update the previous/next chains }
  846. if assigned(pp^.previous) then
  847. pp^.previous^.next:=pp;
  848. if assigned(pp^.next) then
  849. pp^.next^.previous:=pp;
  850. if prevpp=loc_info^.heap_mem_root then
  851. loc_info^.heap_mem_root:=pp;
  852. {$ifdef EXTRA}
  853. { remove prevpp from prev_valid chain }
  854. ppv:=loc_info^.heap_valid_last;
  855. if (ppv=prevpp) then
  856. loc_info^.heap_valid_last:=pp^.prev_valid
  857. else
  858. begin
  859. while assigned(ppv) do
  860. begin
  861. if (ppv^.prev_valid=prevpp) then
  862. begin
  863. ppv^.prev_valid:=pp^.prev_valid;
  864. if prevpp=loc_info^.heap_valid_first then
  865. loc_info^.heap_valid_first:=ppv;
  866. ppv:=nil;
  867. end
  868. else
  869. ppv:=ppv^.prev_valid;
  870. end;
  871. end;
  872. { Reinsert new value in last position }
  873. pp^.prev_valid:=loc_info^.heap_valid_last;
  874. loc_info^.heap_valid_last:=pp;
  875. if not assigned(loc_info^.heap_valid_first) then
  876. loc_info^.heap_valid_first:=pp;
  877. {$endif EXTRA}
  878. end;
  879. end;
  880. { Recreate the info block }
  881. pp^.sig:=longword(AllocateSig);
  882. pp^.size:=size;
  883. pp^.extra_info_size:=oldextrasize;
  884. pp^.exact_info_size:=oldexactsize;
  885. { add the new extra_info and tail }
  886. if pp^.extra_info_size>0 then
  887. begin
  888. pp^.extra_info:=pointer(pp)+allocsize-pp^.extra_info_size;
  889. fillchar(pp^.extra_info^,extra_info_size,0);
  890. pp^.extra_info^.check:=cardinal(CheckSig);
  891. pp^.extra_info^.fillproc:=old_fill_extra_info_proc;
  892. pp^.extra_info^.displayproc:=old_display_extra_info_proc;
  893. if assigned(pp^.extra_info^.fillproc) then
  894. pp^.extra_info^.fillproc(@pp^.extra_info^.data);
  895. end
  896. else
  897. pp^.extra_info:=nil;
  898. if add_tail then
  899. begin
  900. { Calculate position from start because of arm
  901. specific alignment }
  902. pl:=pointer(pp)+sizeof(theap_mem_info)+pp^.size;
  903. for i:=1 to tail_size div sizeof(dword) do
  904. begin
  905. unaligned(pl^):=dword(AllocateSig);
  906. inc(pointer(pl),sizeof(dword));
  907. end;
  908. end;
  909. { adjust like a freemem and then a getmem, so you get correct
  910. results in the summary display }
  911. inc(loc_info^.freemem_size,oldsize);
  912. inc(loc_info^.freemem8_size,(oldsize+7) and not 7);
  913. inc(loc_info^.getmem_size,size);
  914. inc(loc_info^.getmem8_size,(size+7) and not 7);
  915. { generate new backtrace }
  916. CaptureBacktrace(1,tracesize,@pp^.calls[1]);
  917. { regenerate signature }
  918. if usecrc then
  919. pp^.sig:=calculate_sig(pp);
  920. { return the pointer }
  921. p:=pointer(pp)+sizeof(theap_mem_info);
  922. TraceReAllocmem:=p;
  923. end;
  924. {*****************************************************************************
  925. Check pointer
  926. *****************************************************************************}
  927. {$ifndef Unix}
  928. {$S-}
  929. {$endif}
  930. {$ifdef go32v2}
  931. var
  932. __stklen : longword;external name '__stklen';
  933. __stkbottom : longword;external name '__stkbottom';
  934. ebss : longword; external name 'end';
  935. {$endif go32v2}
  936. {$ifdef linux}
  937. var
  938. etext: ptruint; external name '_etext';
  939. edata : ptruint; external name '_edata';
  940. eend : ptruint; external name '_end';
  941. {$endif}
  942. {$ifdef freebsd}
  943. var
  944. text_start: ptruint; external name '__executable_start';
  945. etext: ptruint; external name '_etext';
  946. eend : ptruint; external name '_end';
  947. {$endif}
  948. {$ifdef os2}
  949. (* Currently still EMX based - possibly to be changed in the future. *)
  950. var
  951. etext: ptruint; external name '_etext';
  952. edata : ptruint; external name '_edata';
  953. eend : ptruint; external name '_end';
  954. {$endif}
  955. {$ifdef windows}
  956. var
  957. sdata : ptruint; external name '__data_start__';
  958. edata : ptruint; external name '__data_end__';
  959. sbss : ptruint; external name '__bss_start__';
  960. ebss : ptruint; external name '__bss_end__';
  961. TLSKey : PDWord; external name '_FPC_TlsKey';
  962. TLSSize : DWord; external name '_FPC_TlsSize';
  963. function TlsGetValue(dwTlsIndex : DWord) : pointer;
  964. {$ifdef wince}cdecl{$else}stdcall{$endif};external KernelDLL name 'TlsGetValue';
  965. {$endif}
  966. {$ifdef BEOS}
  967. const
  968. B_ERROR = -1;
  969. type
  970. area_id = Longint;
  971. function area_for(addr : Pointer) : area_id;
  972. cdecl; external 'root' name 'area_for';
  973. {$endif BEOS}
  974. procedure CheckPointer(p : pointer); [public, alias : 'FPC_CHECKPOINTER'];
  975. var
  976. i : ptruint;
  977. pp : pheap_mem_info;
  978. loc_info: pheap_info;
  979. {$ifdef go32v2}
  980. get_ebp,stack_top : longword;
  981. bss_end : longword;
  982. {$endif go32v2}
  983. {$ifdef windows}
  984. datap : pointer;
  985. {$endif windows}
  986. ptext : ^text;
  987. begin
  988. if p=nil then
  989. runerror(204);
  990. i:=0;
  991. loc_info:=@heap_info;
  992. if useownfile then
  993. ptext:=@ownfile
  994. else
  995. ptext:=textoutput;
  996. {$ifdef go32v2}
  997. if ptruint(p)<$1000 then
  998. runerror(216);
  999. asm
  1000. movl %ebp,get_ebp
  1001. leal ebss,%eax
  1002. movl %eax,bss_end
  1003. end;
  1004. stack_top:=__stkbottom+__stklen;
  1005. { allow all between start of code and end of bss }
  1006. if ptruint(p)<=bss_end then
  1007. exit;
  1008. { stack can be above heap !! }
  1009. if (ptruint(p)>=get_ebp) and (ptruint(p)<=stack_top) then
  1010. exit;
  1011. {$endif go32v2}
  1012. { I don't know where the stack is in other OS !! }
  1013. {$ifdef windows}
  1014. { inside stack ? }
  1015. if (ptruint(p)>ptruint(get_frame)) and
  1016. (p<StackTop) then
  1017. exit;
  1018. { inside data, rdata ... bss }
  1019. if (ptruint(p)>=ptruint(@sdata)) and (ptruint(p)<ptruint(@ebss)) then
  1020. exit;
  1021. { is program multi-threaded and p inside Threadvar range? }
  1022. if TlsKey^<>-1 then
  1023. begin
  1024. datap:=TlsGetValue(tlskey^);
  1025. if ((ptruint(p)>=ptruint(datap)) and
  1026. (ptruint(p)<ptruint(datap)+TlsSize)) then
  1027. exit;
  1028. end;
  1029. {$endif windows}
  1030. {$IFDEF OS2}
  1031. { inside stack ? }
  1032. if (PtrUInt (P) > PtrUInt (Get_Frame)) and
  1033. (PtrUInt (P) < PtrUInt (StackTop)) then
  1034. exit;
  1035. { inside data or bss ? }
  1036. if (PtrUInt (P) >= PtrUInt (@etext)) and (PtrUInt (P) < PtrUInt (@eend)) then
  1037. exit;
  1038. {$ENDIF OS2}
  1039. {$ifdef linux}
  1040. { inside stack ? }
  1041. if (ptruint(p)>ptruint(get_frame)) and
  1042. (ptruint(p)<ptruint(StackTop)) then
  1043. exit;
  1044. { inside data or bss ? }
  1045. if (ptruint(p)>=ptruint(@etext)) and (ptruint(p)<ptruint(@eend)) then
  1046. exit;
  1047. {$endif linux}
  1048. {$ifdef freebsd}
  1049. { inside stack ? }
  1050. if (ptruint(p)>ptruint(get_frame)) and
  1051. (ptruint(p)<ptruint(StackTop)) then
  1052. exit;
  1053. { inside data or bss ? }
  1054. if (ptruint(p)>=ptruint(@text_start)) and (ptruint(p)<ptruint(@eend)) then
  1055. exit;
  1056. {$endif linux}
  1057. {$ifdef morphos}
  1058. { inside stack ? }
  1059. if (ptruint(p)<ptruint(StackTop)) and (ptruint(p)>ptruint(StackBottom)) then
  1060. exit;
  1061. { inside data or bss ? }
  1062. {$WARNING data and bss checking missing }
  1063. {$endif morphos}
  1064. {$ifdef darwin}
  1065. {$warning No checkpointer support yet for Darwin}
  1066. exit;
  1067. {$endif}
  1068. {$ifdef BEOS}
  1069. // if we find the address in a known area in our current process,
  1070. // then it is a valid one
  1071. if area_for(p) <> B_ERROR then
  1072. exit;
  1073. {$endif BEOS}
  1074. { first try valid list faster }
  1075. {$ifdef EXTRA}
  1076. pp:=loc_info^.heap_valid_last;
  1077. while pp<>nil do
  1078. begin
  1079. { inside this valid block ! }
  1080. { we can be changing the extrainfo !! }
  1081. if (ptruint(p)>=ptruint(pp)+sizeof(theap_mem_info){+extra_info_size}) and
  1082. (ptruint(p)<=ptruint(pp)+sizeof(theap_mem_info)+extra_info_size+pp^.size) then
  1083. begin
  1084. { check allocated block }
  1085. if ((pp^.sig=longword(AllocateSig)) and not usecrc) or
  1086. ((pp^.sig=calculate_sig(pp)) and usecrc) or
  1087. { special case of the fill_extra_info call }
  1088. ((pp=loc_info^.heap_valid_last) and usecrc and (pp^.sig=longword(AllocateSig))
  1089. and loc_info^.inside_trace_getmem) then
  1090. exit
  1091. else
  1092. begin
  1093. writeln(ptext^,'corrupted heap_mem_info');
  1094. dump_error(pp,ptext^);
  1095. halt(1);
  1096. end;
  1097. end
  1098. else
  1099. pp:=pp^.prev_valid;
  1100. inc(i);
  1101. if i>loc_info^.getmem_cnt-loc_info^.freemem_cnt then
  1102. begin
  1103. writeln(ptext^,'error in linked list of heap_mem_info');
  1104. halt(1);
  1105. end;
  1106. end;
  1107. i:=0;
  1108. {$endif EXTRA}
  1109. pp:=loc_info^.heap_mem_root;
  1110. while pp<>nil do
  1111. begin
  1112. { inside this block ! }
  1113. if (ptruint(p)>=ptruint(pp)+sizeof(theap_mem_info)+ptruint(extra_info_size)) and
  1114. (ptruint(p)<=ptruint(pp)+sizeof(theap_mem_info)+ptruint(extra_info_size)+ptruint(pp^.size)) then
  1115. { allocated block }
  1116. if ((pp^.sig=longword(AllocateSig)) and not usecrc) or
  1117. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  1118. exit
  1119. else
  1120. begin
  1121. writeln(ptext^,'pointer $',hexstr(p),' points into invalid memory block');
  1122. dump_error(pp,ptext^);
  1123. runerror(204);
  1124. end;
  1125. pp:=pp^.previous;
  1126. inc(i);
  1127. if i>loc_info^.getmem_cnt then
  1128. begin
  1129. writeln(ptext^,'error in linked list of heap_mem_info');
  1130. halt(1);
  1131. end;
  1132. end;
  1133. writeln(ptext^,'pointer $',hexstr(p),' does not point to valid memory block');
  1134. dump_stack(ptext^,1);
  1135. runerror(204);
  1136. end;
  1137. {*****************************************************************************
  1138. Dump Heap
  1139. *****************************************************************************}
  1140. procedure dumpheap;
  1141. begin
  1142. DumpHeap(GlobalSkipIfNoLeaks);
  1143. end;
  1144. const
  1145. {$ifdef BSD} // dlopen is in libc on FreeBSD.
  1146. LibDL = 'c';
  1147. {$else}
  1148. {$ifdef HAIKU}
  1149. LibDL = 'root';
  1150. {$else}
  1151. LibDL = 'dl';
  1152. {$endif}
  1153. {$endif}
  1154. {$if defined(LINUX) or defined(BSD)}
  1155. type
  1156. Pdl_info = ^dl_info;
  1157. dl_info = record
  1158. dli_fname : Pansichar;
  1159. dli_fbase : pointer;
  1160. dli_sname : Pansichar;
  1161. dli_saddr : pointer;
  1162. end;
  1163. // *BSD isn't flagged for "weak" support in 3.2.2
  1164. {$if defined(BSD) and (FPC_FULLVERSION<30300)}
  1165. function _dladdr(Lib:pointer; info: Pdl_info): Longint; cdecl; external LibDL name 'dladdr';
  1166. {$else}
  1167. function _dladdr(Lib:pointer; info: Pdl_info): Longint; cdecl; weakexternal LibDL name 'dladdr';
  1168. {$endif}
  1169. {$elseif defined(MSWINDOWS)}
  1170. function _GetModuleFileNameA(hModule:HModule;lpFilename:PAnsiChar;nSize:cardinal):cardinal;stdcall; external 'kernel32' name 'GetModuleFileNameA';
  1171. {$endif}
  1172. function GetModuleName:string;
  1173. {$ifdef MSWINDOWS}
  1174. var
  1175. sz:cardinal;
  1176. buf:array[0..8191] of ansichar;
  1177. {$endif}
  1178. {$if defined(LINUX) or defined(BSD)}
  1179. var
  1180. res:integer;
  1181. dli:dl_info;
  1182. {$endif}
  1183. begin
  1184. GetModuleName:='';
  1185. {$if defined(LINUX) or defined(BSD)}
  1186. if assigned(@_dladdr) then
  1187. begin
  1188. res:=_dladdr(@ParamStr,@dli); { get any non-eliminated address in SO space }
  1189. if res<=0 then
  1190. exit;
  1191. if Assigned(dli.dli_fname) then
  1192. GetModuleName:=PAnsiChar(dli.dli_fname);
  1193. end
  1194. else
  1195. GetModuleName:=ParamStr(0);
  1196. {$elseif defined(MSWINDOWS)}
  1197. sz:=_GetModuleFileNameA(hInstance,PAnsiChar(@buf),sizeof(buf));
  1198. if sz>0 then
  1199. setstring(GetModuleName,PAnsiChar(@buf),sz)
  1200. {$else}
  1201. GetModuleName:=ParamStr(0);
  1202. {$endif}
  1203. end;
  1204. procedure dumpheap(SkipIfNoLeaks : Boolean);
  1205. var
  1206. pp : pheap_mem_info;
  1207. i : ptrint;
  1208. ExpectedHeapFree : ptruint;
  1209. status : TFPCHeapStatus;
  1210. ptext : ^text;
  1211. loc_info: pheap_info;
  1212. begin
  1213. loc_info:=@heap_info;
  1214. if useownfile then
  1215. ptext:=@ownfile
  1216. else
  1217. ptext:=textoutput;
  1218. pp:=loc_info^.heap_mem_root;
  1219. if ((loc_info^.getmem_size-loc_info^.freemem_size)=0) and SkipIfNoLeaks then
  1220. exit;
  1221. Writeln(ptext^,'Heap dump by heaptrc unit of "'+GetModuleName()+'"');
  1222. Writeln(ptext^,loc_info^.getmem_cnt, ' memory blocks allocated : ',
  1223. loc_info^.getmem_size,'/',loc_info^.getmem8_size);
  1224. Writeln(ptext^,loc_info^.freemem_cnt,' memory blocks freed : ',
  1225. loc_info^.freemem_size,'/',loc_info^.freemem8_size);
  1226. Writeln(ptext^,loc_info^.getmem_cnt-loc_info^.freemem_cnt,
  1227. ' unfreed memory blocks : ',loc_info^.getmem_size-loc_info^.freemem_size);
  1228. status:=SysGetFPCHeapStatus;
  1229. Write(ptext^,'True heap size : ',status.CurrHeapSize);
  1230. if EntryMemUsed > 0 then
  1231. Writeln(ptext^,' (',EntryMemUsed,' used in System startup)')
  1232. else
  1233. Writeln(ptext^);
  1234. Writeln(ptext^,'True free heap : ',status.CurrHeapFree);
  1235. ExpectedHeapFree:=status.CurrHeapSize
  1236. -(loc_info^.getmem8_size-loc_info^.freemem8_size)
  1237. -(loc_info^.getmem_cnt-loc_info^.freemem_cnt)*(sizeof(theap_mem_info)+extra_info_size)
  1238. -EntryMemUsed;
  1239. If ExpectedHeapFree<>status.CurrHeapFree then
  1240. Writeln(ptext^,'Should be : ',ExpectedHeapFree);
  1241. i:=loc_info^.getmem_cnt-loc_info^.freemem_cnt;
  1242. while pp<>nil do
  1243. begin
  1244. if i<0 then
  1245. begin
  1246. Writeln(ptext^,'Error in heap memory list');
  1247. Writeln(ptext^,'More memory blocks than expected');
  1248. exit;
  1249. end;
  1250. if ((pp^.sig=longword(AllocateSig)) and not usecrc) or
  1251. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  1252. begin
  1253. { this one was not released !! }
  1254. if exitcode<>203 then
  1255. call_stack(pp,ptext^);
  1256. dec(i);
  1257. end
  1258. else if pp^.sig<>longword(ReleaseSig) then
  1259. begin
  1260. dump_error(pp,ptext^);
  1261. if @stderr<>ptext then
  1262. dump_error(pp,stderr);
  1263. {$ifdef EXTRA}
  1264. dump_error(pp,error_file);
  1265. {$endif EXTRA}
  1266. loc_info^.error_in_heap:=true;
  1267. end
  1268. {$ifdef EXTRA}
  1269. else if pp^.release_sig<>calculate_release_sig(pp) then
  1270. begin
  1271. dump_change_after(pp,ptext^);
  1272. dump_change_after(pp,error_file);
  1273. loc_info^.error_in_heap:=true;
  1274. end
  1275. {$else not EXTRA}
  1276. else
  1277. begin
  1278. if released_modified(pp,ptext^) then
  1279. exitcode:=203;
  1280. end;
  1281. {$endif EXTRA}
  1282. ;
  1283. pp:=pp^.previous;
  1284. end;
  1285. if HaltOnNotReleased and (loc_info^.getmem_cnt<>loc_info^.freemem_cnt) then
  1286. exitcode:=203;
  1287. end;
  1288. {*****************************************************************************
  1289. AllocMem
  1290. *****************************************************************************}
  1291. function TraceAllocMem(size:ptruint):Pointer;
  1292. begin
  1293. TraceAllocMem := TraceGetMem(size);
  1294. if Assigned(TraceAllocMem) then
  1295. FillChar(TraceAllocMem^, TraceMemSize(TraceAllocMem), 0);
  1296. end;
  1297. {*****************************************************************************
  1298. No specific tracing calls
  1299. *****************************************************************************}
  1300. procedure TraceInitThread;
  1301. var
  1302. loc_info: pheap_info;
  1303. begin
  1304. loc_info := @heap_info;
  1305. {$ifdef EXTRA}
  1306. loc_info^.heap_valid_first := nil;
  1307. loc_info^.heap_valid_last := nil;
  1308. {$endif}
  1309. loc_info^.heap_mem_root := nil;
  1310. loc_info^.getmem_cnt := 0;
  1311. loc_info^.freemem_cnt := 0;
  1312. loc_info^.getmem_size := 0;
  1313. loc_info^.freemem_size := 0;
  1314. loc_info^.getmem8_size := 0;
  1315. loc_info^.freemem8_size := 0;
  1316. loc_info^.error_in_heap := false;
  1317. loc_info^.inside_trace_getmem := false;
  1318. EntryMemUsed := SysGetFPCHeapStatus.CurrHeapUsed;
  1319. end;
  1320. procedure TraceRelocateHeap;
  1321. begin
  1322. main_relo_todolist := @heap_info.heap_free_todo;
  1323. {$ifdef FPC_HAS_FEATURE_THREADING}
  1324. initcriticalsection(todo_lock);
  1325. {$endif}
  1326. end;
  1327. procedure move_heap_info(src_info, dst_info: pheap_info);
  1328. var
  1329. heap_mem: pheap_mem_info;
  1330. begin
  1331. if src_info^.heap_free_todo <> nil then
  1332. finish_heap_free_todo_list(src_info);
  1333. if dst_info^.heap_free_todo <> nil then
  1334. finish_heap_free_todo_list(dst_info);
  1335. heap_mem := src_info^.heap_mem_root;
  1336. if heap_mem <> nil then
  1337. begin
  1338. repeat
  1339. heap_mem^.todolist := @dst_info^.heap_free_todo;
  1340. if heap_mem^.previous = nil then break;
  1341. heap_mem := heap_mem^.previous;
  1342. until false;
  1343. heap_mem^.previous := dst_info^.heap_mem_root;
  1344. if dst_info^.heap_mem_root <> nil then
  1345. dst_info^.heap_mem_root^.next := heap_mem;
  1346. dst_info^.heap_mem_root := src_info^.heap_mem_root;
  1347. end;
  1348. inc(dst_info^.getmem_cnt, src_info^.getmem_cnt);
  1349. inc(dst_info^.getmem_size, src_info^.getmem_size);
  1350. inc(dst_info^.getmem8_size, src_info^.getmem8_size);
  1351. inc(dst_info^.freemem_cnt, src_info^.freemem_cnt);
  1352. inc(dst_info^.freemem_size, src_info^.freemem_size);
  1353. inc(dst_info^.freemem8_size, src_info^.freemem8_size);
  1354. dst_info^.error_in_heap := dst_info^.error_in_heap or src_info^.error_in_heap;
  1355. {$ifdef EXTRA}
  1356. if assigned(dst_info^.heap_valid_first) then
  1357. dst_info^.heap_valid_first^.prev_valid := src_info^.heap_valid_last
  1358. else
  1359. dst_info^.heap_valid_last := src_info^.heap_valid_last;
  1360. dst_info^.heap_valid_first := src_info^.heap_valid_first;
  1361. {$endif}
  1362. end;
  1363. procedure TraceExitThread;
  1364. var
  1365. loc_info: pheap_info;
  1366. begin
  1367. loc_info := @heap_info;
  1368. {$ifdef FPC_HAS_FEATURE_THREADING}
  1369. entercriticalsection(todo_lock);
  1370. {$endif}
  1371. move_heap_info(loc_info, @orphaned_info);
  1372. {$ifdef FPC_HAS_FEATURE_THREADING}
  1373. leavecriticalsection(todo_lock);
  1374. {$endif}
  1375. end;
  1376. function TraceGetHeapStatus:THeapStatus;
  1377. begin
  1378. TraceGetHeapStatus:=SysGetHeapStatus;
  1379. end;
  1380. function TraceGetFPCHeapStatus:TFPCHeapStatus;
  1381. begin
  1382. TraceGetFPCHeapStatus:=SysGetFPCHeapStatus;
  1383. end;
  1384. {*****************************************************************************
  1385. Program Hooks
  1386. *****************************************************************************}
  1387. Procedure SetHeapTraceOutput(const name : string);
  1388. var i : ptruint;
  1389. begin
  1390. if useownfile then
  1391. begin
  1392. useownfile:=false;
  1393. close(ownfile);
  1394. end;
  1395. assign(ownfile,name);
  1396. {$I-}
  1397. append(ownfile);
  1398. if IOResult<>0 then
  1399. begin
  1400. Rewrite(ownfile);
  1401. if IOResult<>0 then
  1402. begin
  1403. Writeln(textoutput^,'[heaptrc] Unable to open "',name,'", writing output to stderr instead.');
  1404. useownfile:=false;
  1405. exit;
  1406. end;
  1407. end;
  1408. {$I+}
  1409. useownfile:=true;
  1410. for i:=0 to Paramcount do
  1411. write(ownfile,paramstr(i),' ');
  1412. writeln(ownfile);
  1413. end;
  1414. procedure SetHeapTraceOutput(var ATextOutput : Text);
  1415. Begin
  1416. useowntextoutput := True;
  1417. textoutput := @ATextOutput;
  1418. end;
  1419. procedure SetHeapExtraInfo( size : ptruint;fillproc : tfillextrainfoproc;displayproc : tdisplayextrainfoproc);
  1420. begin
  1421. { the total size must stay multiple of 8, also allocate 2 pointers for
  1422. the fill and display procvars }
  1423. exact_info_size:=size + sizeof(theap_extra_info);
  1424. extra_info_size:=(exact_info_size+7) and not 7;
  1425. fill_extra_info_proc:=fillproc;
  1426. display_extra_info_proc:=displayproc;
  1427. end;
  1428. {*****************************************************************************
  1429. Install MemoryManager
  1430. *****************************************************************************}
  1431. const
  1432. TraceManager:TMemoryManager=(
  1433. NeedLock : true;
  1434. Getmem : @TraceGetMem;
  1435. Freemem : @TraceFreeMem;
  1436. FreememSize : @TraceFreeMemSize;
  1437. AllocMem : @TraceAllocMem;
  1438. ReAllocMem : @TraceReAllocMem;
  1439. MemSize : @TraceMemSize;
  1440. InitThread: @TraceInitThread;
  1441. DoneThread: @TraceExitThread;
  1442. RelocateHeap: @TraceRelocateHeap;
  1443. GetHeapStatus : @TraceGetHeapStatus;
  1444. GetFPCHeapStatus : @TraceGetFPCHeapStatus;
  1445. );
  1446. var
  1447. PrevMemoryManager : TMemoryManager;
  1448. procedure TraceInit;
  1449. begin
  1450. textoutput := @stderr;
  1451. useowntextoutput := false;
  1452. MakeCRC32Tbl;
  1453. main_orig_todolist := @heap_info.heap_free_todo;
  1454. main_relo_todolist := nil;
  1455. TraceInitThread;
  1456. GetMemoryManager(PrevMemoryManager);
  1457. SetMemoryManager(TraceManager);
  1458. useownfile:=false;
  1459. if outputstr <> '' then
  1460. SetHeapTraceOutput(outputstr);
  1461. {$ifdef EXTRA}
  1462. {$i-}
  1463. Assign(error_file,'heap.err');
  1464. Rewrite(error_file);
  1465. {$i+}
  1466. if IOResult<>0 then
  1467. begin
  1468. writeln('[heaptrc] Unable to create heap.err extra log file, writing output to screen.');
  1469. Assign(error_file,'');
  1470. Rewrite(error_file);
  1471. end;
  1472. {$endif EXTRA}
  1473. { if multithreading was initialized before heaptrc gets initialized (this is currently
  1474. the case for windows dlls), then RelocateHeap gets never called and the lock
  1475. must be initialized already here,
  1476. however, IsMultithread is not set in this case on windows,
  1477. it is set only if a new thread is started
  1478. }
  1479. {$IfNDef WINDOWS}
  1480. if IsMultithread then
  1481. {$EndIf WINDOWS}
  1482. TraceRelocateHeap;
  1483. end;
  1484. procedure TraceExit;
  1485. begin
  1486. { no dump if error
  1487. because this gives long long listings }
  1488. { clear inoutres, in case the program that quit didn't }
  1489. ioresult;
  1490. if (exitcode<>0) and (erroraddr<>nil) then
  1491. begin
  1492. if useownfile then
  1493. begin
  1494. Writeln(ownfile,'No heap dump by heaptrc unit');
  1495. Writeln(ownfile,'Exitcode = ',exitcode);
  1496. end
  1497. else
  1498. begin
  1499. Writeln(textoutput^,'No heap dump by heaptrc unit');
  1500. Writeln(textoutput^,'Exitcode = ',exitcode);
  1501. end;
  1502. if useownfile then
  1503. begin
  1504. useownfile:=false;
  1505. close(ownfile);
  1506. end;
  1507. exit;
  1508. end;
  1509. { Disable heaptrc memory manager to avoid problems }
  1510. SetMemoryManager(PrevMemoryManager);
  1511. move_heap_info(@orphaned_info, @heap_info);
  1512. dumpheap;
  1513. if heap_info.error_in_heap and (exitcode=0) then
  1514. exitcode:=203;
  1515. {$ifdef FPC_HAS_FEATURE_THREADING}
  1516. if main_relo_todolist <> nil then
  1517. donecriticalsection(todo_lock);
  1518. {$endif}
  1519. {$ifdef EXTRA}
  1520. Close(error_file);
  1521. {$endif EXTRA}
  1522. if useownfile then
  1523. begin
  1524. useownfile:=false;
  1525. close(ownfile);
  1526. end;
  1527. if useowntextoutput then
  1528. begin
  1529. useowntextoutput := false;
  1530. close(textoutput^);
  1531. end;
  1532. end;
  1533. {$if defined(win32) or defined(win64)}
  1534. function GetEnvironmentStrings : pansichar; stdcall;
  1535. external 'kernel32' name 'GetEnvironmentStringsA';
  1536. function FreeEnvironmentStrings(p : pansichar) : longbool; stdcall;
  1537. external 'kernel32' name 'FreeEnvironmentStringsA';
  1538. Function GetEnv(envvar: ansistring): ansistring;
  1539. var
  1540. s : ansistring;
  1541. i : ptruint;
  1542. hp,p : pansichar;
  1543. begin
  1544. getenv:='';
  1545. p:=GetEnvironmentStrings;
  1546. hp:=p;
  1547. while hp^<>#0 do
  1548. begin
  1549. s:=strpas(hp);
  1550. i:=pos('=',s);
  1551. if upcase(copy(s,1,i-1))=upcase(envvar) then
  1552. begin
  1553. getenv:=copy(s,i+1,length(s)-i);
  1554. break;
  1555. end;
  1556. { next string entry}
  1557. hp:=hp+strlen(hp)+1;
  1558. end;
  1559. FreeEnvironmentStrings(p);
  1560. end;
  1561. {$elseif defined(wince) or defined(sinclairql)}
  1562. Function GetEnv(P:string):PAnsichar;
  1563. begin
  1564. { WinCE does not have environment strings.
  1565. Add some way to specify heaptrc options? }
  1566. GetEnv:=nil;
  1567. end;
  1568. {$elseif defined(msdos) or defined(msxdos)}
  1569. type
  1570. PFarChar=^AnsiChar;far;
  1571. PPFarChar=^PFarChar;
  1572. var
  1573. envp: PPFarChar;external name '__fpc_envp';
  1574. Function GetEnv(P:ansistring):ansistring;
  1575. var
  1576. ep : ppfarchar;
  1577. pc : pfarchar;
  1578. i : smallint;
  1579. found : boolean;
  1580. Begin
  1581. getenv:='';
  1582. p:=p+'='; {Else HOST will also find HOSTNAME, etc}
  1583. ep:=envp;
  1584. found:=false;
  1585. if ep<>nil then
  1586. begin
  1587. while (not found) and (ep^<>nil) do
  1588. begin
  1589. found:=true;
  1590. for i:=1 to length(p) do
  1591. if p[i]<>ep^[i-1] then
  1592. begin
  1593. found:=false;
  1594. break;
  1595. end;
  1596. if not found then
  1597. inc(ep);
  1598. end;
  1599. end;
  1600. if found then
  1601. begin
  1602. pc:=ep^+length(p);
  1603. while pc^<>#0 do
  1604. begin
  1605. getenv:=getenv+pc^;
  1606. Inc(pc);
  1607. end;
  1608. end;
  1609. end;
  1610. {$else}
  1611. Function GetEnv(P:ansistring):Pansichar;
  1612. {
  1613. Searches the environment for a string with name p and
  1614. returns a pansichar to it's value.
  1615. A pansichar is used to accomodate for strings of length > 255
  1616. }
  1617. var
  1618. ep : ppansichar;
  1619. i : ptruint;
  1620. found : boolean;
  1621. Begin
  1622. p:=p+'='; {Else HOST will also find HOSTNAME, etc}
  1623. ep:=envp;
  1624. found:=false;
  1625. if ep<>nil then
  1626. begin
  1627. while (not found) and (ep^<>nil) do
  1628. begin
  1629. found:=true;
  1630. for i:=1 to length(p) do
  1631. if p[i]<>ep^[i-1] then
  1632. begin
  1633. found:=false;
  1634. break;
  1635. end;
  1636. if not found then
  1637. inc(ep);
  1638. end;
  1639. end;
  1640. if found then
  1641. getenv:=ep^+length(p)
  1642. else
  1643. getenv:=nil;
  1644. end;
  1645. {$endif}
  1646. procedure LoadEnvironment;
  1647. var
  1648. i,j : ptruint;
  1649. s,s2 : ansistring;
  1650. err : word;
  1651. begin
  1652. s:=Getenv('HEAPTRC');
  1653. if pos('keepreleased',s)>0 then
  1654. keepreleased:=true;
  1655. if pos('disabled',s)>0 then
  1656. useheaptrace:=false;
  1657. if pos('nohalt',s)>0 then
  1658. haltonerror:=false;
  1659. if pos('haltonnotreleased',s)>0 then
  1660. HaltOnNotReleased :=true;
  1661. if pos('skipifnoleaks',s)>0 then
  1662. GlobalSkipIfNoLeaks :=true;
  1663. if pos('tail_size=',s)>0 then
  1664. begin
  1665. i:=pos('tail_size=',s)+length('tail_size=');
  1666. s2:='';
  1667. while (i<=length(s)) and (s[i] in ['0'..'9']) do
  1668. begin
  1669. s2:=s2+s[i];
  1670. inc(i);
  1671. end;
  1672. val(s2,tail_size,err);
  1673. if err=0 then
  1674. tail_size:=((tail_size + sizeof(ptruint)-1) div sizeof(ptruint)) * sizeof(ptruint)
  1675. else
  1676. tail_size:=sizeof(ptruint);
  1677. add_tail:=(tail_size > 0);
  1678. end;
  1679. i:=pos('log=',s);
  1680. if i>0 then
  1681. begin
  1682. outputstr:=copy(s,i+4,255);
  1683. j:=pos(' ',outputstr);
  1684. if j=0 then
  1685. j:=length(outputstr)+1;
  1686. delete(outputstr,j,255);
  1687. end;
  1688. end;
  1689. Initialization
  1690. LoadEnvironment;
  1691. { heaptrc can be disabled from the environment }
  1692. if useheaptrace then
  1693. TraceInit;
  1694. finalization
  1695. if useheaptrace then
  1696. TraceExit;
  1697. end.