heaptrc.pp 10 KB

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