heaptrc.pp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315
  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. unit heaptrc;
  12. interface
  13. {$inline on}
  14. {$ifdef FPC_HEAPTRC_EXTRA}
  15. {$define EXTRA}
  16. {$endif FPC_HEAPTRC_EXTRA}
  17. {$checkpointer off}
  18. {$goto on}
  19. {$TYPEDADDRESS on}
  20. {$if defined(win32) or defined(wince)}
  21. {$define windows}
  22. {$endif}
  23. Procedure DumpHeap;
  24. { define EXTRA to add more
  25. tests :
  26. - keep all memory after release and
  27. check by CRC value if not changed after release
  28. WARNING this needs extremely much memory (PM) }
  29. type
  30. tFillExtraInfoProc = procedure(p : pointer);
  31. tdisplayextrainfoProc = procedure (var ptext : text;p : pointer);
  32. { Allows to add info pre memory block, see ppheap.pas of the compiler
  33. for example source }
  34. procedure SetHeapExtraInfo( size : ptrint;fillproc : tfillextrainfoproc;displayproc : tdisplayextrainfoproc);
  35. { Redirection of the output to a file }
  36. procedure SetHeapTraceOutput(const name : string);
  37. const
  38. { tracing level
  39. splitted in two if memory is released !! }
  40. {$ifdef EXTRA}
  41. tracesize = 16;
  42. {$else EXTRA}
  43. tracesize = 8;
  44. {$endif EXTRA}
  45. { install heaptrc memorymanager }
  46. useheaptrace : boolean=true;
  47. { less checking }
  48. quicktrace : boolean=true;
  49. { calls halt() on error by default !! }
  50. HaltOnError : boolean = true;
  51. { Halt on exit if any memory was not freed }
  52. HaltOnNotReleased : boolean = false;
  53. { set this to true if you suspect that memory
  54. is freed several times }
  55. {$ifdef EXTRA}
  56. keepreleased : boolean=true;
  57. {$else EXTRA}
  58. keepreleased : boolean=false;
  59. {$endif EXTRA}
  60. { add a small footprint at the end of memory blocks, this
  61. can check for memory overwrites at the end of a block }
  62. add_tail : boolean = true;
  63. { put crc in sig
  64. this allows to test for writing into that part }
  65. usecrc : boolean = true;
  66. implementation
  67. type
  68. pptrint = ^ptrint;
  69. const
  70. { allows to add custom info in heap_mem_info, this is the size that will
  71. be allocated for this information }
  72. extra_info_size : ptrint = 0;
  73. exact_info_size : ptrint = 0;
  74. EntryMemUsed : ptrint = 0;
  75. { function to fill this info up }
  76. fill_extra_info_proc : TFillExtraInfoProc = nil;
  77. display_extra_info_proc : TDisplayExtraInfoProc = nil;
  78. error_in_heap : boolean = false;
  79. inside_trace_getmem : boolean = false;
  80. { indicates where the output will be redirected }
  81. { only set using environment variables }
  82. outputstr : shortstring = '';
  83. type
  84. pheap_extra_info = ^theap_extra_info;
  85. theap_extra_info = record
  86. check : cardinal; { used to check if the procvar is still valid }
  87. fillproc : tfillextrainfoProc;
  88. displayproc : tdisplayextrainfoProc;
  89. data : record
  90. end;
  91. end;
  92. { warning the size of theap_mem_info
  93. must be a multiple of 8
  94. because otherwise you will get
  95. problems when releasing the usual memory part !!
  96. sizeof(theap_mem_info = 16+tracesize*4 so
  97. tracesize must be even !! PM }
  98. pheap_mem_info = ^theap_mem_info;
  99. theap_mem_info = record
  100. previous,
  101. next : pheap_mem_info;
  102. size : ptrint;
  103. sig : longword;
  104. {$ifdef EXTRA}
  105. release_sig : longword;
  106. prev_valid : pheap_mem_info;
  107. {$endif EXTRA}
  108. calls : array [1..tracesize] of pointer;
  109. exact_info_size : word;
  110. extra_info_size : word;
  111. extra_info : pheap_extra_info;
  112. end;
  113. var
  114. useownfile : boolean;
  115. ownfile : text;
  116. {$ifdef EXTRA}
  117. error_file : text;
  118. heap_valid_first,
  119. heap_valid_last : pheap_mem_info;
  120. {$endif EXTRA}
  121. heap_mem_root : pheap_mem_info;
  122. getmem_cnt,
  123. freemem_cnt : ptrint;
  124. getmem_size,
  125. freemem_size : ptrint;
  126. getmem8_size,
  127. freemem8_size : ptrint;
  128. {*****************************************************************************
  129. Crc 32
  130. *****************************************************************************}
  131. var
  132. Crc32Tbl : array[0..255] of longword;
  133. procedure MakeCRC32Tbl;
  134. var
  135. crc : longword;
  136. i,n : byte;
  137. begin
  138. for i:=0 to 255 do
  139. begin
  140. crc:=i;
  141. for n:=1 to 8 do
  142. if odd(crc) then
  143. crc:=(crc shr 1) xor $edb88320
  144. else
  145. crc:=crc shr 1;
  146. Crc32Tbl[i]:=crc;
  147. end;
  148. end;
  149. Function UpdateCrc32(InitCrc:longword;var InBuf;InLen:ptrint):longword;
  150. var
  151. i : ptrint;
  152. p : pchar;
  153. begin
  154. p:=@InBuf;
  155. for i:=1 to InLen do
  156. begin
  157. InitCrc:=Crc32Tbl[byte(InitCrc) xor byte(p^)] xor (InitCrc shr 8);
  158. inc(p);
  159. end;
  160. UpdateCrc32:=InitCrc;
  161. end;
  162. Function calculate_sig(p : pheap_mem_info) : longword;
  163. var
  164. crc : longword;
  165. pl : pptrint;
  166. begin
  167. crc:=cardinal($ffffffff);
  168. crc:=UpdateCrc32(crc,p^.size,sizeof(ptrint));
  169. crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(ptrint));
  170. if p^.extra_info_size>0 then
  171. crc:=UpdateCrc32(crc,p^.extra_info^,p^.exact_info_size);
  172. if add_tail then
  173. begin
  174. { Check also 4 bytes just after allocation !! }
  175. pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info)+p^.size;
  176. crc:=UpdateCrc32(crc,pl^,sizeof(ptrint));
  177. end;
  178. calculate_sig:=crc;
  179. end;
  180. {$ifdef EXTRA}
  181. Function calculate_release_sig(p : pheap_mem_info) : longword;
  182. var
  183. crc : longword;
  184. pl : pptrint;
  185. begin
  186. crc:=$ffffffff;
  187. crc:=UpdateCrc32(crc,p^.size,sizeof(ptrint));
  188. crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(ptrint));
  189. if p^.extra_info_size>0 then
  190. crc:=UpdateCrc32(crc,p^.extra_info^,p^.exact_info_size);
  191. { Check the whole of the whole allocation }
  192. pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info);
  193. crc:=UpdateCrc32(crc,pl^,p^.size);
  194. { Check also 4 bytes just after allocation !! }
  195. if add_tail then
  196. begin
  197. { Check also 4 bytes just after allocation !! }
  198. pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info)+p^.size;
  199. crc:=UpdateCrc32(crc,pl^,sizeof(ptrint));
  200. end;
  201. calculate_release_sig:=crc;
  202. end;
  203. {$endif EXTRA}
  204. {*****************************************************************************
  205. Helpers
  206. *****************************************************************************}
  207. procedure call_stack(pp : pheap_mem_info;var ptext : text);
  208. var
  209. i : ptrint;
  210. begin
  211. writeln(ptext,'Call trace for block $',hexstr(ptrint(pointer(pp)+sizeof(theap_mem_info)),2*sizeof(pointer)),' size ',pp^.size);
  212. for i:=1 to tracesize do
  213. if pp^.calls[i]<>nil then
  214. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  215. { the check is done to be sure that the procvar is not overwritten }
  216. if assigned(pp^.extra_info) and
  217. (pp^.extra_info^.check=$12345678) and
  218. assigned(pp^.extra_info^.displayproc) then
  219. pp^.extra_info^.displayproc(ptext,@pp^.extra_info^.data);
  220. end;
  221. procedure call_free_stack(pp : pheap_mem_info;var ptext : text);
  222. var
  223. i : ptrint;
  224. begin
  225. writeln(ptext,'Call trace for block at $',hexstr(ptrint(pointer(pp)+sizeof(theap_mem_info)),2*sizeof(pointer)),' size ',pp^.size);
  226. for i:=1 to tracesize div 2 do
  227. if pp^.calls[i]<>nil then
  228. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  229. writeln(ptext,' was released at ');
  230. for i:=(tracesize div 2)+1 to tracesize do
  231. if pp^.calls[i]<>nil then
  232. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  233. { the check is done to be sure that the procvar is not overwritten }
  234. if assigned(pp^.extra_info) and
  235. (pp^.extra_info^.check=$12345678) and
  236. assigned(pp^.extra_info^.displayproc) then
  237. pp^.extra_info^.displayproc(ptext,@pp^.extra_info^.data);
  238. end;
  239. procedure dump_already_free(p : pheap_mem_info;var ptext : text);
  240. begin
  241. Writeln(ptext,'Marked memory at $',HexStr(ptrint(pointer(p)+sizeof(theap_mem_info)),2*sizeof(pointer)),' released');
  242. call_free_stack(p,ptext);
  243. Writeln(ptext,'freed again at');
  244. dump_stack(ptext,get_caller_frame(get_frame));
  245. end;
  246. procedure dump_error(p : pheap_mem_info;var ptext : text);
  247. begin
  248. Writeln(ptext,'Marked memory at $',HexStr(ptrint(pointer(p)+sizeof(theap_mem_info)),2*sizeof(pointer)),' invalid');
  249. Writeln(ptext,'Wrong signature $',hexstr(p^.sig,8),' instead of ',hexstr(calculate_sig(p),8));
  250. dump_stack(ptext,get_caller_frame(get_frame));
  251. end;
  252. {$ifdef EXTRA}
  253. procedure dump_change_after(p : pheap_mem_info;var ptext : text);
  254. var pp : pchar;
  255. i : ptrint;
  256. begin
  257. Writeln(ptext,'Marked memory at $',HexStr(ptrint(pointer(p)+sizeof(theap_mem_info)),2*sizeof(pointer)),' invalid');
  258. Writeln(ptext,'Wrong release CRC $',hexstr(p^.release_sig,8),' instead of ',hexstr(calculate_release_sig(p),8));
  259. Writeln(ptext,'This memory was changed after call to freemem !');
  260. call_free_stack(p,ptext);
  261. pp:=pointer(p)+sizeof(theap_mem_info);
  262. for i:=0 to p^.size-1 do
  263. if byte(pp[i])<>$F0 then
  264. Writeln(ptext,'offset',i,':$',hexstr(i,2*sizeof(pointer)),'"',pp[i],'"');
  265. end;
  266. {$endif EXTRA}
  267. procedure dump_wrong_size(p : pheap_mem_info;size : ptrint;var ptext : text);
  268. begin
  269. Writeln(ptext,'Marked memory at $',HexStr(ptrint(pointer(p)+sizeof(theap_mem_info)),2*sizeof(pointer)),' invalid');
  270. Writeln(ptext,'Wrong size : ',p^.size,' allocated ',size,' freed');
  271. dump_stack(ptext,get_caller_frame(get_frame));
  272. { the check is done to be sure that the procvar is not overwritten }
  273. if assigned(p^.extra_info) and
  274. (p^.extra_info^.check=$12345678) and
  275. assigned(p^.extra_info^.displayproc) then
  276. p^.extra_info^.displayproc(ptext,@p^.extra_info^.data);
  277. call_stack(p,ptext);
  278. end;
  279. function is_in_getmem_list (p : pheap_mem_info) : boolean;
  280. var
  281. i : ptrint;
  282. pp : pheap_mem_info;
  283. begin
  284. is_in_getmem_list:=false;
  285. pp:=heap_mem_root;
  286. i:=0;
  287. while pp<>nil do
  288. begin
  289. if ((pp^.sig<>$DEADBEEF) or usecrc) and
  290. ((pp^.sig<>calculate_sig(pp)) or not usecrc) and
  291. (pp^.sig <>$AAAAAAAA) then
  292. begin
  293. if useownfile then
  294. writeln(ownfile,'error in linked list of heap_mem_info')
  295. else
  296. writeln(stderr,'error in linked list of heap_mem_info');
  297. RunError(204);
  298. end;
  299. if pp=p then
  300. is_in_getmem_list:=true;
  301. pp:=pp^.previous;
  302. inc(i);
  303. if i>getmem_cnt-freemem_cnt then
  304. if useownfile then
  305. writeln(ownfile,'error in linked list of heap_mem_info')
  306. else
  307. writeln(stderr,'error in linked list of heap_mem_info');
  308. end;
  309. end;
  310. {*****************************************************************************
  311. TraceGetMem
  312. *****************************************************************************}
  313. Function TraceGetMem(size:ptrint):pointer;
  314. var
  315. allocsize,i : ptrint;
  316. oldbp,
  317. bp : pointer;
  318. pl : pdword;
  319. p : pointer;
  320. pp : pheap_mem_info;
  321. begin
  322. inc(getmem_size,size);
  323. inc(getmem8_size,((size+7) div 8)*8);
  324. { Do the real GetMem, but alloc also for the info block }
  325. {$ifdef cpuarm}
  326. allocsize:=(size + 3) and not 3+sizeof(theap_mem_info)+extra_info_size;
  327. {$else cpuarm}
  328. allocsize:=size+sizeof(theap_mem_info)+extra_info_size;
  329. {$endif cpuarm}
  330. if add_tail then
  331. inc(allocsize,sizeof(ptrint));
  332. { if ReturnNilIfGrowHeapFails is true
  333. SysGetMem can return nil }
  334. p:=SysGetMem(allocsize);
  335. if (p=nil) then
  336. begin
  337. TraceGetMem:=nil;
  338. exit;
  339. end;
  340. pp:=pheap_mem_info(p);
  341. inc(p,sizeof(theap_mem_info));
  342. { Create the info block }
  343. pp^.sig:=$DEADBEEF;
  344. pp^.size:=size;
  345. pp^.extra_info_size:=extra_info_size;
  346. pp^.exact_info_size:=exact_info_size;
  347. {
  348. the end of the block contains:
  349. <tail> 4 bytes
  350. <extra_info> X bytes
  351. }
  352. if extra_info_size>0 then
  353. begin
  354. pp^.extra_info:=pointer(pp)+allocsize-extra_info_size;
  355. fillchar(pp^.extra_info^,extra_info_size,0);
  356. pp^.extra_info^.check:=$12345678;
  357. pp^.extra_info^.fillproc:=fill_extra_info_proc;
  358. pp^.extra_info^.displayproc:=display_extra_info_proc;
  359. if assigned(fill_extra_info_proc) then
  360. begin
  361. inside_trace_getmem:=true;
  362. fill_extra_info_proc(@pp^.extra_info^.data);
  363. inside_trace_getmem:=false;
  364. end;
  365. end
  366. else
  367. pp^.extra_info:=nil;
  368. if add_tail then
  369. begin
  370. pl:=pointer(pp)+allocsize-pp^.extra_info_size-sizeof(ptrint);
  371. {$ifdef FPC_SUPPORTS_UNALIGNED}
  372. unaligned(pl^):=$DEADBEEF;
  373. {$else FPC_SUPPORTS_UNALIGNED}
  374. pl^:=$DEADBEEF;
  375. {$endif FPC_SUPPORTS_UNALIGNED}
  376. end;
  377. { clear the memory }
  378. fillchar(p^,size,#255);
  379. { retrieve backtrace info }
  380. bp:=get_caller_frame(get_frame);
  381. { valid bp? }
  382. if (bp>=StackBottom) and (bp<(StackBottom + StackLength)) then
  383. for i:=1 to tracesize do
  384. begin
  385. pp^.calls[i]:=get_caller_addr(bp);
  386. oldbp:=bp;
  387. bp:=get_caller_frame(bp);
  388. if (bp<oldbp) or (bp>(StackBottom + StackLength)) then
  389. break;
  390. end;
  391. { insert in the linked list }
  392. if heap_mem_root<>nil then
  393. heap_mem_root^.next:=pp;
  394. pp^.previous:=heap_mem_root;
  395. pp^.next:=nil;
  396. {$ifdef EXTRA}
  397. pp^.prev_valid:=heap_valid_last;
  398. heap_valid_last:=pp;
  399. if not assigned(heap_valid_first) then
  400. heap_valid_first:=pp;
  401. {$endif EXTRA}
  402. heap_mem_root:=pp;
  403. { must be changed before fill_extra_info is called
  404. because checkpointer can be called from within
  405. fill_extra_info PM }
  406. inc(getmem_cnt);
  407. { update the signature }
  408. if usecrc then
  409. pp^.sig:=calculate_sig(pp);
  410. TraceGetmem:=p;
  411. end;
  412. {*****************************************************************************
  413. TraceFreeMem
  414. *****************************************************************************}
  415. function TraceFreeMemSize(p:pointer;size:ptrint):ptrint;
  416. var
  417. i,ppsize : ptrint;
  418. bp : pointer;
  419. pp : pheap_mem_info;
  420. {$ifdef EXTRA}
  421. pp2 : pheap_mem_info;
  422. {$endif}
  423. extra_size : ptrint;
  424. ptext : ^text;
  425. begin
  426. if useownfile then
  427. ptext:=@ownfile
  428. else
  429. ptext:=@stderr;
  430. if p=nil then
  431. begin
  432. TraceFreeMemSize:=0;
  433. exit;
  434. end;
  435. inc(freemem_size,size);
  436. inc(freemem8_size,((size+7) div 8)*8);
  437. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  438. ppsize:= size + sizeof(theap_mem_info)+pp^.extra_info_size;
  439. if add_tail then
  440. inc(ppsize,sizeof(ptrint));
  441. if not quicktrace then
  442. begin
  443. if not(is_in_getmem_list(pp)) then
  444. RunError(204);
  445. end;
  446. if (pp^.sig=$AAAAAAAA) and not usecrc then
  447. begin
  448. error_in_heap:=true;
  449. dump_already_free(pp,ptext^);
  450. if haltonerror then halt(1);
  451. end
  452. else if ((pp^.sig<>$DEADBEEF) or usecrc) and
  453. ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
  454. begin
  455. error_in_heap:=true;
  456. dump_error(pp,ptext^);
  457. {$ifdef EXTRA}
  458. dump_error(pp,error_file);
  459. {$endif EXTRA}
  460. { don't release anything in this case !! }
  461. if haltonerror then halt(1);
  462. exit;
  463. end
  464. else if pp^.size<>size then
  465. begin
  466. error_in_heap:=true;
  467. dump_wrong_size(pp,size,ptext^);
  468. {$ifdef EXTRA}
  469. dump_wrong_size(pp,size,error_file);
  470. {$endif EXTRA}
  471. if haltonerror then halt(1);
  472. { don't release anything in this case !! }
  473. exit;
  474. end;
  475. { save old values }
  476. extra_size:=pp^.extra_info_size;
  477. { now it is released !! }
  478. pp^.sig:=$AAAAAAAA;
  479. if not keepreleased then
  480. begin
  481. if pp^.next<>nil then
  482. pp^.next^.previous:=pp^.previous;
  483. if pp^.previous<>nil then
  484. pp^.previous^.next:=pp^.next;
  485. if pp=heap_mem_root then
  486. heap_mem_root:=heap_mem_root^.previous;
  487. end
  488. else
  489. begin
  490. bp:=get_caller_frame(get_frame);
  491. if (bp>=StackBottom) and (bp<(StackBottom + StackLength)) then
  492. for i:=(tracesize div 2)+1 to tracesize do
  493. begin
  494. pp^.calls[i]:=get_caller_addr(bp);
  495. bp:=get_caller_frame(bp);
  496. if not((bp>=StackBottom) and (bp<(StackBottom + StackLength))) then
  497. break;
  498. end;
  499. end;
  500. inc(freemem_cnt);
  501. { clear the memory }
  502. fillchar(p^,size,#240); { $F0 will lead to GFP if used as pointer ! }
  503. { this way we keep all info about all released memory !! }
  504. if keepreleased then
  505. begin
  506. {$ifdef EXTRA}
  507. { We want to check if the memory was changed after release !! }
  508. pp^.release_sig:=calculate_release_sig(pp);
  509. if pp=heap_valid_last then
  510. begin
  511. heap_valid_last:=pp^.prev_valid;
  512. if pp=heap_valid_first then
  513. heap_valid_first:=nil;
  514. TraceFreememsize:=size;
  515. exit;
  516. end;
  517. pp2:=heap_valid_last;
  518. while assigned(pp2) do
  519. begin
  520. if pp2^.prev_valid=pp then
  521. begin
  522. pp2^.prev_valid:=pp^.prev_valid;
  523. if pp=heap_valid_first then
  524. heap_valid_first:=pp2;
  525. TraceFreememsize:=size;
  526. exit;
  527. end
  528. else
  529. pp2:=pp2^.prev_valid;
  530. end;
  531. {$endif EXTRA}
  532. TraceFreememsize:=size;
  533. exit;
  534. end;
  535. { release the normal memory at least }
  536. i:=SysFreeMemSize(pp,ppsize);
  537. { return the correct size }
  538. dec(i,sizeof(theap_mem_info)+extra_size);
  539. if add_tail then
  540. dec(i,sizeof(ptrint));
  541. TraceFreeMemSize:=i;
  542. end;
  543. function TraceMemSize(p:pointer):ptrint;
  544. var
  545. pp : pheap_mem_info;
  546. begin
  547. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  548. TraceMemSize:=pp^.size;
  549. end;
  550. function TraceFreeMem(p:pointer):ptrint;
  551. var
  552. l : ptrint;
  553. pp : pheap_mem_info;
  554. begin
  555. if p=nil then
  556. begin
  557. TraceFreeMem:=0;
  558. exit;
  559. end;
  560. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  561. l:=SysMemSize(pp);
  562. dec(l,sizeof(theap_mem_info)+pp^.extra_info_size);
  563. if add_tail then
  564. dec(l,sizeof(ptrint));
  565. { this can never happend normaly }
  566. if pp^.size>l then
  567. begin
  568. if useownfile then
  569. dump_wrong_size(pp,l,ownfile)
  570. else
  571. dump_wrong_size(pp,l,stderr);
  572. {$ifdef EXTRA}
  573. dump_wrong_size(pp,l,error_file);
  574. {$endif EXTRA}
  575. end;
  576. TraceFreeMem:=TraceFreeMemSize(p,pp^.size);
  577. end;
  578. {*****************************************************************************
  579. ReAllocMem
  580. *****************************************************************************}
  581. function TraceReAllocMem(var p:pointer;size:ptrint):Pointer;
  582. var
  583. newP: pointer;
  584. allocsize,
  585. movesize,
  586. i : ptrint;
  587. oldbp,
  588. bp : pointer;
  589. pl : pdword;
  590. pp : pheap_mem_info;
  591. oldsize,
  592. oldextrasize,
  593. oldexactsize : ptrint;
  594. old_fill_extra_info_proc : tfillextrainfoproc;
  595. old_display_extra_info_proc : tdisplayextrainfoproc;
  596. begin
  597. { Free block? }
  598. if size=0 then
  599. begin
  600. if p<>nil then
  601. TraceFreeMem(p);
  602. p:=nil;
  603. TraceReallocMem:=P;
  604. exit;
  605. end;
  606. { Allocate a new block? }
  607. if p=nil then
  608. begin
  609. p:=TraceGetMem(size);
  610. TraceReallocMem:=P;
  611. exit;
  612. end;
  613. { Resize block }
  614. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  615. { test block }
  616. if ((pp^.sig<>$DEADBEEF) or usecrc) and
  617. ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
  618. begin
  619. error_in_heap:=true;
  620. if useownfile then
  621. dump_error(pp,ownfile)
  622. else
  623. dump_error(pp,stderr);
  624. {$ifdef EXTRA}
  625. dump_error(pp,error_file);
  626. {$endif EXTRA}
  627. { don't release anything in this case !! }
  628. if haltonerror then halt(1);
  629. exit;
  630. end;
  631. { save info }
  632. oldsize:=pp^.size;
  633. oldextrasize:=pp^.extra_info_size;
  634. oldexactsize:=pp^.exact_info_size;
  635. if pp^.extra_info_size>0 then
  636. begin
  637. old_fill_extra_info_proc:=pp^.extra_info^.fillproc;
  638. old_display_extra_info_proc:=pp^.extra_info^.displayproc;
  639. end;
  640. { Do the real ReAllocMem, but alloc also for the info block }
  641. {$ifdef cpuarm}
  642. allocsize:=(size + 3) and not 3+sizeof(theap_mem_info)+pp^.extra_info_size;
  643. {$else cpuarm}
  644. allocsize:=size+sizeof(theap_mem_info)+pp^.extra_info_size;
  645. {$endif cpuarm}
  646. if add_tail then
  647. inc(allocsize,sizeof(ptrint));
  648. { Try to resize the block, if not possible we need to do a
  649. getmem, move data, freemem }
  650. if not SysTryResizeMem(pp,allocsize) then
  651. begin
  652. { get a new block }
  653. newP := TraceGetMem(size);
  654. { move the data }
  655. if newP <> nil then
  656. begin
  657. movesize:=TraceMemSize(p);
  658. {if the old size is larger than the new size,
  659. move only the new size}
  660. if movesize>size then
  661. movesize:=size;
  662. move(p^,newP^,movesize);
  663. end;
  664. { release p }
  665. traceFreeMem(p);
  666. { return the new pointer }
  667. p:=newp;
  668. traceReAllocMem := newp;
  669. exit;
  670. end;
  671. { Recreate the info block }
  672. pp^.sig:=$DEADBEEF;
  673. pp^.size:=size;
  674. pp^.extra_info_size:=oldextrasize;
  675. pp^.exact_info_size:=oldexactsize;
  676. { add the new extra_info and tail }
  677. if pp^.extra_info_size>0 then
  678. begin
  679. pp^.extra_info:=pointer(pp)+allocsize-pp^.extra_info_size;
  680. fillchar(pp^.extra_info^,extra_info_size,0);
  681. pp^.extra_info^.check:=$12345678;
  682. pp^.extra_info^.fillproc:=old_fill_extra_info_proc;
  683. pp^.extra_info^.displayproc:=old_display_extra_info_proc;
  684. if assigned(pp^.extra_info^.fillproc) then
  685. pp^.extra_info^.fillproc(@pp^.extra_info^.data);
  686. end
  687. else
  688. pp^.extra_info:=nil;
  689. if add_tail then
  690. begin
  691. pl:=pointer(pp)+allocsize-pp^.extra_info_size-sizeof(ptrint);
  692. {$ifdef FPC_SUPPORTS_UNALIGNED}
  693. unaligned(pl^):=$DEADBEEF;
  694. {$else FPC_SUPPORTS_UNALIGNED}
  695. pl^:=$DEADBEEF;
  696. {$endif FPC_SUPPORTS_UNALIGNED}
  697. end;
  698. { adjust like a freemem and then a getmem, so you get correct
  699. results in the summary display }
  700. inc(freemem_size,oldsize);
  701. inc(freemem8_size,((oldsize+7) div 8)*8);
  702. inc(getmem_size,size);
  703. inc(getmem8_size,((size+7) div 8)*8);
  704. { generate new backtrace }
  705. bp:=get_caller_frame(get_frame);
  706. if (bp>=StackBottom) and (bp<(StackBottom + StackLength)) then
  707. for i:=1 to tracesize do
  708. begin
  709. pp^.calls[i]:=get_caller_addr(bp);
  710. oldbp:=bp;
  711. bp:=get_caller_frame(bp);
  712. if (bp<oldbp) or (bp>(StackBottom + StackLength)) then
  713. break;
  714. end;
  715. { regenerate signature }
  716. if usecrc then
  717. pp^.sig:=calculate_sig(pp);
  718. { return the pointer }
  719. p:=pointer(pp)+sizeof(theap_mem_info);
  720. TraceReAllocmem:=p;
  721. end;
  722. {*****************************************************************************
  723. Check pointer
  724. *****************************************************************************}
  725. {$ifndef Unix}
  726. {$S-}
  727. {$endif}
  728. {$ifdef go32v2}
  729. var
  730. __stklen : longword;external name '__stklen';
  731. __stkbottom : longword;external name '__stkbottom';
  732. ebss : longword; external name 'end';
  733. {$endif go32v2}
  734. {$ifdef linux}
  735. var
  736. etext: ptruint; external name '_etext';
  737. edata : ptruint; external name '_edata';
  738. eend : ptruint; external name '_end';
  739. {$endif}
  740. {$ifdef os2}
  741. (* Currently still EMX based - possibly to be changed in the future. *)
  742. var
  743. etext: ptruint; external name '_etext';
  744. edata : ptruint; external name '_edata';
  745. eend : ptruint; external name '_end';
  746. {$endif}
  747. {$ifdef windows}
  748. var
  749. sdata : ptruint; external name '__data_start__';
  750. edata : ptruint; external name '__data_end__';
  751. sbss : ptruint; external name '__bss_start__';
  752. ebss : ptruint; external name '__bss_end__';
  753. {$endif}
  754. {$ifdef BEOS}
  755. const
  756. B_ERROR = -1;
  757. type
  758. area_id = Longint;
  759. function area_for(addr : Pointer) : area_id;
  760. cdecl; external 'root' name 'area_for';
  761. {$endif BEOS}
  762. procedure CheckPointer(p : pointer); [public, alias : 'FPC_CHECKPOINTER'];
  763. var
  764. i : ptrint;
  765. pp : pheap_mem_info;
  766. {$ifdef go32v2}
  767. get_ebp,stack_top : longword;
  768. bss_end : longword;
  769. {$endif go32v2}
  770. {$ifdef morphos}
  771. stack_top: longword;
  772. {$endif morphos}
  773. ptext : ^text;
  774. label
  775. _exit;
  776. begin
  777. if p=nil then
  778. runerror(204);
  779. i:=0;
  780. if useownfile then
  781. ptext:=@ownfile
  782. else
  783. ptext:=@stderr;
  784. {$ifdef go32v2}
  785. if ptruint(p)<$1000 then
  786. runerror(216);
  787. asm
  788. movl %ebp,get_ebp
  789. leal ebss,%eax
  790. movl %eax,bss_end
  791. end;
  792. stack_top:=__stkbottom+__stklen;
  793. { allow all between start of code and end of bss }
  794. if ptruint(p)<=bss_end then
  795. goto _exit;
  796. { stack can be above heap !! }
  797. if (ptruint(p)>=get_ebp) and (ptruint(p)<=stack_top) then
  798. goto _exit;
  799. {$endif go32v2}
  800. { I don't know where the stack is in other OS !! }
  801. {$ifdef windows}
  802. { inside stack ? }
  803. if (ptruint(p)>ptruint(get_frame)) and
  804. (p<StackTop) then
  805. goto _exit;
  806. { inside data ? }
  807. if (ptruint(p)>=ptruint(@sdata)) and (ptruint(p)<ptruint(@edata)) then
  808. goto _exit;
  809. { inside bss ? }
  810. if (ptruint(p)>=ptruint(@sbss)) and (ptruint(p)<ptruint(@ebss)) then
  811. goto _exit;
  812. {$endif windows}
  813. {$IFDEF OS2}
  814. { inside stack ? }
  815. if (PtrUInt (P) > PtrUInt (Get_Frame)) and
  816. (PtrUInt (P) < PtrUInt (StackTop)) then
  817. goto _exit;
  818. { inside data or bss ? }
  819. if (PtrUInt (P) >= PtrUInt (@etext)) and (PtrUInt (P) < PtrUInt (@eend)) then
  820. goto _exit;
  821. {$ENDIF OS2}
  822. {$ifdef linux}
  823. { inside stack ? }
  824. if (ptruint(p)>ptruint(get_frame)) and
  825. (ptruint(p)<$c0000000) then //todo: 64bit!
  826. goto _exit;
  827. { inside data or bss ? }
  828. if (ptruint(p)>=ptruint(@etext)) and (ptruint(p)<ptruint(@eend)) then
  829. goto _exit;
  830. {$endif linux}
  831. {$ifdef morphos}
  832. { inside stack ? }
  833. stack_top:=ptruint(StackBottom)+StackLength;
  834. if (ptruint(p)<stack_top) and (ptruint(p)>ptruint(StackBottom)) then
  835. goto _exit;
  836. { inside data or bss ? }
  837. {$WARNING data and bss checking missing }
  838. {$endif morphos}
  839. {$ifdef darwin}
  840. {$warning No checkpointer support yet for Darwin}
  841. exit;
  842. {$endif}
  843. {$ifdef BEOS}
  844. // if we find the address in a known area in our current process,
  845. // then it is a valid one
  846. if area_for(p) <> B_ERROR then
  847. goto _exit;
  848. {$endif BEOS}
  849. { first try valid list faster }
  850. {$ifdef EXTRA}
  851. pp:=heap_valid_last;
  852. while pp<>nil do
  853. begin
  854. { inside this valid block ! }
  855. { we can be changing the extrainfo !! }
  856. if (ptruint(p)>=ptruint(pp)+sizeof(theap_mem_info){+extra_info_size}) and
  857. (ptruint(p)<=ptruint(pp)+sizeof(theap_mem_info)+extra_info_size+pp^.size) then
  858. begin
  859. { check allocated block }
  860. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  861. ((pp^.sig=calculate_sig(pp)) and usecrc) or
  862. { special case of the fill_extra_info call }
  863. ((pp=heap_valid_last) and usecrc and (pp^.sig=$DEADBEEF)
  864. and inside_trace_getmem) then
  865. goto _exit
  866. else
  867. begin
  868. writeln(ptext^,'corrupted heap_mem_info');
  869. dump_error(pp,ptext^);
  870. halt(1);
  871. end;
  872. end
  873. else
  874. pp:=pp^.prev_valid;
  875. inc(i);
  876. if i>getmem_cnt-freemem_cnt then
  877. begin
  878. writeln(ptext^,'error in linked list of heap_mem_info');
  879. halt(1);
  880. end;
  881. end;
  882. i:=0;
  883. {$endif EXTRA}
  884. pp:=heap_mem_root;
  885. while pp<>nil do
  886. begin
  887. { inside this block ! }
  888. if (ptruint(p)>=ptruint(pp)+sizeof(theap_mem_info)+ptruint(extra_info_size)) and
  889. (ptruint(p)<=ptruint(pp)+sizeof(theap_mem_info)+ptruint(extra_info_size)+ptruint(pp^.size)) then
  890. { allocated block }
  891. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  892. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  893. goto _exit
  894. else
  895. begin
  896. writeln(ptext^,'pointer $',hexstr(ptrint(p),2*sizeof(pointer)),' points into invalid memory block');
  897. dump_error(pp,ptext^);
  898. runerror(204);
  899. end;
  900. pp:=pp^.previous;
  901. inc(i);
  902. if i>getmem_cnt then
  903. begin
  904. writeln(ptext^,'error in linked list of heap_mem_info');
  905. halt(1);
  906. end;
  907. end;
  908. writeln(ptext^,'pointer $',hexstr(ptrint(p),2*sizeof(pointer)),' does not point to valid memory block');
  909. dump_error(p,ptext^);
  910. runerror(204);
  911. _exit:
  912. end;
  913. {*****************************************************************************
  914. Dump Heap
  915. *****************************************************************************}
  916. procedure dumpheap;
  917. var
  918. pp : pheap_mem_info;
  919. i : ptrint;
  920. ExpectedHeapFree : ptrint;
  921. status : TFPCHeapStatus;
  922. ptext : ^text;
  923. begin
  924. if useownfile then
  925. ptext:=@ownfile
  926. else
  927. ptext:=@stderr;
  928. pp:=heap_mem_root;
  929. Writeln(ptext^,'Heap dump by heaptrc unit');
  930. Writeln(ptext^,getmem_cnt, ' memory blocks allocated : ',getmem_size,'/',getmem8_size);
  931. Writeln(ptext^,freemem_cnt,' memory blocks freed : ',freemem_size,'/',freemem8_size);
  932. Writeln(ptext^,getmem_cnt-freemem_cnt,' unfreed memory blocks : ',getmem_size-freemem_size);
  933. status:=SysGetFPCHeapStatus;
  934. Write(ptext^,'True heap size : ',status.CurrHeapSize);
  935. if EntryMemUsed > 0 then
  936. Writeln(ptext^,' (',EntryMemUsed,' used in System startup)')
  937. else
  938. Writeln(ptext^);
  939. Writeln(ptext^,'True free heap : ',status.CurrHeapFree);
  940. ExpectedHeapFree:=status.CurrHeapSize-(getmem8_size-freemem8_size)-
  941. (getmem_cnt-freemem_cnt)*(sizeof(theap_mem_info)+extra_info_size)-EntryMemUsed;
  942. If ExpectedHeapFree<>status.CurrHeapFree then
  943. Writeln(ptext^,'Should be : ',ExpectedHeapFree);
  944. i:=getmem_cnt-freemem_cnt;
  945. while pp<>nil do
  946. begin
  947. if i<0 then
  948. begin
  949. Writeln(ptext^,'Error in heap memory list');
  950. Writeln(ptext^,'More memory blocks than expected');
  951. exit;
  952. end;
  953. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  954. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  955. begin
  956. { this one was not released !! }
  957. if exitcode<>203 then
  958. call_stack(pp,ptext^);
  959. dec(i);
  960. end
  961. else if pp^.sig<>$AAAAAAAA then
  962. begin
  963. dump_error(pp,ptext^);
  964. {$ifdef EXTRA}
  965. dump_error(pp,error_file);
  966. {$endif EXTRA}
  967. error_in_heap:=true;
  968. end
  969. {$ifdef EXTRA}
  970. else if pp^.release_sig<>calculate_release_sig(pp) then
  971. begin
  972. dump_change_after(pp,ptext^);
  973. dump_change_after(pp,error_file);
  974. error_in_heap:=true;
  975. end
  976. {$endif EXTRA}
  977. ;
  978. pp:=pp^.previous;
  979. end;
  980. if HaltOnNotReleased and (getmem_cnt<>freemem_cnt) then
  981. exitcode:=203;
  982. end;
  983. {*****************************************************************************
  984. AllocMem
  985. *****************************************************************************}
  986. function TraceAllocMem(size:ptrint):Pointer;
  987. begin
  988. TraceAllocMem:=SysAllocMem(size);
  989. end;
  990. {*****************************************************************************
  991. No specific tracing calls
  992. *****************************************************************************}
  993. function TraceGetHeapStatus:THeapStatus;
  994. begin
  995. TraceGetHeapStatus:=SysGetHeapStatus;
  996. end;
  997. function TraceGetFPCHeapStatus:TFPCHeapStatus;
  998. begin
  999. TraceGetFPCHeapStatus:=SysGetFPCHeapStatus;
  1000. end;
  1001. {*****************************************************************************
  1002. Program Hooks
  1003. *****************************************************************************}
  1004. Procedure SetHeapTraceOutput(const name : string);
  1005. var i : ptrint;
  1006. begin
  1007. if useownfile then
  1008. begin
  1009. useownfile:=false;
  1010. close(ownfile);
  1011. end;
  1012. assign(ownfile,name);
  1013. {$I-}
  1014. append(ownfile);
  1015. if IOResult<>0 then
  1016. Rewrite(ownfile);
  1017. {$I+}
  1018. useownfile:=true;
  1019. for i:=0 to Paramcount do
  1020. write(ownfile,paramstr(i),' ');
  1021. writeln(ownfile);
  1022. end;
  1023. procedure SetHeapExtraInfo( size : ptrint;fillproc : tfillextrainfoproc;displayproc : tdisplayextrainfoproc);
  1024. begin
  1025. { the total size must stay multiple of 8, also allocate 2 pointers for
  1026. the fill and display procvars }
  1027. exact_info_size:=size + sizeof(theap_extra_info);
  1028. extra_info_size:=((exact_info_size+7) div 8)*8;
  1029. fill_extra_info_proc:=fillproc;
  1030. display_extra_info_proc:=displayproc;
  1031. end;
  1032. {*****************************************************************************
  1033. Install MemoryManager
  1034. *****************************************************************************}
  1035. const
  1036. TraceManager:TMemoryManager=(
  1037. NeedLock : true;
  1038. Getmem : @TraceGetMem;
  1039. Freemem : @TraceFreeMem;
  1040. FreememSize : @TraceFreeMemSize;
  1041. AllocMem : @TraceAllocMem;
  1042. ReAllocMem : @TraceReAllocMem;
  1043. MemSize : @TraceMemSize;
  1044. GetHeapStatus : @TraceGetHeapStatus;
  1045. GetFPCHeapStatus : @TraceGetFPCHeapStatus;
  1046. );
  1047. procedure TraceInit;
  1048. var
  1049. initheapstatus : TFPCHeapStatus;
  1050. begin
  1051. initheapstatus:=SysGetFPCHeapStatus;
  1052. EntryMemUsed:=initheapstatus.CurrHeapUsed;
  1053. MakeCRC32Tbl;
  1054. SetMemoryManager(TraceManager);
  1055. useownfile:=false;
  1056. if outputstr <> '' then
  1057. SetHeapTraceOutput(outputstr);
  1058. {$ifdef EXTRA}
  1059. Assign(error_file,'heap.err');
  1060. Rewrite(error_file);
  1061. {$endif EXTRA}
  1062. end;
  1063. procedure TraceExit;
  1064. begin
  1065. { no dump if error
  1066. because this gives long long listings }
  1067. { clear inoutres, in case the program that quit didn't }
  1068. ioresult;
  1069. if (exitcode<>0) and (erroraddr<>nil) then
  1070. begin
  1071. if useownfile then
  1072. begin
  1073. Writeln(ownfile,'No heap dump by heaptrc unit');
  1074. Writeln(ownfile,'Exitcode = ',exitcode);
  1075. end
  1076. else
  1077. begin
  1078. Writeln(stderr,'No heap dump by heaptrc unit');
  1079. Writeln(stderr,'Exitcode = ',exitcode);
  1080. end;
  1081. if useownfile then
  1082. begin
  1083. useownfile:=false;
  1084. close(ownfile);
  1085. end;
  1086. exit;
  1087. end;
  1088. if not error_in_heap then
  1089. Dumpheap;
  1090. if error_in_heap and (exitcode=0) then
  1091. exitcode:=203;
  1092. {$ifdef EXTRA}
  1093. Close(error_file);
  1094. {$endif EXTRA}
  1095. if useownfile then
  1096. begin
  1097. useownfile:=false;
  1098. close(ownfile);
  1099. end;
  1100. end;
  1101. {$if defined(win32) or defined(win64)}
  1102. function GetEnvironmentStrings : pchar; stdcall;
  1103. external 'kernel32' name 'GetEnvironmentStringsA';
  1104. function FreeEnvironmentStrings(p : pchar) : longbool; stdcall;
  1105. external 'kernel32' name 'FreeEnvironmentStringsA';
  1106. Function GetEnv(envvar: string): string;
  1107. var
  1108. s : string;
  1109. i : ptrint;
  1110. hp,p : pchar;
  1111. begin
  1112. getenv:='';
  1113. p:=GetEnvironmentStrings;
  1114. hp:=p;
  1115. while hp^<>#0 do
  1116. begin
  1117. s:=strpas(hp);
  1118. i:=pos('=',s);
  1119. if upcase(copy(s,1,i-1))=upcase(envvar) then
  1120. begin
  1121. getenv:=copy(s,i+1,length(s)-i);
  1122. break;
  1123. end;
  1124. { next string entry}
  1125. hp:=hp+strlen(hp)+1;
  1126. end;
  1127. FreeEnvironmentStrings(p);
  1128. end;
  1129. {$else defined(win32) or defined(win64)}
  1130. {$ifdef wince}
  1131. Function GetEnv(P:string):Pchar;
  1132. begin
  1133. { WinCE does not have environment strings.
  1134. Add some way to specify heaptrc options? }
  1135. GetEnv:=nil;
  1136. end;
  1137. {$else wince}
  1138. Function GetEnv(P:string):Pchar;
  1139. {
  1140. Searches the environment for a string with name p and
  1141. returns a pchar to it's value.
  1142. A pchar is used to accomodate for strings of length > 255
  1143. }
  1144. var
  1145. ep : ppchar;
  1146. i : ptrint;
  1147. found : boolean;
  1148. Begin
  1149. p:=p+'='; {Else HOST will also find HOSTNAME, etc}
  1150. ep:=envp;
  1151. found:=false;
  1152. if ep<>nil then
  1153. begin
  1154. while (not found) and (ep^<>nil) do
  1155. begin
  1156. found:=true;
  1157. for i:=1 to length(p) do
  1158. if p[i]<>ep^[i-1] then
  1159. begin
  1160. found:=false;
  1161. break;
  1162. end;
  1163. if not found then
  1164. inc(ep);
  1165. end;
  1166. end;
  1167. if found then
  1168. getenv:=ep^+length(p)
  1169. else
  1170. getenv:=nil;
  1171. end;
  1172. {$endif wince}
  1173. {$endif win32}
  1174. procedure LoadEnvironment;
  1175. var
  1176. i,j : ptrint;
  1177. s : string;
  1178. begin
  1179. s:=Getenv('HEAPTRC');
  1180. if pos('keepreleased',s)>0 then
  1181. keepreleased:=true;
  1182. if pos('disabled',s)>0 then
  1183. useheaptrace:=false;
  1184. if pos('nohalt',s)>0 then
  1185. haltonerror:=false;
  1186. if pos('haltonnotreleased',s)>0 then
  1187. HaltOnNotReleased :=true;
  1188. i:=pos('log=',s);
  1189. if i>0 then
  1190. begin
  1191. outputstr:=copy(s,i+4,255);
  1192. j:=pos(' ',outputstr);
  1193. if j=0 then
  1194. j:=length(outputstr)+1;
  1195. delete(outputstr,j,255);
  1196. end;
  1197. end;
  1198. Initialization
  1199. LoadEnvironment;
  1200. { heaptrc can be disabled from the environment }
  1201. if useheaptrace then
  1202. TraceInit;
  1203. finalization
  1204. if useheaptrace then
  1205. TraceExit;
  1206. end.