heaptrc.pp 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2000 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. inside_trace_getmem : boolean = false;
  67. type
  68. pheap_mem_info = ^theap_mem_info;
  69. { warning the size of theap_mem_info
  70. must be a multiple of 8
  71. because otherwise you will get
  72. problems when releasing the usual memory part !!
  73. sizeof(theap_mem_info = 16+tracesize*4 so
  74. tracesize must be even !! PM }
  75. theap_mem_info = record
  76. previous,
  77. next : pheap_mem_info;
  78. size : longint;
  79. sig : longint;
  80. {$ifdef EXTRA}
  81. release_sig : longint;
  82. prev_valid : pheap_mem_info;
  83. {$endif EXTRA}
  84. calls : array [1..tracesize] of longint;
  85. extra_info : record
  86. end;
  87. end;
  88. var
  89. ptext : ^text;
  90. ownfile : text;
  91. {$ifdef EXTRA}
  92. error_file : text;
  93. heap_valid_first,
  94. heap_valid_last : pheap_mem_info;
  95. {$endif EXTRA}
  96. heap_mem_root : pheap_mem_info;
  97. getmem_cnt,
  98. freemem_cnt : longint;
  99. getmem_size,
  100. freemem_size : longint;
  101. getmem8_size,
  102. freemem8_size : longint;
  103. {*****************************************************************************
  104. Crc 32
  105. *****************************************************************************}
  106. var
  107. {$ifdef Delphi}
  108. Crc32Tbl : array[0..255] of longword;
  109. {$else Delphi}
  110. Crc32Tbl : array[0..255] of longint;
  111. {$endif Delphi}
  112. procedure MakeCRC32Tbl;
  113. var
  114. {$ifdef Delphi}
  115. crc : longword;
  116. {$else Delphi}
  117. crc : longint;
  118. {$endif Delphi}
  119. i,n : byte;
  120. begin
  121. for i:=0 to 255 do
  122. begin
  123. crc:=i;
  124. for n:=1 to 8 do
  125. if odd(crc) then
  126. crc:=(crc shr 1) xor $edb88320
  127. else
  128. crc:=crc shr 1;
  129. Crc32Tbl[i]:=crc;
  130. end;
  131. end;
  132. {$ifopt R+}
  133. {$define Range_check_on}
  134. {$endif opt R+}
  135. {$R- needed here }
  136. Function UpdateCrc32(InitCrc:longint;var InBuf;InLen:Longint):longint;
  137. var
  138. i : longint;
  139. p : pchar;
  140. begin
  141. p:=@InBuf;
  142. for i:=1 to InLen do
  143. begin
  144. InitCrc:=Crc32Tbl[byte(InitCrc) xor byte(p^)] xor (InitCrc shr 8);
  145. inc(longint(p));
  146. end;
  147. UpdateCrc32:=InitCrc;
  148. end;
  149. Function calculate_sig(p : pheap_mem_info) : longint;
  150. var
  151. crc : longint;
  152. pl : plongint;
  153. begin
  154. crc:=$ffffffff;
  155. crc:=UpdateCrc32(crc,p^.size,sizeof(longint));
  156. crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(longint));
  157. if extra_info_size>0 then
  158. crc:=UpdateCrc32(crc,p^.extra_info,exact_info_size);
  159. if add_tail then
  160. begin
  161. { Check also 4 bytes just after allocation !! }
  162. pl:=pointer(p)+extra_info_size+sizeof(theap_mem_info)+p^.size;
  163. crc:=UpdateCrc32(crc,pl^,sizeof(longint));
  164. end;
  165. calculate_sig:=crc;
  166. end;
  167. {$ifdef EXTRA}
  168. Function calculate_release_sig(p : pheap_mem_info) : longint;
  169. var
  170. crc : longint;
  171. pl : plongint;
  172. begin
  173. crc:=$ffffffff;
  174. crc:=UpdateCrc32(crc,p^.size,sizeof(longint));
  175. crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(longint));
  176. if extra_info_size>0 then
  177. crc:=UpdateCrc32(crc,p^.extra_info,exact_info_size);
  178. { Check the whole of the whole allocation }
  179. pl:=pointer(p)+extra_info_size+sizeof(theap_mem_info);
  180. crc:=UpdateCrc32(crc,pl^,p^.size);
  181. { Check also 4 bytes just after allocation !! }
  182. if add_tail then
  183. begin
  184. { Check also 4 bytes just after allocation !! }
  185. pl:=pointer(p)+extra_info_size+sizeof(theap_mem_info)+p^.size;
  186. crc:=UpdateCrc32(crc,pl^,sizeof(longint));
  187. end;
  188. calculate_release_sig:=crc;
  189. end;
  190. {$endif EXTRA}
  191. {$ifdef Range_check_on}
  192. {$R+}
  193. {$undef Range_check_on}
  194. {$endif Range_check_on}
  195. {*****************************************************************************
  196. Helpers
  197. *****************************************************************************}
  198. procedure call_stack(pp : pheap_mem_info;var ptext : text);
  199. var
  200. i : longint;
  201. begin
  202. writeln(ptext,'Call trace for block 0x',hexstr(longint(pointer(pp)+sizeof(theap_mem_info)),8),' size ',pp^.size);
  203. for i:=1 to tracesize do
  204. if pp^.calls[i]<>0 then
  205. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  206. for i:=0 to (exact_info_size div 4)-1 do
  207. writeln(ptext,'info ',i,'=',plongint(pointer(@pp^.extra_info)+4*i)^);
  208. end;
  209. procedure call_free_stack(pp : pheap_mem_info;var ptext : text);
  210. var
  211. i : longint;
  212. begin
  213. writeln(ptext,'Call trace for block 0x',hexstr(longint(pointer(pp)+sizeof(theap_mem_info)),8),' size ',pp^.size);
  214. for i:=1 to tracesize div 2 do
  215. if pp^.calls[i]<>0 then
  216. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  217. writeln(ptext,' was released at ');
  218. for i:=(tracesize div 2)+1 to tracesize do
  219. if pp^.calls[i]<>0 then
  220. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  221. for i:=0 to (exact_info_size div 4)-1 do
  222. writeln(ptext,'info ',i,'=',plongint(pointer(@pp^.extra_info)+4*i)^);
  223. end;
  224. procedure dump_already_free(p : pheap_mem_info;var ptext : text);
  225. begin
  226. Writeln(ptext,'Marked memory at ',HexStr(longint(pointer(p)+sizeof(theap_mem_info)),8),' released');
  227. call_free_stack(p,ptext);
  228. Writeln(ptext,'freed again at');
  229. dump_stack(ptext,get_caller_frame(get_frame));
  230. end;
  231. procedure dump_error(p : pheap_mem_info;var ptext : text);
  232. begin
  233. Writeln(ptext,'Marked memory at ',HexStr(longint(pointer(p)+sizeof(theap_mem_info)),8),' invalid');
  234. Writeln(ptext,'Wrong signature $',hexstr(p^.sig,8)
  235. ,' instead of ',hexstr(calculate_sig(p),8));
  236. dump_stack(ptext,get_caller_frame(get_frame));
  237. end;
  238. {$ifdef EXTRA}
  239. procedure dump_change_after(p : pheap_mem_info;var ptext : text);
  240. var pp : pchar;
  241. i : longint;
  242. begin
  243. Writeln(ptext,'Marked memory at ',HexStr(longint(pointer(p)+sizeof(theap_mem_info)),8),' invalid');
  244. Writeln(ptext,'Wrong release CRC $',hexstr(p^.release_sig,8)
  245. ,' instead of ',hexstr(calculate_release_sig(p),8));
  246. Writeln(ptext,'This memory was changed after call to freemem !');
  247. call_free_stack(p,ptext);
  248. pp:=pointer(p)+sizeof(theap_mem_info)+extra_info_size;
  249. for i:=0 to p^.size-1 do
  250. if byte(pp[i])<>$F0 then
  251. Writeln(ptext,'offset',i,':$',hexstr(i,8),'"',pp[i],'"');
  252. end;
  253. {$endif EXTRA}
  254. procedure dump_wrong_size(p : pheap_mem_info;size : longint;var ptext : text);
  255. var
  256. i : longint;
  257. begin
  258. Writeln(ptext,'Marked memory at ',HexStr(longint(pointer(p)+sizeof(theap_mem_info)),8),' invalid');
  259. Writeln(ptext,'Wrong size : ',p^.size,' allocated ',size,' freed');
  260. dump_stack(ptext,get_caller_frame(get_frame));
  261. for i:=0 to (exact_info_size div 4)-1 do
  262. writeln(ptext,'info ',i,'=',plongint(@p^.extra_info+4*i)^);
  263. call_stack(p,ptext);
  264. end;
  265. function is_in_getmem_list (p : pheap_mem_info) : boolean;
  266. var
  267. i : longint;
  268. pp : pheap_mem_info;
  269. begin
  270. is_in_getmem_list:=false;
  271. pp:=heap_mem_root;
  272. i:=0;
  273. while pp<>nil do
  274. begin
  275. if ((pp^.sig<>$DEADBEEF) or usecrc) and
  276. ((pp^.sig<>calculate_sig(pp)) or not usecrc) and
  277. (pp^.sig <> $AAAAAAAA) then
  278. begin
  279. writeln(ptext^,'error in linked list of heap_mem_info');
  280. RunError(204);
  281. end;
  282. if pp=p then
  283. is_in_getmem_list:=true;
  284. pp:=pp^.previous;
  285. inc(i);
  286. if i>getmem_cnt-freemem_cnt then
  287. writeln(ptext^,'error in linked list of heap_mem_info');
  288. end;
  289. end;
  290. {*****************************************************************************
  291. TraceGetMem
  292. *****************************************************************************}
  293. Function TraceGetMem(size:longint):pointer;
  294. var
  295. i,bp : longint;
  296. pl : plongint;
  297. p : pointer;
  298. begin
  299. inc(getmem_size,size);
  300. inc(getmem8_size,((size+7) div 8)*8);
  301. { Do the real GetMem, but alloc also for the info block }
  302. bp:=size+sizeof(theap_mem_info)+extra_info_size;
  303. if add_tail then
  304. inc(bp,sizeof(longint));
  305. p:=SysGetMem(bp);
  306. { Create the info block }
  307. pheap_mem_info(p)^.sig:=$DEADBEEF;
  308. pheap_mem_info(p)^.size:=size;
  309. if add_tail then
  310. begin
  311. pl:=pointer(p)+bp-sizeof(longint);
  312. pl^:=$DEADBEEF;
  313. end;
  314. bp:=get_caller_frame(get_frame);
  315. for i:=1 to tracesize do
  316. begin
  317. pheap_mem_info(p)^.calls[i]:=get_caller_addr(bp);
  318. bp:=get_caller_frame(bp);
  319. end;
  320. { insert in the linked list }
  321. if heap_mem_root<>nil then
  322. heap_mem_root^.next:=pheap_mem_info(p);
  323. pheap_mem_info(p)^.previous:=heap_mem_root;
  324. pheap_mem_info(p)^.next:=nil;
  325. {$ifdef EXTRA}
  326. pheap_mem_info(p)^.prev_valid:=heap_valid_last;
  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. { must be changed before fill_extra_info is called
  333. because checkpointer can be called from within
  334. fill_extra_info PM }
  335. inc(getmem_cnt);
  336. if assigned(fill_extra_info) then
  337. begin
  338. inside_trace_getmem:=true;
  339. fill_extra_info(@pheap_mem_info(p)^.extra_info);
  340. inside_trace_getmem:=false;
  341. end;
  342. { update the pointer }
  343. if usecrc then
  344. pheap_mem_info(p)^.sig:=calculate_sig(pheap_mem_info(p));
  345. inc(p,sizeof(theap_mem_info)+extra_info_size);
  346. TraceGetmem:=p;
  347. end;
  348. {*****************************************************************************
  349. TraceFreeMem
  350. *****************************************************************************}
  351. function TraceFreeMemSize(var p:pointer;size:longint):longint;
  352. var
  353. i,bp, ppsize : longint;
  354. pp : pheap_mem_info;
  355. {$ifdef EXTRA}
  356. pp2 : pheap_mem_info;
  357. {$endif}
  358. begin
  359. inc(freemem_size,size);
  360. inc(freemem8_size,((size+7) div 8)*8);
  361. ppsize:= size + sizeof(theap_mem_info)+extra_info_size;
  362. if add_tail then
  363. ppsize:=ppsize+sizeof(longint);
  364. dec(p,sizeof(theap_mem_info)+extra_info_size);
  365. pp:=pheap_mem_info(p);
  366. if not quicktrace and not(is_in_getmem_list(pp)) then
  367. RunError(204);
  368. if (pp^.sig=$AAAAAAAA) and not usecrc then
  369. begin
  370. error_in_heap:=true;
  371. dump_already_free(pp,ptext^);
  372. if haltonerror then halt(1);
  373. end
  374. else if ((pp^.sig<>$DEADBEEF) or usecrc) and
  375. ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
  376. begin
  377. error_in_heap:=true;
  378. dump_error(pp,ptext^);
  379. {$ifdef EXTRA}
  380. dump_error(pp,error_file);
  381. {$endif EXTRA}
  382. { don't release anything in this case !! }
  383. if haltonerror then halt(1);
  384. exit;
  385. end
  386. else if pp^.size<>size then
  387. begin
  388. error_in_heap:=true;
  389. dump_wrong_size(pp,size,ptext^);
  390. {$ifdef EXTRA}
  391. dump_wrong_size(pp,size,error_file);
  392. {$endif EXTRA}
  393. if haltonerror then halt(1);
  394. { don't release anything in this case !! }
  395. exit;
  396. end;
  397. { now it is released !! }
  398. pp^.sig:=$AAAAAAAA;
  399. if not keepreleased then
  400. begin
  401. if pp^.next<>nil then
  402. pp^.next^.previous:=pp^.previous;
  403. if pp^.previous<>nil then
  404. pp^.previous^.next:=pp^.next;
  405. if pp=heap_mem_root then
  406. heap_mem_root:=heap_mem_root^.previous;
  407. end
  408. else
  409. begin
  410. bp:=get_caller_frame(get_frame);
  411. for i:=(tracesize div 2)+1 to tracesize do
  412. begin
  413. pp^.calls[i]:=get_caller_addr(bp);
  414. bp:=get_caller_frame(bp);
  415. end;
  416. end;
  417. inc(freemem_cnt);
  418. { release the normal memory at least !! }
  419. { this way we keep all info about all released memory !! }
  420. if keepreleased then
  421. begin
  422. {$ifndef EXTRA}
  423. dec(ppsize,sizeof(theap_mem_info)+extra_info_size);
  424. inc(p,sizeof(theap_mem_info)+extra_info_size);
  425. {$else EXTRA}
  426. inc(p,sizeof(theap_mem_info)+extra_info_size);
  427. fillchar(p^,size,#240); { $F0 will lead to GFP if used as pointer ! }
  428. { We want to check if the memory was changed after release !! }
  429. pp^.release_sig:=calculate_release_sig(pp);
  430. if pp=heap_valid_last then
  431. begin
  432. heap_valid_last:=pp^.prev_valid;
  433. if pp=heap_valid_first then
  434. heap_valid_first:=nil;
  435. exit;
  436. end;
  437. pp2:=heap_valid_last;
  438. while assigned(pp2) do
  439. begin
  440. if pp2^.prev_valid=pp then
  441. begin
  442. pp2^.prev_valid:=pp^.prev_valid;
  443. if pp=heap_valid_first then
  444. heap_valid_first:=pp2;
  445. exit;
  446. end
  447. else
  448. pp2:=pp2^.prev_valid;
  449. end;
  450. exit;
  451. {$endif EXTRA}
  452. end;
  453. i:=SysFreeMemSize(p,ppsize);
  454. dec(i,sizeof(theap_mem_info)+extra_info_size);
  455. if add_tail then
  456. dec(i,sizeof(longint));
  457. TraceFreeMemSize:=i;
  458. end;
  459. function TraceMemSize(p:pointer):Longint;
  460. var
  461. l : longint;
  462. begin
  463. l:=SysMemSize(p-(sizeof(theap_mem_info)+extra_info_size));
  464. dec(l,sizeof(theap_mem_info)+extra_info_size);
  465. if add_tail then
  466. dec(l,sizeof(longint));
  467. TraceMemSize:=l;
  468. end;
  469. function TraceFreeMem(var p:pointer):longint;
  470. var
  471. size : longint;
  472. pp : pheap_mem_info;
  473. begin
  474. pp:=pheap_mem_info(pointer(p)-(sizeof(theap_mem_info)+extra_info_size));
  475. size:=TraceMemSize(p);
  476. { this can never happend normaly }
  477. if pp^.size>size then
  478. begin
  479. dump_wrong_size(pp,size,ptext^);
  480. {$ifdef EXTRA}
  481. dump_wrong_size(pp,size,error_file);
  482. {$endif EXTRA}
  483. end;
  484. TraceFreeMem:=TraceFreeMemSize(p,pp^.size);
  485. end;
  486. {*****************************************************************************
  487. Check pointer
  488. *****************************************************************************}
  489. {$ifndef linux}
  490. {$S-}
  491. {$endif}
  492. {$ifdef go32v2}
  493. var
  494. __stklen : cardinal;external name '__stklen';
  495. __stkbottom : cardinal;external name '__stkbottom';
  496. edata : cardinal; external name 'edata';
  497. heap_at_init : pointer;
  498. {$endif go32v2}
  499. {$ifdef win32}
  500. var
  501. StartUpHeapEnd : pointer;
  502. {$endif}
  503. procedure CheckPointer(p : pointer);[public, alias : 'FPC_CHECKPOINTER'];
  504. var
  505. i : longint;
  506. pp : pheap_mem_info;
  507. get_ebp,stack_top : cardinal;
  508. data_end : cardinal;
  509. label
  510. _exit;
  511. begin
  512. asm
  513. pushal
  514. end;
  515. if p=nil then
  516. goto _exit;
  517. i:=0;
  518. {$ifdef go32v2}
  519. if cardinal(p)<$1000 then
  520. runerror(216);
  521. asm
  522. movl %ebp,get_ebp
  523. leal edata,%eax
  524. movl %eax,data_end
  525. end;
  526. stack_top:=__stkbottom+__stklen;
  527. { allow all between start of code and end of data }
  528. if cardinal(p)<=data_end then
  529. goto _exit;
  530. { .bss section }
  531. if cardinal(p)<=cardinal(heap_at_init) then
  532. goto _exit;
  533. { stack can be above heap !! }
  534. if (cardinal(p)>=get_ebp) and (cardinal(p)<=stack_top) then
  535. goto _exit;
  536. {$endif go32v2}
  537. { I don't know where the stack is in other OS !! }
  538. {$ifdef win32}
  539. if (cardinal(p)>=$40000) and (p<=HeapOrg) then
  540. goto _exit;
  541. { inside stack ? }
  542. if (cardinal(startupheapend)<Win32StackTop) and (cardinal(p)>cardinal(startupheapend)) and
  543. (cardinal(p)<Win32StackTop) then
  544. goto _exit;
  545. {$endif win32}
  546. if p>=heapptr then
  547. runerror(216);
  548. { first try valid list faster }
  549. {$ifdef EXTRA}
  550. pp:=heap_valid_last;
  551. while pp<>nil do
  552. begin
  553. { inside this valid block ! }
  554. { we can be changing the extrainfo !! }
  555. if (cardinal(p)>=cardinal(pp)+sizeof(theap_mem_info){+extra_info_size}) and
  556. (cardinal(p)<=cardinal(pp)+sizeof(theap_mem_info)+extra_info_size+pp^.size) then
  557. begin
  558. { check allocated block }
  559. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  560. ((pp^.sig=calculate_sig(pp)) and usecrc) or
  561. { special case of the fill_extra_info call }
  562. ((pp=heap_valid_last) and usecrc and (pp^.sig=$DEADBEEF)
  563. and inside_trace_getmem) then
  564. goto _exit
  565. else
  566. begin
  567. writeln(ptext^,'corrupted heap_mem_info');
  568. dump_error(pp,ptext^);
  569. halt(1);
  570. end;
  571. end
  572. else
  573. pp:=pp^.prev_valid;
  574. inc(i);
  575. if i>getmem_cnt-freemem_cnt then
  576. begin
  577. writeln(ptext^,'error in linked list of heap_mem_info');
  578. halt(1);
  579. end;
  580. end;
  581. i:=0;
  582. {$endif EXTRA}
  583. pp:=heap_mem_root;
  584. while pp<>nil do
  585. begin
  586. { inside this block ! }
  587. if (cardinal(p)>=cardinal(pp)+sizeof(theap_mem_info)+extra_info_size) and
  588. (cardinal(p)<=cardinal(pp)+sizeof(theap_mem_info)+extra_info_size+pp^.size) then
  589. { allocated block }
  590. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  591. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  592. goto _exit
  593. else
  594. begin
  595. writeln(ptext^,'pointer $',hexstr(longint(p),8),' points into invalid memory block');
  596. dump_error(pp,ptext^);
  597. runerror(204);
  598. end;
  599. pp:=pp^.previous;
  600. inc(i);
  601. if i>getmem_cnt then
  602. begin
  603. writeln(ptext^,'error in linked list of heap_mem_info');
  604. halt(1);
  605. end;
  606. end;
  607. writeln(ptext^,'pointer $',hexstr(longint(p),8),' does not point to valid memory block');
  608. runerror(204);
  609. _exit:
  610. asm
  611. popal
  612. end;
  613. end;
  614. {*****************************************************************************
  615. Dump Heap
  616. *****************************************************************************}
  617. procedure dumpheap;
  618. var
  619. pp : pheap_mem_info;
  620. i : longint;
  621. begin
  622. pp:=heap_mem_root;
  623. Writeln(ptext^,'Heap dump by heaptrc unit');
  624. Writeln(ptext^,getmem_cnt, ' memory blocks allocated : ',getmem_size,'/',getmem8_size);
  625. Writeln(ptext^,freemem_cnt,' memory blocks freed : ',freemem_size,'/',freemem8_size);
  626. Writeln(ptext^,getmem_cnt-freemem_cnt,' unfreed memory blocks : ',getmem_size-freemem_size);
  627. Writeln(ptext^,'True heap size : ',system.HeapSize);
  628. Writeln(ptext^,'True free heap : ',MemAvail);
  629. Writeln(ptext^,'Should be : ',system.HeapSize-(getmem8_size-freemem8_size)-
  630. (getmem_cnt-freemem_cnt)*(sizeof(theap_mem_info)+extra_info_size));
  631. i:=getmem_cnt-freemem_cnt;
  632. while pp<>nil do
  633. begin
  634. if i<0 then
  635. begin
  636. Writeln(ptext^,'Error in heap memory list');
  637. Writeln(ptext^,'More memory blocks than expected');
  638. exit;
  639. end;
  640. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  641. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  642. begin
  643. { this one was not released !! }
  644. if exitcode<>203 then
  645. call_stack(pp,ptext^);
  646. dec(i);
  647. end
  648. else if pp^.sig<>$AAAAAAAA then
  649. begin
  650. dump_error(pp,ptext^);
  651. {$ifdef EXTRA}
  652. dump_error(pp,error_file);
  653. {$endif EXTRA}
  654. error_in_heap:=true;
  655. end
  656. {$ifdef EXTRA}
  657. else if pp^.release_sig<>calculate_release_sig(pp) then
  658. begin
  659. dump_change_after(pp,ptext^);
  660. dump_change_after(pp,error_file);
  661. error_in_heap:=true;
  662. end
  663. {$endif EXTRA}
  664. ;
  665. pp:=pp^.previous;
  666. end;
  667. end;
  668. procedure markheap;
  669. var
  670. pp : pheap_mem_info;
  671. begin
  672. pp:=heap_mem_root;
  673. while pp<>nil do
  674. begin
  675. pp^.sig:=$AAAAAAAA;
  676. pp:=pp^.previous;
  677. end;
  678. end;
  679. {*****************************************************************************
  680. AllocMem
  681. *****************************************************************************}
  682. function TraceAllocMem(size:longint):Pointer;
  683. begin
  684. TraceAllocMem:=SysAllocMem(size);
  685. end;
  686. {*****************************************************************************
  687. ReAllocMem
  688. *****************************************************************************}
  689. function TraceReAllocMem(var p:pointer;size:longint):Pointer;
  690. var
  691. newP: pointer;
  692. oldsize,
  693. i,bp : longint;
  694. pl : plongint;
  695. pp : pheap_mem_info;
  696. begin
  697. { Free block? }
  698. if size=0 then
  699. begin
  700. if p<>nil then
  701. TraceFreeMem(p);
  702. TraceReallocMem:=P;
  703. exit;
  704. end;
  705. { Allocate a new block? }
  706. if p=nil then
  707. begin
  708. p:=TraceGetMem(size);
  709. TraceReallocMem:=P;
  710. exit;
  711. end;
  712. { Resize block }
  713. dec(p,sizeof(theap_mem_info)+extra_info_size);
  714. { remove heap_mem_info from linked list }
  715. pp:=pheap_mem_info(p);
  716. if pp^.next<>nil then
  717. pp^.next^.previous:=pp^.previous;
  718. if pp^.previous<>nil then
  719. pp^.previous^.next:=pp^.next;
  720. if pp=heap_mem_root then
  721. heap_mem_root:=heap_mem_root^.previous;
  722. { Do the real ReAllocMem, but alloc also for the info block }
  723. bp:=size+sizeof(theap_mem_info)+extra_info_size;
  724. if add_tail then
  725. inc(bp,sizeof(longint));
  726. { the internal ReAllocMem is not allowed to move any data }
  727. if not SysTryResizeMem(p,bp) then
  728. begin
  729. { restore p }
  730. inc(p,sizeof(theap_mem_info)+extra_info_size);
  731. { get a new block }
  732. oldsize:=TraceMemSize(p);
  733. newP := TraceGetMem(size);
  734. { move the data }
  735. if newP <> nil then
  736. move(p^,newP^,oldsize);
  737. { release p }
  738. traceFreeMem(p);
  739. p := newP;
  740. traceReAllocMem := p;
  741. exit;
  742. end;
  743. { adjust getmem/freemem sizes }
  744. if pp^.size > size then
  745. inc(freemem_size,pp^.size-size)
  746. else
  747. inc(getmem_size,size-pp^.size);
  748. { Create the info block }
  749. pheap_mem_info(p)^.sig:=$DEADBEEF;
  750. pheap_mem_info(p)^.size:=size;
  751. if add_tail then
  752. begin
  753. pl:=pointer(p)+bp-sizeof(longint);
  754. pl^:=$DEADBEEF;
  755. end;
  756. bp:=get_caller_frame(get_frame);
  757. for i:=1 to tracesize do
  758. begin
  759. pheap_mem_info(p)^.calls[i]:=get_caller_addr(bp);
  760. bp:=get_caller_frame(bp);
  761. end;
  762. { insert in the linked list }
  763. if heap_mem_root<>nil then
  764. heap_mem_root^.next:=pheap_mem_info(p);
  765. pheap_mem_info(p)^.previous:=heap_mem_root;
  766. pheap_mem_info(p)^.next:=nil;
  767. {$ifdef EXTRA}
  768. pheap_mem_info(p)^.next_valid:=nil;
  769. if assigned(heap_valid_last) then
  770. heap_valid_last^.next_valid:=pheap_mem_info(p);
  771. heap_valid_last:=pheap_mem_info(p);
  772. if not assigned(heap_valid_first) then
  773. heap_valid_first:=pheap_mem_info(p);
  774. {$endif EXTRA}
  775. heap_mem_root:=p;
  776. if assigned(fill_extra_info) then
  777. fill_extra_info(@pheap_mem_info(p)^.extra_info);
  778. { update the pointer }
  779. if usecrc then
  780. pheap_mem_info(p)^.sig:=calculate_sig(pheap_mem_info(p));
  781. inc(p,sizeof(theap_mem_info)+extra_info_size);
  782. TraceReAllocmem:=p;
  783. end;
  784. {*****************************************************************************
  785. No specific tracing calls
  786. *****************************************************************************}
  787. function TraceMemAvail:longint;
  788. begin
  789. TraceMemAvail:=SysMemAvail;
  790. end;
  791. function TraceMaxAvail:longint;
  792. begin
  793. TraceMaxAvail:=SysMaxAvail;
  794. end;
  795. function TraceHeapSize:longint;
  796. begin
  797. TraceHeapSize:=SysHeapSize;
  798. end;
  799. {*****************************************************************************
  800. Install MemoryManager
  801. *****************************************************************************}
  802. const
  803. TraceManager:TMemoryManager=(
  804. Getmem : TraceGetMem;
  805. Freemem : TraceFreeMem;
  806. FreememSize : TraceFreeMemSize;
  807. AllocMem : TraceAllocMem;
  808. ReAllocMem : TraceReAllocMem;
  809. MemSize : TraceMemSize;
  810. MemAvail : TraceMemAvail;
  811. MaxAvail : TraceMaxAvail;
  812. HeapSize : TraceHeapsize;
  813. );
  814. procedure TraceExit;
  815. begin
  816. { no dump if error
  817. because this gives long long listings }
  818. if (exitcode<>0) and (erroraddr<>nil) then
  819. begin
  820. Writeln(ptext^,'No heap dump by heaptrc unit');
  821. Writeln(ptext^,'Exitcode = ',exitcode);
  822. if ptext<>@stderr then
  823. begin
  824. ptext:=@stderr;
  825. close(ownfile);
  826. end;
  827. exit;
  828. end;
  829. if not error_in_heap then
  830. Dumpheap;
  831. if error_in_heap and (exitcode=0) then
  832. exitcode:=203;
  833. {$ifdef EXTRA}
  834. Close(error_file);
  835. {$endif EXTRA}
  836. if ptext<>@stderr then
  837. begin
  838. ptext:=@stderr;
  839. close(ownfile);
  840. end;
  841. end;
  842. Procedure SetHeapTraceOutput(const name : string);
  843. var i : longint;
  844. begin
  845. if ptext<>@stderr then
  846. begin
  847. ptext:=@stderr;
  848. close(ownfile);
  849. end;
  850. assign(ownfile,name);
  851. {$I-}
  852. append(ownfile);
  853. if IOResult<>0 then
  854. Rewrite(ownfile);
  855. {$I+}
  856. ptext:=@ownfile;
  857. for i:=0 to Paramcount do
  858. write(ptext^,paramstr(i),' ');
  859. writeln(ptext^);
  860. end;
  861. procedure SetExtraInfo( size : longint;func : fillextrainfotype);
  862. begin
  863. if getmem_cnt>0 then
  864. begin
  865. writeln(ptext^,'Setting extra info is only possible at start !! ');
  866. dumpheap;
  867. end
  868. else
  869. begin
  870. { the total size must stay multiple of 8 !! }
  871. exact_info_size:=size;
  872. extra_info_size:=((size+7) div 8)*8;
  873. fill_extra_info:=func;
  874. end;
  875. end;
  876. Initialization
  877. MakeCRC32Tbl;
  878. SetMemoryManager(TraceManager);
  879. ptext:=@stderr;
  880. {$ifdef EXTRA}
  881. Assign(error_file,'heap.err');
  882. Rewrite(error_file);
  883. {$endif EXTRA}
  884. { checkpointer init }
  885. {$ifdef go32v2}
  886. Heap_at_init:=HeapPtr;
  887. {$endif}
  888. {$ifdef win32}
  889. StartupHeapEnd:=HeapEnd;
  890. {$endif}
  891. finalization
  892. TraceExit;
  893. end.
  894. {
  895. $Log$
  896. Revision 1.41 2000-02-10 13:59:35 peter
  897. * fixed bug with reallocmem to use the wrong size when copying the
  898. data to the new allocated pointer
  899. Revision 1.40 2000/02/09 16:59:30 peter
  900. * truncated log
  901. Revision 1.39 2000/02/07 10:42:44 peter
  902. * use backtracestrfunc()
  903. Revision 1.38 2000/02/02 11:13:15 peter
  904. * fixed tracereallocmem which supplied the wrong size for tryresize
  905. Revision 1.37 2000/01/31 23:41:30 peter
  906. * reallocmem fixed for freemem() call when size=0
  907. Revision 1.36 2000/01/20 14:25:51 jonas
  908. * finally fixed tracereallocmem completely
  909. Revision 1.35 2000/01/20 13:17:11 jonas
  910. * another problme with realloc fixed (one left)
  911. Revision 1.34 2000/01/20 12:35:35 jonas
  912. * fixed problem with reallocmem and heaptrc
  913. Revision 1.33 2000/01/07 16:41:34 daniel
  914. * copyright 2000
  915. Revision 1.32 2000/01/07 16:32:24 daniel
  916. * copyright 2000 added
  917. Revision 1.31 2000/01/05 13:56:55 jonas
  918. * fixed traceReallocMem with nil pointer (simply calls traceGetMem now in
  919. such a case)
  920. Revision 1.30 2000/01/03 19:37:52 peter
  921. * fixed reallocmem with p=nil
  922. Revision 1.29 1999/11/14 21:35:04 peter
  923. * removed warnings
  924. Revision 1.28 1999/11/09 22:32:23 pierre
  925. * several extra_size_info fixes
  926. Revision 1.27 1999/11/06 14:35:38 peter
  927. * truncated log
  928. Revision 1.26 1999/11/01 13:56:50 peter
  929. * freemem,reallocmem now get var argument
  930. Revision 1.25 1999/10/30 17:39:05 peter
  931. * memorymanager expanded with allocmem/reallocmem
  932. Revision 1.24 1999/09/17 17:14:12 peter
  933. + new heap manager supporting delphi freemem(pointer)
  934. Revision 1.23 1999/09/10 17:13:41 peter
  935. * fixed missing var
  936. Revision 1.22 1999/09/08 16:14:41 peter
  937. * pointer fixes
  938. Revision 1.21 1999/08/18 12:03:16 peter
  939. * objfpc mode for 0.99.12
  940. Revision 1.20 1999/08/17 14:56:03 michael
  941. Removed the mode for objpas
  942. }