heaptrc.pp 27 KB

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