heaptrc.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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. interface
  14. Procedure DumpHeap;
  15. Procedure MarkHeap;
  16. type
  17. FillExtraInfoType = procedure(p : pointer);
  18. { allows to add several longint value that can help
  19. to debug :
  20. see for instance ppheap.pas unit of the compiler source PM }
  21. Procedure SetExtraInfo( size : longint;func : FillExtraInfoType);
  22. const
  23. { tracing level
  24. splitted in two if memory is released !! }
  25. tracesize = 8;
  26. quicktrace : boolean=true;
  27. { calls halt() on error by default !! }
  28. HaltOnError : boolean = true;
  29. { set this to true if you suspect that memory
  30. is freed several times }
  31. keepreleased : boolean=false;
  32. implementation
  33. const
  34. { allows to add custom info in heap_mem_info }
  35. extra_info_size : longint = 0;
  36. exact_info_size : longint = 0;
  37. { function to fill this info up }
  38. fill_extra_info : FillExtraInfoType = nil;
  39. error_in_heap : boolean = false;
  40. type
  41. pheap_mem_info = ^theap_mem_info;
  42. { warning the size of theap_mem_info
  43. must be a multiple of 8
  44. because otherwise you will get
  45. problems when releasing the usual memory part !!
  46. sizeof(theap_mem_info = 16+tracesize*4 so
  47. tracesize must be even !! PM }
  48. theap_mem_info = record
  49. next,
  50. previous : pheap_mem_info;
  51. size : longint;
  52. sig : longint;
  53. calls : array [1..tracesize] of longint;
  54. extra_info : record
  55. end;
  56. end;
  57. var
  58. heap_mem_root : pheap_mem_info;
  59. getmem_cnt,
  60. freemem_cnt : longint;
  61. getmem_size,
  62. freemem_size : longint;
  63. getmem8_size,
  64. freemem8_size : longint;
  65. {*****************************************************************************
  66. Helpers
  67. *****************************************************************************}
  68. type plongint = ^longint;
  69. procedure call_stack(pp : pheap_mem_info);
  70. var
  71. i : longint;
  72. begin
  73. writeln(stderr,'Call trace for block 0x',hexstr(longint(pp+sizeof(theap_mem_info)),8),' size ',pp^.size);
  74. for i:=1 to tracesize do
  75. if pp^.calls[i]<>0 then
  76. writeln(stderr,' 0x',hexstr(pp^.calls[i],8));
  77. for i:=0 to (exact_info_size div 4)-1 do
  78. writeln(stderr,'info ',i,'=',plongint(@pp^.extra_info+4*i)^);
  79. end;
  80. procedure call_free_stack(pp : pheap_mem_info);
  81. var
  82. i : longint;
  83. begin
  84. writeln(stderr,'Call trace for block 0x',hexstr(longint(pp+sizeof(theap_mem_info)),8),' size ',pp^.size);
  85. for i:=1 to tracesize div 2 do
  86. if pp^.calls[i]<>0 then
  87. writeln(stderr,' 0x',hexstr(pp^.calls[i],8));
  88. writeln(stderr,' was released at ');
  89. for i:=(tracesize div 2)+1 to tracesize do
  90. if pp^.calls[i]<>0 then
  91. writeln(stderr,' 0x',hexstr(pp^.calls[i],8));
  92. for i:=0 to (exact_info_size div 4)-1 do
  93. writeln(stderr,'info ',i,'=',plongint(@pp^.extra_info+4*i)^);
  94. end;
  95. procedure dump_already_free(p : pheap_mem_info);
  96. begin
  97. Writeln(stderr,'Marked memory at ',HexStr(longint(p+sizeof(theap_mem_info)),8),' released');
  98. call_free_stack(p);
  99. Writeln(stderr,'freed again at');
  100. dump_stack(stderr,get_caller_frame(get_frame));
  101. end;
  102. procedure dump_error(p : pheap_mem_info);
  103. begin
  104. Writeln(stderr,'Marked memory at ',HexStr(longint(p+sizeof(theap_mem_info)),8),' invalid');
  105. Writeln(stderr,'Wrong signature $',hexstr(p^.sig,8));
  106. dump_stack(stderr,get_caller_frame(get_frame));
  107. end;
  108. procedure dump_wrong_size(p : pheap_mem_info;size : longint);
  109. var
  110. i : longint;
  111. begin
  112. Writeln(stderr,'Marked memory at ',HexStr(longint(p+sizeof(theap_mem_info)),8),' invalid');
  113. Writeln(stderr,'Wrong size : ',p^.size,' allocated ',size,' freed');
  114. dump_stack(stderr,get_caller_frame(get_frame));
  115. for i:=0 to (exact_info_size div 4)-1 do
  116. writeln(stderr,'info ',i,'=',plongint(@p^.extra_info+4*i)^);
  117. end;
  118. function is_in_getmem_list (p : pointer) : boolean;
  119. var
  120. i : longint;
  121. pp : pheap_mem_info;
  122. begin
  123. is_in_getmem_list:=false;
  124. pp:=heap_mem_root;
  125. i:=0;
  126. while pp<>nil do
  127. begin
  128. if (pp^.sig<>$DEADBEEF) and (pp^.sig <> $AAAAAAAA) then
  129. begin
  130. writeln(stderr,'error in linked list of heap_mem_info');
  131. RunError(204);
  132. end;
  133. if pp=p then
  134. is_in_getmem_list:=true;
  135. pp:=pp^.previous;
  136. inc(i);
  137. if i>getmem_cnt-freemem_cnt then
  138. writeln(stderr,'error in linked list of heap_mem_info');
  139. end;
  140. end;
  141. {*****************************************************************************
  142. TraceGetMem
  143. *****************************************************************************}
  144. procedure TraceGetMem(var p:pointer;size:longint);
  145. var
  146. i,bp : longint;
  147. begin
  148. inc(getmem_size,size);
  149. inc(getmem8_size,((size+7) div 8)*8);
  150. { Do the real GetMem, but alloc also for the info block }
  151. SysGetMem(p,size+sizeof(theap_mem_info)+extra_info_size);
  152. { Create the info block }
  153. pheap_mem_info(p)^.sig:=$DEADBEEF;
  154. pheap_mem_info(p)^.size:=size;
  155. bp:=get_caller_frame(get_frame);
  156. for i:=1 to tracesize do
  157. begin
  158. pheap_mem_info(p)^.calls[i]:=get_caller_addr(bp);
  159. bp:=get_caller_frame(bp);
  160. end;
  161. { insert in the linked list }
  162. if heap_mem_root<>nil then
  163. heap_mem_root^.next:=pheap_mem_info(p);
  164. pheap_mem_info(p)^.previous:=heap_mem_root;
  165. pheap_mem_info(p)^.next:=nil;
  166. heap_mem_root:=p;
  167. if assigned(fill_extra_info) then
  168. fill_extra_info(@pheap_mem_info(p)^.extra_info);
  169. { update the pointer }
  170. inc(p,sizeof(theap_mem_info)+extra_info_size);
  171. inc(getmem_cnt);
  172. end;
  173. {*****************************************************************************
  174. TraceFreeMem
  175. *****************************************************************************}
  176. procedure TraceFreeMem(var p:pointer;size:longint);
  177. var i,bp, ppsize : longint;
  178. pp : pheap_mem_info;
  179. begin
  180. inc(freemem_size,size);
  181. inc(freemem8_size,((size+7) div 8)*8);
  182. ppsize:= size + sizeof(theap_mem_info)+extra_info_size;
  183. dec(p,sizeof(theap_mem_info)+extra_info_size);
  184. pp:=pheap_mem_info(p);
  185. if not quicktrace and not(is_in_getmem_list(p)) then
  186. RunError(204);
  187. if pp^.sig=$AAAAAAAA then
  188. begin
  189. error_in_heap:=true;
  190. dump_already_free(pp);
  191. if haltonerror then halt(1);
  192. end
  193. else if pp^.sig<>$DEADBEEF then
  194. begin
  195. error_in_heap:=true;
  196. dump_error(pp);
  197. { don't release anything in this case !! }
  198. if haltonerror then halt(1);
  199. exit;
  200. end
  201. else if pp^.size<>size then
  202. begin
  203. error_in_heap:=true;
  204. dump_wrong_size(pp,size);
  205. if haltonerror then halt(1);
  206. { don't release anything in this case !! }
  207. exit;
  208. end;
  209. { now it is released !! }
  210. pp^.sig:=$AAAAAAAA;
  211. if not keepreleased then
  212. begin
  213. if pp^.next<>nil then
  214. pp^.next^.previous:=pp^.previous;
  215. if pp^.previous<>nil then
  216. pp^.previous^.next:=pp^.next;
  217. if pp=heap_mem_root then
  218. heap_mem_root:=heap_mem_root^.previous;
  219. bp:=get_caller_frame(get_frame);
  220. for i:=(tracesize div 2)+1 to tracesize do
  221. begin
  222. pp^.calls[i]:=get_caller_addr(bp);
  223. bp:=get_caller_frame(bp);
  224. end;
  225. end;
  226. inc(freemem_cnt);
  227. { release the normal memory at least !! }
  228. { this way we keep all info about all released memory !! }
  229. if keepreleased then
  230. begin
  231. dec(ppsize,sizeof(theap_mem_info)+extra_info_size);
  232. inc(p,sizeof(theap_mem_info)+extra_info_size);
  233. end;
  234. SysFreeMem(p,ppsize);
  235. end;
  236. {*****************************************************************************
  237. Dump Heap
  238. *****************************************************************************}
  239. procedure dumpheap;
  240. var
  241. pp : pheap_mem_info;
  242. i : longint;
  243. begin
  244. pp:=heap_mem_root;
  245. Writeln(stderr,'Heap dump by heaptrc unit');
  246. Writeln(stderr,getmem_cnt, ' memory blocks allocated : ',getmem_size,'/',getmem8_size);
  247. Writeln(stderr,freemem_cnt,' memory blocks freed : ',freemem_size,'/',freemem8_size);
  248. Writeln(stderr,getmem_cnt-freemem_cnt,' unfreed memory blocks : ',getmem_size-freemem_size);
  249. Writeln(stderr,'True heap size : ',system.HeapSize);
  250. Writeln(stderr,'True free heap : ',MemAvail);
  251. Writeln(stderr,'Should be : ',system.HeapSize-(getmem8_size-freemem8_size)-
  252. (getmem_cnt-freemem_cnt)*(sizeof(theap_mem_info)+extra_info_size));
  253. i:=getmem_cnt-freemem_cnt;
  254. while pp<>nil do
  255. begin
  256. if i<0 then
  257. begin
  258. Writeln(stderr,'Error in heap memory list');
  259. Writeln(stderr,'More memory blocks than expected');
  260. exit;
  261. end;
  262. if pp^.sig=$DEADBEEF then
  263. begin
  264. { this one was not released !! }
  265. call_stack(pp);
  266. dec(i);
  267. end
  268. else if pp^.sig<>$AAAAAAAA then
  269. dump_error(pp);
  270. pp:=pp^.previous;
  271. end;
  272. end;
  273. procedure markheap;
  274. var
  275. pp : pheap_mem_info;
  276. begin
  277. pp:=heap_mem_root;
  278. while pp<>nil do
  279. begin
  280. pp^.sig:=$AAAAAAAA;
  281. pp:=pp^.previous;
  282. end;
  283. end;
  284. {*****************************************************************************
  285. Install MemoryManager
  286. *****************************************************************************}
  287. const
  288. TraceManager:TMemoryManager=(
  289. Getmem : TraceGetMem;
  290. Freemem : TraceFreeMem
  291. );
  292. var
  293. SaveExit : pointer;
  294. procedure TraceExit;
  295. begin
  296. ExitProc:=SaveExit;
  297. if not error_in_heap then
  298. Dumpheap;
  299. end;
  300. procedure SetExtraInfo( size : longint;func : fillextrainfotype);
  301. begin
  302. if getmem_cnt>0 then
  303. begin
  304. writeln(stderr,'settting extra info is only possible at start !! ');
  305. dumpheap;
  306. end
  307. else
  308. begin
  309. { the total size must stay multiple of 8 !! }
  310. exact_info_size:=size;
  311. extra_info_size:=((size+7) div 8)*8;
  312. fill_extra_info:=func;
  313. end;
  314. end;
  315. begin
  316. SetMemoryManager(TraceManager);
  317. SaveExit:=ExitProc;
  318. ExitProc:=@TraceExit;
  319. end.
  320. {
  321. $Log$
  322. Revision 1.10 1999-02-16 17:20:26 pierre
  323. * no heap dump if program has an heap error !
  324. Revision 1.9 1999/01/22 12:39:22 pierre
  325. + added text arg for dump_stack
  326. Revision 1.8 1998/12/15 23:49:51 michael
  327. + Removed underscores in heaptrc unit
  328. Revision 1.7 1998/11/16 12:20:13 peter
  329. * write extra info also for wrong size
  330. Revision 1.6 1998/11/06 08:46:01 pierre
  331. * size is now also checked
  332. + added halt_on_error variable (default true)
  333. to stop at first error in getmem/freemem
  334. Revision 1.5 1998/10/09 11:59:31 pierre
  335. * changed default to keepreleased=false
  336. (allows to compile pp in one call without reaching the
  337. 64Mb limit of Windows 95 dos box)
  338. * corrected so typo errors
  339. Revision 1.4 1998/10/08 14:49:05 pierre
  340. + added possibility for more info
  341. Revision 1.3 1998/10/06 17:09:13 pierre
  342. + added trace of first dispose for errors
  343. Revision 1.2 1998/10/02 10:35:38 peter
  344. + quicktrace
  345. Revision 1.1 1998/10/01 14:54:20 peter
  346. + first version
  347. }