heaptrc.pp 23 KB

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