heaptrc.pp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965
  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. Function TraceGetMem(size:longint):pointer;
  293. var
  294. i,bp : longint;
  295. pl : plongint;
  296. p : pointer;
  297. begin
  298. inc(getmem_size,size);
  299. inc(getmem8_size,((size+7) div 8)*8);
  300. { Do the real GetMem, but alloc also for the info block }
  301. bp:=size+sizeof(theap_mem_info)+extra_info_size;
  302. if add_tail then
  303. inc(bp,sizeof(longint));
  304. p:=SysGetMem(bp);
  305. { Create the info block }
  306. pheap_mem_info(p)^.sig:=$DEADBEEF;
  307. pheap_mem_info(p)^.size:=size;
  308. if add_tail then
  309. begin
  310. pl:=pointer(p)+bp-sizeof(longint);
  311. pl^:=$DEADBEEF;
  312. end;
  313. bp:=get_caller_frame(get_frame);
  314. for i:=1 to tracesize do
  315. begin
  316. pheap_mem_info(p)^.calls[i]:=get_caller_addr(bp);
  317. bp:=get_caller_frame(bp);
  318. end;
  319. { insert in the linked list }
  320. if heap_mem_root<>nil then
  321. heap_mem_root^.next:=pheap_mem_info(p);
  322. pheap_mem_info(p)^.previous:=heap_mem_root;
  323. pheap_mem_info(p)^.next:=nil;
  324. {$ifdef EXTRA}
  325. pheap_mem_info(p)^.next_valid:=nil;
  326. if assigned(heap_valid_last) then
  327. heap_valid_last^.next_valid:=pheap_mem_info(p);
  328. heap_valid_last:=pheap_mem_info(p);
  329. if not assigned(heap_valid_first) then
  330. heap_valid_first:=pheap_mem_info(p);
  331. {$endif EXTRA}
  332. heap_mem_root:=p;
  333. if assigned(fill_extra_info) then
  334. fill_extra_info(@pheap_mem_info(p)^.extra_info);
  335. { update the pointer }
  336. if usecrc then
  337. pheap_mem_info(p)^.sig:=calculate_sig(pheap_mem_info(p));
  338. inc(p,sizeof(theap_mem_info)+extra_info_size);
  339. inc(getmem_cnt);
  340. TraceGetmem:=p;
  341. end;
  342. {*****************************************************************************
  343. TraceFreeMem
  344. *****************************************************************************}
  345. function TraceFreeMemSize(var p:pointer;size:longint):longint;
  346. var
  347. i,bp, ppsize : longint;
  348. pp : pheap_mem_info;
  349. {$ifdef EXTRA}
  350. pp2 : pheap_mem_info;
  351. {$endif}
  352. begin
  353. inc(freemem_size,size);
  354. inc(freemem8_size,((size+7) div 8)*8);
  355. ppsize:= size + sizeof(theap_mem_info)+extra_info_size;
  356. if add_tail then
  357. ppsize:=ppsize+sizeof(longint);
  358. dec(p,sizeof(theap_mem_info)+extra_info_size);
  359. pp:=pheap_mem_info(p);
  360. if not quicktrace and not(is_in_getmem_list(p)) then
  361. RunError(204);
  362. if pp^.sig=$AAAAAAAA then
  363. begin
  364. error_in_heap:=true;
  365. dump_already_free(pp,ptext^);
  366. if haltonerror then halt(1);
  367. end
  368. else if ((pp^.sig<>$DEADBEEF) or usecrc) and
  369. ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
  370. begin
  371. error_in_heap:=true;
  372. dump_error(pp,ptext^);
  373. {$ifdef EXTRA}
  374. dump_error(pp,error_file);
  375. {$endif EXTRA}
  376. { don't release anything in this case !! }
  377. if haltonerror then halt(1);
  378. exit;
  379. end
  380. else if pp^.size<>size then
  381. begin
  382. error_in_heap:=true;
  383. dump_wrong_size(pp,size,ptext^);
  384. {$ifdef EXTRA}
  385. dump_wrong_size(pp,size,error_file);
  386. {$endif EXTRA}
  387. if haltonerror then halt(1);
  388. { don't release anything in this case !! }
  389. exit;
  390. end;
  391. { now it is released !! }
  392. pp^.sig:=$AAAAAAAA;
  393. if not keepreleased then
  394. begin
  395. if pp^.next<>nil then
  396. pp^.next^.previous:=pp^.previous;
  397. if pp^.previous<>nil then
  398. pp^.previous^.next:=pp^.next;
  399. if pp=heap_mem_root then
  400. heap_mem_root:=heap_mem_root^.previous;
  401. end
  402. else
  403. begin
  404. bp:=get_caller_frame(get_frame);
  405. for i:=(tracesize div 2)+1 to tracesize do
  406. begin
  407. pp^.calls[i]:=get_caller_addr(bp);
  408. bp:=get_caller_frame(bp);
  409. end;
  410. end;
  411. inc(freemem_cnt);
  412. { release the normal memory at least !! }
  413. { this way we keep all info about all released memory !! }
  414. if keepreleased then
  415. begin
  416. {$ifndef EXTRA}
  417. dec(ppsize,sizeof(theap_mem_info)+extra_info_size);
  418. inc(p,sizeof(theap_mem_info)+extra_info_size);
  419. {$else EXTRA}
  420. inc(p,sizeof(theap_mem_info)+extra_info_size);
  421. fillchar(p^,size,#240); { $F0 will lead to GFP if used as pointer ! }
  422. { We want to check if the memory was changed after release !! }
  423. pp^.release_sig:=calculate_release_sig(pp);
  424. if pp=heap_valid_first then
  425. begin
  426. heap_valid_first:=pp^.next_valid;
  427. if pp=heap_valid_last then
  428. heap_valid_last:=nil;
  429. exit;
  430. end;
  431. pp2:=heap_valid_first;
  432. while assigned(pp2) do
  433. begin
  434. if pp2^.next_valid=pp then
  435. begin
  436. pp2^.next_valid:=pp^.next_valid;
  437. if pp=heap_valid_last then
  438. heap_valid_last:=pp2;
  439. exit;
  440. end
  441. else
  442. pp2:=pp2^.next_valid;
  443. end;
  444. exit;
  445. {$endif EXTRA}
  446. end;
  447. i:=SysFreeMemSize(p,ppsize);
  448. dec(i,sizeof(theap_mem_info)+extra_info_size);
  449. if add_tail then
  450. dec(i,sizeof(longint));
  451. TraceFreeMemSize:=i;
  452. end;
  453. function TraceMemSize(p:pointer):Longint;
  454. var
  455. l : longint;
  456. begin
  457. l:=SysMemSize(p-sizeof(theap_mem_info)+extra_info_size);
  458. dec(l,sizeof(theap_mem_info)+extra_info_size);
  459. if add_tail then
  460. dec(l,sizeof(longint));
  461. TraceMemSize:=l;
  462. end;
  463. function TraceFreeMem(var p:pointer):longint;
  464. var
  465. size : longint;
  466. pp : pheap_mem_info;
  467. begin
  468. pp:=pheap_mem_info(pointer(p)-sizeof(theap_mem_info)+extra_info_size);
  469. size:=TraceMemSize(p);
  470. { this can never happend normaly }
  471. if pp^.size>size then
  472. begin
  473. dump_wrong_size(pp,size,ptext^);
  474. {$ifdef EXTRA}
  475. dump_wrong_size(pp,size,error_file);
  476. {$endif EXTRA}
  477. end;
  478. TraceFreeMem:=TraceFreeMemSize(p,pp^.size);
  479. end;
  480. {*****************************************************************************
  481. Check pointer
  482. *****************************************************************************}
  483. {$ifdef go32v2}
  484. var
  485. __stklen : cardinal;external name '__stklen';
  486. __stkbottom : cardinal;external name '__stkbottom';
  487. edata : cardinal; external name 'edata';
  488. {$endif go32v2}
  489. var
  490. heap_at_init : pointer;
  491. procedure CheckPointer(p : pointer);[public, alias : 'FPC_CHECKPOINTER'];
  492. var
  493. i : longint;
  494. pp : pheap_mem_info;
  495. get_ebp,stack_top : cardinal;
  496. data_end : cardinal;
  497. label
  498. _exit;
  499. begin
  500. asm
  501. pushal
  502. end;
  503. if p=nil then
  504. goto _exit;
  505. i:=0;
  506. {$ifdef go32v2}
  507. if cardinal(p)<$1000 then
  508. runerror(216);
  509. asm
  510. movl %ebp,get_ebp
  511. leal edata,%eax
  512. movl %eax,data_end
  513. end;
  514. stack_top:=__stkbottom+__stklen;
  515. { allow all between start of code and end of data }
  516. if cardinal(p)<=data_end then
  517. goto _exit;
  518. { .bss section }
  519. if cardinal(p)<=cardinal(heap_at_init) then
  520. goto _exit;
  521. { stack can be above heap !! }
  522. if (cardinal(p)>=get_ebp) and (cardinal(p)<=stack_top) then
  523. goto _exit;
  524. {$endif go32v2}
  525. { I don't know where the stack is in other OS !! }
  526. if p>=heapptr then
  527. runerror(216);
  528. { first try valid list faster }
  529. {$ifdef EXTRA}
  530. pp:=heap_valid_first;
  531. while pp<>nil do
  532. begin
  533. { inside this valid block ! }
  534. if (cardinal(p)>=cardinal(pp)+sizeof(theap_mem_info)+extra_info_size) and
  535. (cardinal(p)<=cardinal(pp)+sizeof(theap_mem_info)+extra_info_size+pp^.size) then
  536. begin
  537. { check allocated block }
  538. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  539. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  540. goto _exit;
  541. end
  542. else
  543. pp:=pp^.next_valid;
  544. inc(i);
  545. if i>getmem_cnt-freemem_cnt then
  546. begin
  547. writeln(ptext^,'error in linked list of heap_mem_info');
  548. halt(1);
  549. end;
  550. end;
  551. i:=0;
  552. {$endif EXTRA}
  553. pp:=heap_mem_root;
  554. while pp<>nil do
  555. begin
  556. { inside this block ! }
  557. if (cardinal(p)>=cardinal(pp)+sizeof(theap_mem_info)+extra_info_size) and
  558. (cardinal(p)<=cardinal(pp)+sizeof(theap_mem_info)+extra_info_size+pp^.size) then
  559. { allocated block }
  560. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  561. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  562. goto _exit
  563. else
  564. begin
  565. writeln(ptext^,'pointer $',hexstr(longint(p),8),' points into invalid memory block');
  566. dump_error(pp,ptext^);
  567. runerror(204);
  568. end;
  569. pp:=pp^.previous;
  570. inc(i);
  571. if i>getmem_cnt then
  572. begin
  573. writeln(ptext^,'error in linked list of heap_mem_info');
  574. halt(1);
  575. end;
  576. end;
  577. writeln(ptext^,'pointer $',hexstr(longint(p),8),' does not point to valid memory block');
  578. runerror(204);
  579. _exit:
  580. asm
  581. popal
  582. end;
  583. end;
  584. {*****************************************************************************
  585. Dump Heap
  586. *****************************************************************************}
  587. procedure dumpheap;
  588. var
  589. pp : pheap_mem_info;
  590. i : longint;
  591. begin
  592. pp:=heap_mem_root;
  593. Writeln(ptext^,'Heap dump by heaptrc unit');
  594. Writeln(ptext^,getmem_cnt, ' memory blocks allocated : ',getmem_size,'/',getmem8_size);
  595. Writeln(ptext^,freemem_cnt,' memory blocks freed : ',freemem_size,'/',freemem8_size);
  596. Writeln(ptext^,getmem_cnt-freemem_cnt,' unfreed memory blocks : ',getmem_size-freemem_size);
  597. Writeln(ptext^,'True heap size : ',system.HeapSize);
  598. Writeln(ptext^,'True free heap : ',MemAvail);
  599. Writeln(ptext^,'Should be : ',system.HeapSize-(getmem8_size-freemem8_size)-
  600. (getmem_cnt-freemem_cnt)*(sizeof(theap_mem_info)+extra_info_size));
  601. i:=getmem_cnt-freemem_cnt;
  602. while pp<>nil do
  603. begin
  604. if i<0 then
  605. begin
  606. Writeln(ptext^,'Error in heap memory list');
  607. Writeln(ptext^,'More memory blocks than expected');
  608. exit;
  609. end;
  610. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  611. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  612. begin
  613. { this one was not released !! }
  614. if exitcode<>203 then
  615. call_stack(pp,ptext^);
  616. dec(i);
  617. end
  618. else if pp^.sig<>$AAAAAAAA then
  619. begin
  620. dump_error(pp,ptext^);
  621. {$ifdef EXTRA}
  622. dump_error(pp,error_file);
  623. {$endif EXTRA}
  624. error_in_heap:=true;
  625. end
  626. {$ifdef EXTRA}
  627. else if pp^.release_sig<>calculate_release_sig(pp) then
  628. begin
  629. dump_change_after(pp,ptext^);
  630. dump_change_after(pp,error_file);
  631. error_in_heap:=true;
  632. end
  633. {$endif EXTRA}
  634. ;
  635. pp:=pp^.previous;
  636. end;
  637. end;
  638. procedure markheap;
  639. var
  640. pp : pheap_mem_info;
  641. begin
  642. pp:=heap_mem_root;
  643. while pp<>nil do
  644. begin
  645. pp^.sig:=$AAAAAAAA;
  646. pp:=pp^.previous;
  647. end;
  648. end;
  649. {*****************************************************************************
  650. AllocMem
  651. *****************************************************************************}
  652. function TraceAllocMem(size:longint):Pointer;
  653. begin
  654. TraceAllocMem:=SysAllocMem(size);
  655. end;
  656. {*****************************************************************************
  657. ReAllocMem
  658. *****************************************************************************}
  659. function TraceReAllocMem(var p:pointer;size:longint):Pointer;
  660. var
  661. i,bp : longint;
  662. pl : plongint;
  663. pp : pheap_mem_info;
  664. begin
  665. dec(p,sizeof(theap_mem_info)+extra_info_size);
  666. { remove heap_mem_info for linked list }
  667. pp:=pheap_mem_info(p);
  668. if pp^.next<>nil then
  669. pp^.next^.previous:=pp^.previous;
  670. if pp^.previous<>nil then
  671. pp^.previous^.next:=pp^.next;
  672. if pp=heap_mem_root then
  673. heap_mem_root:=heap_mem_root^.previous;
  674. { Do the real GetMem, but alloc also for the info block }
  675. bp:=size+sizeof(theap_mem_info)+extra_info_size;
  676. if add_tail then
  677. inc(bp,sizeof(longint));
  678. p:=SysReAllocMem(p,bp);
  679. { Create the info block }
  680. pheap_mem_info(p)^.sig:=$DEADBEEF;
  681. pheap_mem_info(p)^.size:=size;
  682. if add_tail then
  683. begin
  684. pl:=pointer(p)+bp-sizeof(longint);
  685. pl^:=$DEADBEEF;
  686. end;
  687. bp:=get_caller_frame(get_frame);
  688. for i:=1 to tracesize do
  689. begin
  690. pheap_mem_info(p)^.calls[i]:=get_caller_addr(bp);
  691. bp:=get_caller_frame(bp);
  692. end;
  693. { insert in the linked list }
  694. if heap_mem_root<>nil then
  695. heap_mem_root^.next:=pheap_mem_info(p);
  696. pheap_mem_info(p)^.previous:=heap_mem_root;
  697. pheap_mem_info(p)^.next:=nil;
  698. {$ifdef EXTRA}
  699. pheap_mem_info(p)^.next_valid:=nil;
  700. if assigned(heap_valid_last) then
  701. heap_valid_last^.next_valid:=pheap_mem_info(p);
  702. heap_valid_last:=pheap_mem_info(p);
  703. if not assigned(heap_valid_first) then
  704. heap_valid_first:=pheap_mem_info(p);
  705. {$endif EXTRA}
  706. heap_mem_root:=p;
  707. if assigned(fill_extra_info) then
  708. fill_extra_info(@pheap_mem_info(p)^.extra_info);
  709. { update the pointer }
  710. if usecrc then
  711. pheap_mem_info(p)^.sig:=calculate_sig(pheap_mem_info(p));
  712. inc(p,sizeof(theap_mem_info)+extra_info_size);
  713. inc(getmem_cnt);
  714. TraceReAllocmem:=p;
  715. end;
  716. {*****************************************************************************
  717. No specific tracing calls
  718. *****************************************************************************}
  719. function TraceMemAvail:longint;
  720. begin
  721. TraceMemAvail:=SysMemAvail;
  722. end;
  723. function TraceMaxAvail:longint;
  724. begin
  725. TraceMaxAvail:=SysMaxAvail;
  726. end;
  727. function TraceHeapSize:longint;
  728. begin
  729. TraceHeapSize:=SysHeapSize;
  730. end;
  731. {*****************************************************************************
  732. Install MemoryManager
  733. *****************************************************************************}
  734. const
  735. TraceManager:TMemoryManager=(
  736. Getmem : TraceGetMem;
  737. Freemem : TraceFreeMem;
  738. FreememSize : TraceFreeMemSize;
  739. AllocMem : TraceAllocMem;
  740. ReAllocMem : TraceReAllocMem;
  741. MemSize : TraceMemSize;
  742. MemAvail : TraceMemAvail;
  743. MaxAvail : TraceMaxAvail;
  744. HeapSize : TraceHeapsize;
  745. );
  746. procedure TraceExit;
  747. begin
  748. { no dump if error
  749. because this gives long long listings }
  750. if (exitcode<>0) and (erroraddr<>nil) then
  751. begin
  752. Writeln(ptext^,'No heap dump by heaptrc unit');
  753. Writeln(ptext^,'Exitcode = ',exitcode);
  754. if ptext<>@stderr then
  755. begin
  756. ptext:=@stderr;
  757. close(ownfile);
  758. end;
  759. exit;
  760. end;
  761. if not error_in_heap then
  762. Dumpheap;
  763. if error_in_heap and (exitcode=0) then
  764. exitcode:=203;
  765. {$ifdef EXTRA}
  766. Close(error_file);
  767. {$endif EXTRA}
  768. if ptext<>@stderr then
  769. begin
  770. ptext:=@stderr;
  771. close(ownfile);
  772. end;
  773. end;
  774. Procedure SetHeapTraceOutput(const name : string);
  775. begin
  776. if ptext<>@stderr then
  777. begin
  778. ptext:=@stderr;
  779. close(ownfile);
  780. end;
  781. assign(ownfile,name);
  782. {$I-}
  783. append(ownfile);
  784. if IOResult<>0 then
  785. Rewrite(ownfile);
  786. {$I+}
  787. ptext:=@ownfile;
  788. end;
  789. procedure SetExtraInfo( size : longint;func : fillextrainfotype);
  790. begin
  791. if getmem_cnt>0 then
  792. begin
  793. writeln(ptext^,'Setting extra info is only possible at start !! ');
  794. dumpheap;
  795. end
  796. else
  797. begin
  798. { the total size must stay multiple of 8 !! }
  799. exact_info_size:=size;
  800. extra_info_size:=((size+7) div 8)*8;
  801. fill_extra_info:=func;
  802. end;
  803. end;
  804. Initialization
  805. MakeCRC32Tbl;
  806. SetMemoryManager(TraceManager);
  807. ptext:=@stderr;
  808. {$ifdef EXTRA}
  809. Assign(error_file,'heap.err');
  810. Rewrite(error_file);
  811. {$endif EXTRA}
  812. Heap_at_init:=HeapPtr;
  813. finalization
  814. TraceExit;
  815. end.
  816. {
  817. $Log$
  818. Revision 1.27 1999-11-06 14:35:38 peter
  819. * truncated log
  820. Revision 1.26 1999/11/01 13:56:50 peter
  821. * freemem,reallocmem now get var argument
  822. Revision 1.25 1999/10/30 17:39:05 peter
  823. * memorymanager expanded with allocmem/reallocmem
  824. Revision 1.24 1999/09/17 17:14:12 peter
  825. + new heap manager supporting delphi freemem(pointer)
  826. Revision 1.23 1999/09/10 17:13:41 peter
  827. * fixed missing var
  828. Revision 1.22 1999/09/08 16:14:41 peter
  829. * pointer fixes
  830. Revision 1.21 1999/08/18 12:03:16 peter
  831. * objfpc mode for 0.99.12
  832. Revision 1.20 1999/08/17 14:56:03 michael
  833. Removed the mode for objpas
  834. Revision 1.19 1999/07/10 10:33:50 peter
  835. * merged
  836. Revision 1.18 1999/07/09 10:38:10 michael
  837. + + heaptrc now uses finalize instead of exitproc
  838. Revision 1.17 1999/07/05 20:22:08 peter
  839. * merged
  840. Revision 1.16.2.3 1999/07/10 10:31:56 peter
  841. * removed unused var
  842. Revision 1.16.2.2 1999/07/09 10:44:23 michael
  843. + Merged finalize
  844. Revision 1.16 1999/05/23 00:07:17 pierre
  845. * support for heap allocated before TraceGetMem is used in
  846. FPC_CHECKPOINTER
  847. * faster CHECKPOINTER routine (list of valid blocks only !)
  848. Revision 1.15 1999/05/18 22:15:55 pierre
  849. * allow for .bss section below heaporg in go32v2 code
  850. Revision 1.14 1999/05/16 23:56:09 pierre
  851. * allow nil pointer in FPC_CHECKPOINTER !!
  852. Revision 1.13 1999/05/12 16:49:29 pierre
  853. + with EXTRA memory is filled with $F0 and checked at end
  854. Revision 1.12 1999/05/11 12:52:42 pierre
  855. + extra's with -dEXTRA, uses a CRC check for released memory
  856. Revision 1.11 1999/03/26 19:10:34 peter
  857. * show also allocation stack for a wrong size
  858. Revision 1.10 1999/02/16 17:20:26 pierre
  859. * no heap dump if program has an heap error !
  860. Revision 1.9 1999/01/22 12:39:22 pierre
  861. + added text arg for dump_stack
  862. }