heaptrc.pp 26 KB

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