heaptrc.pp 11 KB

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