heaptrc.pp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1993-98 by the Free Pascal development team.
  5. Heap tracer
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit heaptrc;
  13. {$mode objfpc}
  14. interface
  15. Procedure DumpHeap;
  16. Procedure MarkHeap;
  17. { define EXTRA to add more
  18. tests :
  19. - keep all memory after release and
  20. check by CRC value if not changed after release
  21. WARNING this needs extremely much memory (PM) }
  22. type
  23. FillExtraInfoType = procedure(p : pointer);
  24. { allows to add several longint value that can help
  25. to debug :
  26. see for instance ppheap.pas unit of the compiler source PM }
  27. Procedure SetExtraInfo( size : longint;func : FillExtraInfoType);
  28. Procedure SetHeapTraceOutput(const name : string);
  29. const
  30. { tracing level
  31. splitted in two if memory is released !! }
  32. {$ifdef EXTRA}
  33. tracesize = 16;
  34. {$else EXTRA}
  35. tracesize = 8;
  36. {$endif EXTRA}
  37. quicktrace : boolean=true;
  38. { calls halt() on error by default !! }
  39. HaltOnError : boolean = true;
  40. { set this to true if you suspect that memory
  41. is freed several times }
  42. {$ifdef EXTRA}
  43. keepreleased : boolean=true;
  44. add_tail : boolean = true;
  45. {$else EXTRA}
  46. keepreleased : boolean=false;
  47. add_tail : boolean = false;
  48. {$endif EXTRA}
  49. { put crc in sig
  50. this allows to test for writing into that part }
  51. usecrc : boolean = true;
  52. implementation
  53. type
  54. plongint = ^longint;
  55. const
  56. { allows to add custom info in heap_mem_info }
  57. extra_info_size : longint = 0;
  58. exact_info_size : longint = 0;
  59. { function to fill this info up }
  60. fill_extra_info : FillExtraInfoType = nil;
  61. error_in_heap : boolean = false;
  62. type
  63. pheap_mem_info = ^theap_mem_info;
  64. { warning the size of theap_mem_info
  65. must be a multiple of 8
  66. because otherwise you will get
  67. problems when releasing the usual memory part !!
  68. sizeof(theap_mem_info = 16+tracesize*4 so
  69. tracesize must be even !! PM }
  70. theap_mem_info = record
  71. previous,
  72. next : pheap_mem_info;
  73. size : longint;
  74. sig : longint;
  75. {$ifdef EXTRA}
  76. release_sig : longint;
  77. next_valid : pheap_mem_info;
  78. {$endif EXTRA}
  79. calls : array [1..tracesize] of longint;
  80. extra_info : record
  81. end;
  82. end;
  83. var
  84. ptext : ^text;
  85. ownfile : text;
  86. {$ifdef EXTRA}
  87. error_file : text;
  88. heap_valid_first,
  89. heap_valid_last : pheap_mem_info;
  90. {$endif EXTRA}
  91. heap_mem_root : pheap_mem_info;
  92. getmem_cnt,
  93. freemem_cnt : longint;
  94. getmem_size,
  95. freemem_size : longint;
  96. getmem8_size,
  97. freemem8_size : longint;
  98. {*****************************************************************************
  99. Crc 32
  100. *****************************************************************************}
  101. var
  102. {$ifdef Delphi}
  103. Crc32Tbl : array[0..255] of longword;
  104. {$else Delphi}
  105. Crc32Tbl : array[0..255] of longint;
  106. {$endif Delphi}
  107. procedure MakeCRC32Tbl;
  108. var
  109. {$ifdef Delphi}
  110. crc : longword;
  111. {$else Delphi}
  112. crc : longint;
  113. {$endif Delphi}
  114. i,n : byte;
  115. begin
  116. for i:=0 to 255 do
  117. begin
  118. crc:=i;
  119. for n:=1 to 8 do
  120. if odd(crc) then
  121. crc:=(crc shr 1) xor $edb88320
  122. else
  123. crc:=crc shr 1;
  124. Crc32Tbl[i]:=crc;
  125. end;
  126. end;
  127. {$ifopt R+}
  128. {$define Range_check_on}
  129. {$endif opt R+}
  130. {$R- needed here }
  131. Function UpdateCrc32(InitCrc:longint;var InBuf;InLen:Longint):longint;
  132. var
  133. i : longint;
  134. p : pchar;
  135. begin
  136. p:=@InBuf;
  137. for i:=1 to InLen do
  138. begin
  139. InitCrc:=Crc32Tbl[byte(InitCrc) xor byte(p^)] xor (InitCrc shr 8);
  140. inc(longint(p));
  141. end;
  142. UpdateCrc32:=InitCrc;
  143. end;
  144. Function calculate_sig(p : pheap_mem_info) : longint;
  145. var
  146. crc : longint;
  147. pl : plongint;
  148. begin
  149. crc:=$ffffffff;
  150. crc:=UpdateCrc32(crc,p^.size,sizeof(longint));
  151. crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(longint));
  152. if extra_info_size>0 then
  153. crc:=UpdateCrc32(crc,p^.extra_info,exact_info_size);
  154. if add_tail then
  155. begin
  156. { Check also 4 bytes just after allocation !! }
  157. pl:=pointer(p)+extra_info_size+sizeof(theap_mem_info)+p^.size;
  158. crc:=UpdateCrc32(crc,pl^,sizeof(longint));
  159. end;
  160. calculate_sig:=crc;
  161. end;
  162. {$ifdef EXTRA}
  163. Function calculate_release_sig(p : pheap_mem_info) : longint;
  164. var
  165. crc : longint;
  166. pl : plongint;
  167. begin
  168. crc:=$ffffffff;
  169. crc:=UpdateCrc32(crc,p^.size,sizeof(longint));
  170. crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(longint));
  171. if extra_info_size>0 then
  172. crc:=UpdateCrc32(crc,p^.extra_info,exact_info_size);
  173. { Check the whole of the whole allocation }
  174. pl:=pointer(p)+extra_info_size+sizeof(theap_mem_info);
  175. crc:=UpdateCrc32(crc,pl^,p^.size);
  176. { Check also 4 bytes just after allocation !! }
  177. if add_tail then
  178. begin
  179. { Check also 4 bytes just after allocation !! }
  180. pl:=pointer(p)+extra_info_size+sizeof(theap_mem_info)+p^.size;
  181. crc:=UpdateCrc32(crc,pl^,sizeof(longint));
  182. end;
  183. calculate_release_sig:=crc;
  184. end;
  185. {$endif EXTRA}
  186. {$ifdef Range_check_on}
  187. {$R+}
  188. {$undef Range_check_on}
  189. {$endif Range_check_on}
  190. {*****************************************************************************
  191. Helpers
  192. *****************************************************************************}
  193. procedure call_stack(pp : pheap_mem_info;var ptext : text);
  194. var
  195. i : longint;
  196. begin
  197. writeln(ptext,'Call trace for block 0x',hexstr(longint(pp+sizeof(theap_mem_info)),8),' size ',pp^.size);
  198. for i:=1 to tracesize do
  199. if pp^.calls[i]<>0 then
  200. writeln(ptext,' 0x',hexstr(pp^.calls[i],8));
  201. for i:=0 to (exact_info_size div 4)-1 do
  202. writeln(ptext,'info ',i,'=',plongint(@pp^.extra_info+4*i)^);
  203. end;
  204. procedure call_free_stack(pp : pheap_mem_info;var ptext : text);
  205. var
  206. i : longint;
  207. begin
  208. writeln(ptext,'Call trace for block 0x',hexstr(longint(pp+sizeof(theap_mem_info)),8),' size ',pp^.size);
  209. for i:=1 to tracesize div 2 do
  210. if pp^.calls[i]<>0 then
  211. writeln(ptext,' 0x',hexstr(pp^.calls[i],8));
  212. writeln(ptext,' was released at ');
  213. for i:=(tracesize div 2)+1 to tracesize do
  214. if pp^.calls[i]<>0 then
  215. writeln(ptext,' 0x',hexstr(pp^.calls[i],8));
  216. for i:=0 to (exact_info_size div 4)-1 do
  217. writeln(ptext,'info ',i,'=',plongint(@pp^.extra_info+4*i)^);
  218. end;
  219. procedure dump_already_free(p : pheap_mem_info;var ptext : text);
  220. begin
  221. Writeln(ptext,'Marked memory at ',HexStr(longint(p+sizeof(theap_mem_info)),8),' released');
  222. call_free_stack(p,ptext);
  223. Writeln(ptext,'freed again at');
  224. dump_stack(ptext,get_caller_frame(get_frame));
  225. end;
  226. procedure dump_error(p : pheap_mem_info;var ptext : text);
  227. begin
  228. Writeln(ptext,'Marked memory at ',HexStr(longint(p+sizeof(theap_mem_info)),8),' invalid');
  229. Writeln(ptext,'Wrong signature $',hexstr(p^.sig,8)
  230. ,' instead of ',hexstr(calculate_sig(p),8));
  231. dump_stack(ptext,get_caller_frame(get_frame));
  232. end;
  233. {$ifdef EXTRA}
  234. procedure dump_change_after(p : pheap_mem_info;var ptext : text);
  235. var pp : pchar;
  236. i : longint;
  237. begin
  238. Writeln(ptext,'Marked memory at ',HexStr(longint(p+sizeof(theap_mem_info)),8),' invalid');
  239. Writeln(ptext,'Wrong release CRC $',hexstr(p^.release_sig,8)
  240. ,' instead of ',hexstr(calculate_release_sig(p),8));
  241. Writeln(ptext,'This memory was changed after call to freemem !');
  242. call_free_stack(p,ptext);
  243. pp:=pchar(p)+sizeof(theap_mem_info)+extra_info_size;
  244. for i:=0 to p^.size-1 do
  245. if byte(pp[i])<>$F0 then
  246. Writeln(ptext,'offset',i,':$',hexstr(i,8),'"',pp[i],'"');
  247. end;
  248. {$endif EXTRA}
  249. procedure dump_wrong_size(p : pheap_mem_info;size : longint;var ptext : text);
  250. var
  251. i : longint;
  252. begin
  253. Writeln(ptext,'Marked memory at ',HexStr(longint(p+sizeof(theap_mem_info)),8),' invalid');
  254. Writeln(ptext,'Wrong size : ',p^.size,' allocated ',size,' freed');
  255. dump_stack(ptext,get_caller_frame(get_frame));
  256. for i:=0 to (exact_info_size div 4)-1 do
  257. writeln(ptext,'info ',i,'=',plongint(@p^.extra_info+4*i)^);
  258. call_stack(p,ptext);
  259. end;
  260. function is_in_getmem_list (p : pointer) : boolean;
  261. var
  262. i : longint;
  263. pp : pheap_mem_info;
  264. begin
  265. is_in_getmem_list:=false;
  266. pp:=heap_mem_root;
  267. i:=0;
  268. while pp<>nil do
  269. begin
  270. if ((pp^.sig<>$DEADBEEF) or usecrc) and
  271. ((pp^.sig<>calculate_sig(pp)) or not usecrc) and
  272. (pp^.sig <> $AAAAAAAA) then
  273. begin
  274. writeln(ptext^,'error in linked list of heap_mem_info');
  275. RunError(204);
  276. end;
  277. if pp=p then
  278. is_in_getmem_list:=true;
  279. pp:=pp^.previous;
  280. inc(i);
  281. if i>getmem_cnt-freemem_cnt then
  282. writeln(ptext^,'error in linked list of heap_mem_info');
  283. end;
  284. end;
  285. {*****************************************************************************
  286. TraceGetMem
  287. *****************************************************************************}
  288. procedure TraceGetMem(var p:pointer;size:longint);
  289. var
  290. i,bp : longint;
  291. pl : plongint;
  292. begin
  293. inc(getmem_size,size);
  294. inc(getmem8_size,((size+7) div 8)*8);
  295. { Do the real GetMem, but alloc also for the info block }
  296. bp:=size+sizeof(theap_mem_info)+extra_info_size;
  297. if add_tail then
  298. bp:=bp+sizeof(longint);
  299. SysGetMem(p,bp);
  300. { Create the info block }
  301. pheap_mem_info(p)^.sig:=$DEADBEEF;
  302. pheap_mem_info(p)^.size:=size;
  303. if add_tail then
  304. begin
  305. pl:=pointer(p)+bp-sizeof(longint);
  306. pl^:=$DEADBEEF;
  307. end;
  308. bp:=get_caller_frame(get_frame);
  309. for i:=1 to tracesize do
  310. begin
  311. pheap_mem_info(p)^.calls[i]:=get_caller_addr(bp);
  312. bp:=get_caller_frame(bp);
  313. end;
  314. { insert in the linked list }
  315. if heap_mem_root<>nil then
  316. heap_mem_root^.next:=pheap_mem_info(p);
  317. pheap_mem_info(p)^.previous:=heap_mem_root;
  318. pheap_mem_info(p)^.next:=nil;
  319. {$ifdef EXTRA}
  320. pheap_mem_info(p)^.next_valid:=nil;
  321. if assigned(heap_valid_last) then
  322. heap_valid_last^.next_valid:=pheap_mem_info(p);
  323. heap_valid_last:=pheap_mem_info(p);
  324. if not assigned(heap_valid_first) then
  325. heap_valid_first:=pheap_mem_info(p);
  326. {$endif EXTRA}
  327. heap_mem_root:=p;
  328. if assigned(fill_extra_info) then
  329. fill_extra_info(@pheap_mem_info(p)^.extra_info);
  330. { update the pointer }
  331. if usecrc then
  332. pheap_mem_info(p)^.sig:=calculate_sig(pheap_mem_info(p));
  333. inc(p,sizeof(theap_mem_info)+extra_info_size);
  334. inc(getmem_cnt);
  335. end;
  336. {*****************************************************************************
  337. TraceFreeMem
  338. *****************************************************************************}
  339. procedure TraceFreeMem(var p:pointer;size:longint);
  340. var i,bp, ppsize : longint;
  341. pp : pheap_mem_info;
  342. begin
  343. inc(freemem_size,size);
  344. inc(freemem8_size,((size+7) div 8)*8);
  345. ppsize:= size + sizeof(theap_mem_info)+extra_info_size;
  346. if add_tail then
  347. ppsize:=ppsize+sizeof(longint);
  348. dec(p,sizeof(theap_mem_info)+extra_info_size);
  349. pp:=pheap_mem_info(p);
  350. if not quicktrace and not(is_in_getmem_list(p)) then
  351. RunError(204);
  352. if pp^.sig=$AAAAAAAA then
  353. begin
  354. error_in_heap:=true;
  355. dump_already_free(pp,ptext^);
  356. if haltonerror then halt(1);
  357. end
  358. else if ((pp^.sig<>$DEADBEEF) or usecrc) and
  359. ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
  360. begin
  361. error_in_heap:=true;
  362. dump_error(pp,ptext^);
  363. {$ifdef EXTRA}
  364. dump_error(pp,error_file);
  365. {$endif EXTRA}
  366. { don't release anything in this case !! }
  367. if haltonerror then halt(1);
  368. exit;
  369. end
  370. else if pp^.size<>size then
  371. begin
  372. error_in_heap:=true;
  373. dump_wrong_size(pp,size,ptext^);
  374. {$ifdef EXTRA}
  375. dump_wrong_size(pp,size,error_file);
  376. {$endif EXTRA}
  377. if haltonerror then halt(1);
  378. { don't release anything in this case !! }
  379. exit;
  380. end;
  381. { now it is released !! }
  382. pp^.sig:=$AAAAAAAA;
  383. if not keepreleased then
  384. begin
  385. if pp^.next<>nil then
  386. pp^.next^.previous:=pp^.previous;
  387. if pp^.previous<>nil then
  388. pp^.previous^.next:=pp^.next;
  389. if pp=heap_mem_root then
  390. heap_mem_root:=heap_mem_root^.previous;
  391. end
  392. else
  393. begin
  394. bp:=get_caller_frame(get_frame);
  395. for i:=(tracesize div 2)+1 to tracesize do
  396. begin
  397. pp^.calls[i]:=get_caller_addr(bp);
  398. bp:=get_caller_frame(bp);
  399. end;
  400. end;
  401. inc(freemem_cnt);
  402. { release the normal memory at least !! }
  403. { this way we keep all info about all released memory !! }
  404. if keepreleased then
  405. begin
  406. {$ifndef EXTRA}
  407. dec(ppsize,sizeof(theap_mem_info)+extra_info_size);
  408. inc(p,sizeof(theap_mem_info)+extra_info_size);
  409. {$else EXTRA}
  410. inc(p,sizeof(theap_mem_info)+extra_info_size);
  411. fillchar(p^,size,#240); { $F0 will lead to GFP if used as pointer ! }
  412. { We want to check if the memory was changed after release !! }
  413. pp^.release_sig:=calculate_release_sig(pp);
  414. if pp=heap_valid_first then
  415. begin
  416. heap_valid_first:=pp^.next_valid;
  417. if pp=heap_valid_last then
  418. heap_valid_last:=nil;
  419. exit;
  420. end;
  421. pp2:=heap_valid_first;
  422. while assigned(pp2) do
  423. begin
  424. if pp2^.next_valid=pp then
  425. begin
  426. pp2^.next_valid:=pp^.next_valid;
  427. if pp=heap_valid_last then
  428. heap_valid_last:=pp2;
  429. exit;
  430. end
  431. else
  432. pp2:=pp2^.next_valid;
  433. end;
  434. exit;
  435. {$endif EXTRA}
  436. end;
  437. SysFreeMem(p,ppsize);
  438. end;
  439. {*****************************************************************************
  440. Check pointer
  441. *****************************************************************************}
  442. {$ifdef go32v2}
  443. var
  444. __stklen : cardinal;external name '__stklen';
  445. __stkbottom : cardinal;external name '__stkbottom';
  446. edata : cardinal; external name 'edata';
  447. {$endif go32v2}
  448. var
  449. heap_at_init : pointer;
  450. procedure CheckPointer(p : pointer);[public, alias : 'FPC_CHECKPOINTER'];
  451. var
  452. i : longint;
  453. pp : pheap_mem_info;
  454. get_ebp,stack_top : cardinal;
  455. data_end : cardinal;
  456. label
  457. _exit;
  458. begin
  459. asm
  460. pushal
  461. end;
  462. if p=nil then
  463. goto _exit;
  464. i:=0;
  465. {$ifdef go32v2}
  466. if cardinal(p)<$1000 then
  467. runerror(216);
  468. asm
  469. movl %ebp,get_ebp
  470. leal edata,%eax
  471. movl %eax,data_end
  472. end;
  473. stack_top:=__stkbottom+__stklen;
  474. { allow all between start of code and end of data }
  475. if cardinal(p)<=data_end then
  476. goto _exit;
  477. { .bss section }
  478. if cardinal(p)<=cardinal(heap_at_init) then
  479. goto _exit;
  480. { stack can be above heap !! }
  481. if (cardinal(p)>=get_ebp) and (cardinal(p)<=stack_top) then
  482. goto _exit;
  483. {$endif go32v2}
  484. { I don't know where the stack is in other OS !! }
  485. if p>=heapptr then
  486. runerror(216);
  487. { first try valid list faster }
  488. {$ifdef EXTRA}
  489. pp:=heap_valid_first;
  490. while pp<>nil do
  491. begin
  492. { inside this valid block ! }
  493. if (cardinal(p)>=cardinal(pp)+sizeof(theap_mem_info)+extra_info_size) and
  494. (cardinal(p)<=cardinal(pp)+sizeof(theap_mem_info)+extra_info_size+pp^.size) then
  495. begin
  496. { check allocated block }
  497. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  498. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  499. goto _exit;
  500. end
  501. else
  502. pp:=pp^.next_valid;
  503. inc(i);
  504. if i>getmem_cnt-freemem_cnt then
  505. begin
  506. writeln(ptext^,'error in linked list of heap_mem_info');
  507. halt(1);
  508. end;
  509. end;
  510. i:=0;
  511. {$endif EXTRA}
  512. pp:=heap_mem_root;
  513. while pp<>nil do
  514. begin
  515. { inside this block ! }
  516. if (cardinal(p)>=cardinal(pp)+sizeof(theap_mem_info)+extra_info_size) and
  517. (cardinal(p)<=cardinal(pp)+sizeof(theap_mem_info)+extra_info_size+pp^.size) then
  518. { allocated block }
  519. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  520. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  521. goto _exit
  522. else
  523. begin
  524. writeln(ptext^,'pointer $',hexstr(longint(p),8),' points into invalid memory block');
  525. dump_error(pp,ptext^);
  526. runerror(204);
  527. end;
  528. pp:=pp^.previous;
  529. inc(i);
  530. if i>getmem_cnt then
  531. begin
  532. writeln(ptext^,'error in linked list of heap_mem_info');
  533. halt(1);
  534. end;
  535. end;
  536. writeln(ptext^,'pointer $',hexstr(longint(p),8),' does not point to valid memory block');
  537. runerror(204);
  538. _exit:
  539. asm
  540. popal
  541. end;
  542. end;
  543. {*****************************************************************************
  544. Dump Heap
  545. *****************************************************************************}
  546. procedure dumpheap;
  547. var
  548. pp : pheap_mem_info;
  549. i : longint;
  550. begin
  551. pp:=heap_mem_root;
  552. Writeln(ptext^,'Heap dump by heaptrc unit');
  553. Writeln(ptext^,getmem_cnt, ' memory blocks allocated : ',getmem_size,'/',getmem8_size);
  554. Writeln(ptext^,freemem_cnt,' memory blocks freed : ',freemem_size,'/',freemem8_size);
  555. Writeln(ptext^,getmem_cnt-freemem_cnt,' unfreed memory blocks : ',getmem_size-freemem_size);
  556. Writeln(ptext^,'True heap size : ',system.HeapSize);
  557. Writeln(ptext^,'True free heap : ',MemAvail);
  558. Writeln(ptext^,'Should be : ',system.HeapSize-(getmem8_size-freemem8_size)-
  559. (getmem_cnt-freemem_cnt)*(sizeof(theap_mem_info)+extra_info_size));
  560. i:=getmem_cnt-freemem_cnt;
  561. while pp<>nil do
  562. begin
  563. if i<0 then
  564. begin
  565. Writeln(ptext^,'Error in heap memory list');
  566. Writeln(ptext^,'More memory blocks than expected');
  567. exit;
  568. end;
  569. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  570. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  571. begin
  572. { this one was not released !! }
  573. if exitcode<>203 then
  574. call_stack(pp,ptext^);
  575. dec(i);
  576. end
  577. else if pp^.sig<>$AAAAAAAA then
  578. begin
  579. dump_error(pp,ptext^);
  580. {$ifdef EXTRA}
  581. dump_error(pp,error_file);
  582. {$endif EXTRA}
  583. error_in_heap:=true;
  584. end
  585. {$ifdef EXTRA}
  586. else if pp^.release_sig<>calculate_release_sig(pp) then
  587. begin
  588. dump_change_after(pp,ptext^);
  589. dump_change_after(pp,error_file);
  590. error_in_heap:=true;
  591. end
  592. {$endif EXTRA}
  593. ;
  594. pp:=pp^.previous;
  595. end;
  596. end;
  597. procedure markheap;
  598. var
  599. pp : pheap_mem_info;
  600. begin
  601. pp:=heap_mem_root;
  602. while pp<>nil do
  603. begin
  604. pp^.sig:=$AAAAAAAA;
  605. pp:=pp^.previous;
  606. end;
  607. end;
  608. {*****************************************************************************
  609. Install MemoryManager
  610. *****************************************************************************}
  611. const
  612. TraceManager:TMemoryManager=(
  613. Getmem : TraceGetMem;
  614. Freemem : TraceFreeMem
  615. );
  616. procedure TraceExit;
  617. begin
  618. { no dump if error
  619. because this gives long long listings }
  620. if (exitcode<>0) and (erroraddr<>nil) then
  621. begin
  622. Writeln(ptext^,'No heap dump by heaptrc unit');
  623. Writeln(ptext^,'Exitcode = ',exitcode);
  624. if ptext<>@stderr then
  625. begin
  626. ptext:=@stderr;
  627. close(ownfile);
  628. end;
  629. exit;
  630. end;
  631. if not error_in_heap then
  632. Dumpheap;
  633. if error_in_heap and (exitcode=0) then
  634. exitcode:=203;
  635. {$ifdef EXTRA}
  636. Close(error_file);
  637. {$endif EXTRA}
  638. if ptext<>@stderr then
  639. begin
  640. ptext:=@stderr;
  641. close(ownfile);
  642. end;
  643. end;
  644. Procedure SetHeapTraceOutput(const name : string);
  645. begin
  646. if ptext<>@stderr then
  647. begin
  648. ptext:=@stderr;
  649. close(ownfile);
  650. end;
  651. assign(ownfile,name);
  652. {$I-}
  653. append(ownfile);
  654. if IOResult<>0 then
  655. Rewrite(ownfile);
  656. {$I+}
  657. ptext:=@ownfile;
  658. end;
  659. procedure SetExtraInfo( size : longint;func : fillextrainfotype);
  660. begin
  661. if getmem_cnt>0 then
  662. begin
  663. writeln(ptext^,'Setting extra info is only possible at start !! ');
  664. dumpheap;
  665. end
  666. else
  667. begin
  668. { the total size must stay multiple of 8 !! }
  669. exact_info_size:=size;
  670. extra_info_size:=((size+7) div 8)*8;
  671. fill_extra_info:=func;
  672. end;
  673. end;
  674. Initialization
  675. MakeCRC32Tbl;
  676. SetMemoryManager(TraceManager);
  677. ptext:=@stderr;
  678. {$ifdef EXTRA}
  679. Assign(error_file,'heap.err');
  680. Rewrite(error_file);
  681. {$endif EXTRA}
  682. Heap_at_init:=HeapPtr;
  683. finalization
  684. TraceExit;
  685. end.
  686. {
  687. $Log$
  688. Revision 1.16.2.3 1999-07-10 10:31:56 peter
  689. * removed unused var
  690. Revision 1.16.2.2 1999/07/09 10:44:23 michael
  691. + Merged finalize
  692. Revision 1.18 1999/07/09 10:38:10 michael
  693. + + heaptrc now uses finalize instead of exitproc
  694. Revision 1.17 1999/07/05 20:22:08 peter
  695. * merged
  696. Revision 1.16.2.1 1999/07/05 20:12:27 peter
  697. * removed warning
  698. Revision 1.16 1999/05/23 00:07:17 pierre
  699. * support for heap allocated before TraceGetMem is used in
  700. FPC_CHECKPOINTER
  701. * faster CHECKPOINTER routine (list of valid blocks only !)
  702. Revision 1.15 1999/05/18 22:15:55 pierre
  703. * allow for .bss section below heaporg in go32v2 code
  704. Revision 1.14 1999/05/16 23:56:09 pierre
  705. * allow nil pointer in FPC_CHECKPOINTER !!
  706. Revision 1.13 1999/05/12 16:49:29 pierre
  707. + with EXTRA memory is filled with $F0 and checked at end
  708. Revision 1.12 1999/05/11 12:52:42 pierre
  709. + extra's with -dEXTRA, uses a CRC check for released memory
  710. Revision 1.11 1999/03/26 19:10:34 peter
  711. * show also allocation stack for a wrong size
  712. Revision 1.10 1999/02/16 17:20:26 pierre
  713. * no heap dump if program has an heap error !
  714. Revision 1.9 1999/01/22 12:39:22 pierre
  715. + added text arg for dump_stack
  716. Revision 1.8 1998/12/15 23:49:51 michael
  717. + Removed underscores in heaptrc unit
  718. Revision 1.7 1998/11/16 12:20:13 peter
  719. * write extra info also for wrong size
  720. Revision 1.6 1998/11/06 08:46:01 pierre
  721. * size is now also checked
  722. + added halt_on_error variable (default true)
  723. to stop at first error in getmem/freemem
  724. Revision 1.5 1998/10/09 11:59:31 pierre
  725. * changed default to keepreleased=false
  726. (allows to compile pp in one call without reaching the
  727. 64Mb limit of Windows 95 dos box)
  728. * corrected so typo errors
  729. Revision 1.4 1998/10/08 14:49:05 pierre
  730. + added possibility for more info
  731. Revision 1.3 1998/10/06 17:09:13 pierre
  732. + added trace of first dispose for errors
  733. Revision 1.2 1998/10/02 10:35:38 peter
  734. + quicktrace
  735. Revision 1.1 1998/10/01 14:54:20 peter
  736. + first version
  737. }