heaptrc.pp 9.6 KB

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