heaptrc.pp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by the Free Pascal development team.
  4. Heap tracer
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit heaptrc;
  12. interface
  13. {$goto on}
  14. Procedure DumpHeap;
  15. { define EXTRA to add more
  16. tests :
  17. - keep all memory after release and
  18. check by CRC value if not changed after release
  19. WARNING this needs extremely much memory (PM) }
  20. type
  21. tFillExtraInfoProc = procedure(p : pointer);
  22. tdisplayextrainfoProc = procedure (var ptext : text;p : pointer);
  23. { Allows to add info pre memory block, see ppheap.pas of the compiler
  24. for example source }
  25. procedure SetHeapExtraInfo( size : ptrint;fillproc : tfillextrainfoproc;displayproc : tdisplayextrainfoproc);
  26. { Redirection of the output to a file }
  27. procedure SetHeapTraceOutput(const name : string);
  28. const
  29. { tracing level
  30. splitted in two if memory is released !! }
  31. {$ifdef EXTRA}
  32. tracesize = 16;
  33. {$else EXTRA}
  34. tracesize = 8;
  35. {$endif EXTRA}
  36. { install heaptrc memorymanager }
  37. useheaptrace : boolean=true;
  38. { less checking }
  39. quicktrace : boolean=true;
  40. { calls halt() on error by default !! }
  41. HaltOnError : boolean = true;
  42. { set this to true if you suspect that memory
  43. is freed several times }
  44. {$ifdef EXTRA}
  45. keepreleased : boolean=true;
  46. {$else EXTRA}
  47. keepreleased : boolean=false;
  48. {$endif EXTRA}
  49. { add a small footprint at the end of memory blocks, this
  50. can check for memory overwrites at the end of a block }
  51. add_tail : boolean = true;
  52. { put crc in sig
  53. this allows to test for writing into that part }
  54. usecrc : boolean = true;
  55. implementation
  56. type
  57. pptrint = ^ptrint;
  58. const
  59. { allows to add custom info in heap_mem_info, this is the size that will
  60. be allocated for this information }
  61. extra_info_size : ptrint = 0;
  62. exact_info_size : ptrint = 0;
  63. EntryMemUsed : ptrint = 0;
  64. { function to fill this info up }
  65. fill_extra_info_proc : TFillExtraInfoProc = nil;
  66. display_extra_info_proc : TDisplayExtraInfoProc = nil;
  67. error_in_heap : boolean = false;
  68. inside_trace_getmem : boolean = false;
  69. { indicates where the output will be redirected }
  70. { only set using environment variables }
  71. outputstr : shortstring = '';
  72. type
  73. pheap_extra_info = ^theap_extra_info;
  74. theap_extra_info = record
  75. check : cardinal; { used to check if the procvar is still valid }
  76. fillproc : tfillextrainfoProc;
  77. displayproc : tdisplayextrainfoProc;
  78. data : record
  79. end;
  80. end;
  81. { warning the size of theap_mem_info
  82. must be a multiple of 8
  83. because otherwise you will get
  84. problems when releasing the usual memory part !!
  85. sizeof(theap_mem_info = 16+tracesize*4 so
  86. tracesize must be even !! PM }
  87. pheap_mem_info = ^theap_mem_info;
  88. theap_mem_info = record
  89. previous,
  90. next : pheap_mem_info;
  91. size : ptrint;
  92. sig : longword;
  93. {$ifdef EXTRA}
  94. release_sig : longword;
  95. prev_valid : pheap_mem_info;
  96. {$endif EXTRA}
  97. calls : array [1..tracesize] of pointer;
  98. exact_info_size : word;
  99. extra_info_size : word;
  100. extra_info : pheap_extra_info;
  101. end;
  102. var
  103. ptext : ^text;
  104. ownfile : text;
  105. {$ifdef EXTRA}
  106. error_file : text;
  107. heap_valid_first,
  108. heap_valid_last : pheap_mem_info;
  109. {$endif EXTRA}
  110. heap_mem_root : pheap_mem_info;
  111. getmem_cnt,
  112. freemem_cnt : ptrint;
  113. getmem_size,
  114. freemem_size : ptrint;
  115. getmem8_size,
  116. freemem8_size : ptrint;
  117. {*****************************************************************************
  118. Crc 32
  119. *****************************************************************************}
  120. var
  121. Crc32Tbl : array[0..255] of longword;
  122. procedure MakeCRC32Tbl;
  123. var
  124. crc : longword;
  125. i,n : byte;
  126. begin
  127. for i:=0 to 255 do
  128. begin
  129. crc:=i;
  130. for n:=1 to 8 do
  131. if odd(crc) then
  132. crc:=(crc shr 1) xor $edb88320
  133. else
  134. crc:=crc shr 1;
  135. Crc32Tbl[i]:=crc;
  136. end;
  137. end;
  138. Function UpdateCrc32(InitCrc:longword;var InBuf;InLen:ptrint):longword;
  139. var
  140. i : ptrint;
  141. p : pchar;
  142. begin
  143. p:=@InBuf;
  144. for i:=1 to InLen do
  145. begin
  146. InitCrc:=Crc32Tbl[byte(InitCrc) xor byte(p^)] xor (InitCrc shr 8);
  147. inc(p);
  148. end;
  149. UpdateCrc32:=InitCrc;
  150. end;
  151. Function calculate_sig(p : pheap_mem_info) : longword;
  152. var
  153. crc : longword;
  154. pl : pptrint;
  155. begin
  156. crc:=cardinal($ffffffff);
  157. crc:=UpdateCrc32(crc,p^.size,sizeof(ptrint));
  158. crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(ptrint));
  159. if p^.extra_info_size>0 then
  160. crc:=UpdateCrc32(crc,p^.extra_info^,p^.exact_info_size);
  161. if add_tail then
  162. begin
  163. { Check also 4 bytes just after allocation !! }
  164. pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info)+p^.size;
  165. crc:=UpdateCrc32(crc,pl^,sizeof(ptrint));
  166. end;
  167. calculate_sig:=crc;
  168. end;
  169. {$ifdef EXTRA}
  170. Function calculate_release_sig(p : pheap_mem_info) : longword;
  171. var
  172. crc : longword;
  173. pl : pptrint;
  174. begin
  175. crc:=$ffffffff;
  176. crc:=UpdateCrc32(crc,p^.size,sizeof(ptrint));
  177. crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(ptrint));
  178. if p^.extra_info_size>0 then
  179. crc:=UpdateCrc32(crc,p^.extra_info^,p^.exact_info_size);
  180. { Check the whole of the whole allocation }
  181. pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info);
  182. crc:=UpdateCrc32(crc,pl^,p^.size);
  183. { Check also 4 bytes just after allocation !! }
  184. if add_tail then
  185. begin
  186. { Check also 4 bytes just after allocation !! }
  187. pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info)+p^.size;
  188. crc:=UpdateCrc32(crc,pl^,sizeof(ptrint));
  189. end;
  190. calculate_release_sig:=crc;
  191. end;
  192. {$endif EXTRA}
  193. {*****************************************************************************
  194. Helpers
  195. *****************************************************************************}
  196. procedure call_stack(pp : pheap_mem_info;var ptext : text);
  197. var
  198. i : ptrint;
  199. begin
  200. writeln(ptext,'Call trace for block $',hexstr(ptrint(pointer(pp)+sizeof(theap_mem_info)),2*sizeof(pointer)),' size ',pp^.size);
  201. for i:=1 to tracesize do
  202. if pp^.calls[i]<>nil then
  203. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  204. { the check is done to be sure that the procvar is not overwritten }
  205. if assigned(pp^.extra_info) and
  206. (pp^.extra_info^.check=$12345678) and
  207. assigned(pp^.extra_info^.displayproc) then
  208. pp^.extra_info^.displayproc(ptext,@pp^.extra_info^.data);
  209. end;
  210. procedure call_free_stack(pp : pheap_mem_info;var ptext : text);
  211. var
  212. i : ptrint;
  213. begin
  214. writeln(ptext,'Call trace for block at $',hexstr(ptrint(pointer(pp)+sizeof(theap_mem_info)),2*sizeof(pointer)),' size ',pp^.size);
  215. for i:=1 to tracesize div 2 do
  216. if pp^.calls[i]<>nil then
  217. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  218. writeln(ptext,' was released at ');
  219. for i:=(tracesize div 2)+1 to tracesize do
  220. if pp^.calls[i]<>nil then
  221. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  222. { the check is done to be sure that the procvar is not overwritten }
  223. if assigned(pp^.extra_info) and
  224. (pp^.extra_info^.check=$12345678) and
  225. assigned(pp^.extra_info^.displayproc) then
  226. pp^.extra_info^.displayproc(ptext,@pp^.extra_info^.data);
  227. end;
  228. procedure dump_already_free(p : pheap_mem_info;var ptext : text);
  229. begin
  230. Writeln(ptext,'Marked memory at $',HexStr(ptrint(pointer(p)+sizeof(theap_mem_info)),2*sizeof(pointer)),' released');
  231. call_free_stack(p,ptext);
  232. Writeln(ptext,'freed again at');
  233. dump_stack(ptext,get_caller_frame(get_frame));
  234. end;
  235. procedure dump_error(p : pheap_mem_info;var ptext : text);
  236. begin
  237. Writeln(ptext,'Marked memory at $',HexStr(ptrint(pointer(p)+sizeof(theap_mem_info)),2*sizeof(pointer)),' invalid');
  238. Writeln(ptext,'Wrong signature $',hexstr(p^.sig,8),' instead of ',hexstr(calculate_sig(p),8));
  239. dump_stack(ptext,get_caller_frame(get_frame));
  240. end;
  241. {$ifdef EXTRA}
  242. procedure dump_change_after(p : pheap_mem_info;var ptext : text);
  243. var pp : pchar;
  244. i : ptrint;
  245. begin
  246. Writeln(ptext,'Marked memory at $',HexStr(ptrint(pointer(p)+sizeof(theap_mem_info)),2*sizeof(pointer)),' invalid');
  247. Writeln(ptext,'Wrong release CRC $',hexstr(p^.release_sig,8),' instead of ',hexstr(calculate_release_sig(p),8));
  248. Writeln(ptext,'This memory was changed after call to freemem !');
  249. call_free_stack(p,ptext);
  250. pp:=pointer(p)+sizeof(theap_mem_info);
  251. for i:=0 to p^.size-1 do
  252. if byte(pp[i])<>$F0 then
  253. Writeln(ptext,'offset',i,':$',hexstr(i,2*sizeof(pointer)),'"',pp[i],'"');
  254. end;
  255. {$endif EXTRA}
  256. procedure dump_wrong_size(p : pheap_mem_info;size : ptrint;var ptext : text);
  257. begin
  258. Writeln(ptext,'Marked memory at $',HexStr(ptrint(pointer(p)+sizeof(theap_mem_info)),2*sizeof(pointer)),' invalid');
  259. Writeln(ptext,'Wrong size : ',p^.size,' allocated ',size,' freed');
  260. dump_stack(ptext,get_caller_frame(get_frame));
  261. { the check is done to be sure that the procvar is not overwritten }
  262. if assigned(p^.extra_info) and
  263. (p^.extra_info^.check=$12345678) and
  264. assigned(p^.extra_info^.displayproc) then
  265. p^.extra_info^.displayproc(ptext,@p^.extra_info^.data);
  266. call_stack(p,ptext);
  267. end;
  268. function is_in_getmem_list (p : pheap_mem_info) : boolean;
  269. var
  270. i : ptrint;
  271. pp : pheap_mem_info;
  272. begin
  273. is_in_getmem_list:=false;
  274. pp:=heap_mem_root;
  275. i:=0;
  276. while pp<>nil do
  277. begin
  278. if ((pp^.sig<>$DEADBEEF) or usecrc) and
  279. ((pp^.sig<>calculate_sig(pp)) or not usecrc) and
  280. (pp^.sig <>$AAAAAAAA) then
  281. begin
  282. writeln(ptext^,'error in linked list of heap_mem_info');
  283. RunError(204);
  284. end;
  285. if pp=p then
  286. is_in_getmem_list:=true;
  287. pp:=pp^.previous;
  288. inc(i);
  289. if i>getmem_cnt-freemem_cnt then
  290. writeln(ptext^,'error in linked list of heap_mem_info');
  291. end;
  292. end;
  293. {*****************************************************************************
  294. TraceGetMem
  295. *****************************************************************************}
  296. Function TraceGetMem(size:ptrint):pointer;
  297. var
  298. allocsize,i : ptrint;
  299. oldbp,
  300. bp : pointer;
  301. pl : pdword;
  302. p : pointer;
  303. pp : pheap_mem_info;
  304. begin
  305. inc(getmem_size,size);
  306. inc(getmem8_size,((size+7) div 8)*8);
  307. { Do the real GetMem, but alloc also for the info block }
  308. allocsize:=size+sizeof(theap_mem_info)+extra_info_size;
  309. if add_tail then
  310. inc(allocsize,sizeof(ptrint));
  311. p:=SysGetMem(allocsize);
  312. pp:=pheap_mem_info(p);
  313. inc(p,sizeof(theap_mem_info));
  314. { Create the info block }
  315. pp^.sig:=$DEADBEEF;
  316. pp^.size:=size;
  317. pp^.extra_info_size:=extra_info_size;
  318. pp^.exact_info_size:=exact_info_size;
  319. {
  320. the end of the block contains:
  321. <tail> 4 bytes
  322. <extra_info> X bytes
  323. }
  324. if extra_info_size>0 then
  325. begin
  326. pp^.extra_info:=pointer(pp)+allocsize-extra_info_size;
  327. fillchar(pp^.extra_info^,extra_info_size,0);
  328. pp^.extra_info^.check:=$12345678;
  329. pp^.extra_info^.fillproc:=fill_extra_info_proc;
  330. pp^.extra_info^.displayproc:=display_extra_info_proc;
  331. if assigned(fill_extra_info_proc) then
  332. begin
  333. inside_trace_getmem:=true;
  334. fill_extra_info_proc(@pp^.extra_info^.data);
  335. inside_trace_getmem:=false;
  336. end;
  337. end
  338. else
  339. pp^.extra_info:=nil;
  340. if add_tail then
  341. begin
  342. pl:=pointer(pp)+allocsize-pp^.extra_info_size-sizeof(ptrint);
  343. pl^:=$DEADBEEF;
  344. end;
  345. { clear the memory }
  346. fillchar(p^,size,#255);
  347. { retrieve backtrace info }
  348. bp:=get_caller_frame(get_frame);
  349. for i:=1 to tracesize do
  350. begin
  351. pp^.calls[i]:=get_caller_addr(bp);
  352. oldbp:=bp;
  353. bp:=get_caller_frame(bp);
  354. if (bp<oldbp) or (bp>(StackBottom + StackLength)) then
  355. bp:=nil;
  356. end;
  357. { insert in the linked list }
  358. if heap_mem_root<>nil then
  359. heap_mem_root^.next:=pp;
  360. pp^.previous:=heap_mem_root;
  361. pp^.next:=nil;
  362. {$ifdef EXTRA}
  363. pp^.prev_valid:=heap_valid_last;
  364. heap_valid_last:=pp;
  365. if not assigned(heap_valid_first) then
  366. heap_valid_first:=pp;
  367. {$endif EXTRA}
  368. heap_mem_root:=pp;
  369. { must be changed before fill_extra_info is called
  370. because checkpointer can be called from within
  371. fill_extra_info PM }
  372. inc(getmem_cnt);
  373. { update the signature }
  374. if usecrc then
  375. pp^.sig:=calculate_sig(pp);
  376. TraceGetmem:=p;
  377. end;
  378. {*****************************************************************************
  379. TraceFreeMem
  380. *****************************************************************************}
  381. function TraceFreeMemSize(p:pointer;size:ptrint):ptrint;
  382. var
  383. i,ppsize : ptrint;
  384. bp : pointer;
  385. pp : pheap_mem_info;
  386. {$ifdef EXTRA}
  387. pp2 : pheap_mem_info;
  388. {$endif}
  389. extra_size : ptrint;
  390. begin
  391. if p=nil then
  392. begin
  393. TraceFreeMemSize:=0;
  394. exit;
  395. end;
  396. inc(freemem_size,size);
  397. inc(freemem8_size,((size+7) div 8)*8);
  398. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  399. ppsize:= size + sizeof(theap_mem_info)+pp^.extra_info_size;
  400. if add_tail then
  401. inc(ppsize,sizeof(ptrint));
  402. if not quicktrace then
  403. begin
  404. if not(is_in_getmem_list(pp)) then
  405. RunError(204);
  406. end;
  407. if (pp^.sig=$AAAAAAAA) and not usecrc then
  408. begin
  409. error_in_heap:=true;
  410. dump_already_free(pp,ptext^);
  411. if haltonerror then halt(1);
  412. end
  413. else if ((pp^.sig<>$DEADBEEF) or usecrc) and
  414. ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
  415. begin
  416. error_in_heap:=true;
  417. dump_error(pp,ptext^);
  418. {$ifdef EXTRA}
  419. dump_error(pp,error_file);
  420. {$endif EXTRA}
  421. { don't release anything in this case !! }
  422. if haltonerror then halt(1);
  423. exit;
  424. end
  425. else if pp^.size<>size then
  426. begin
  427. error_in_heap:=true;
  428. dump_wrong_size(pp,size,ptext^);
  429. {$ifdef EXTRA}
  430. dump_wrong_size(pp,size,error_file);
  431. {$endif EXTRA}
  432. if haltonerror then halt(1);
  433. { don't release anything in this case !! }
  434. exit;
  435. end;
  436. { save old values }
  437. extra_size:=pp^.extra_info_size;
  438. { now it is released !! }
  439. pp^.sig:=$AAAAAAAA;
  440. if not keepreleased then
  441. begin
  442. if pp^.next<>nil then
  443. pp^.next^.previous:=pp^.previous;
  444. if pp^.previous<>nil then
  445. pp^.previous^.next:=pp^.next;
  446. if pp=heap_mem_root then
  447. heap_mem_root:=heap_mem_root^.previous;
  448. end
  449. else
  450. begin
  451. bp:=get_caller_frame(get_frame);
  452. for i:=(tracesize div 2)+1 to tracesize do
  453. begin
  454. pp^.calls[i]:=get_caller_addr(bp);
  455. bp:=get_caller_frame(bp);
  456. end;
  457. end;
  458. inc(freemem_cnt);
  459. { clear the memory }
  460. fillchar(p^,size,#240); { $F0 will lead to GFP if used as pointer ! }
  461. { this way we keep all info about all released memory !! }
  462. if keepreleased then
  463. begin
  464. {$ifdef EXTRA}
  465. { We want to check if the memory was changed after release !! }
  466. pp^.release_sig:=calculate_release_sig(pp);
  467. if pp=heap_valid_last then
  468. begin
  469. heap_valid_last:=pp^.prev_valid;
  470. if pp=heap_valid_first then
  471. heap_valid_first:=nil;
  472. TraceFreememsize:=size;
  473. exit;
  474. end;
  475. pp2:=heap_valid_last;
  476. while assigned(pp2) do
  477. begin
  478. if pp2^.prev_valid=pp then
  479. begin
  480. pp2^.prev_valid:=pp^.prev_valid;
  481. if pp=heap_valid_first then
  482. heap_valid_first:=pp2;
  483. TraceFreememsize:=size;
  484. exit;
  485. end
  486. else
  487. pp2:=pp2^.prev_valid;
  488. end;
  489. {$endif EXTRA}
  490. TraceFreememsize:=size;
  491. exit;
  492. end;
  493. { release the normal memory at least }
  494. i:=SysFreeMemSize(pp,ppsize);
  495. { return the correct size }
  496. dec(i,sizeof(theap_mem_info)+extra_size);
  497. if add_tail then
  498. dec(i,sizeof(ptrint));
  499. TraceFreeMemSize:=i;
  500. end;
  501. function TraceMemSize(p:pointer):ptrint;
  502. var
  503. pp : pheap_mem_info;
  504. begin
  505. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  506. TraceMemSize:=pp^.size;
  507. end;
  508. function TraceFreeMem(p:pointer):ptrint;
  509. var
  510. l : ptrint;
  511. pp : pheap_mem_info;
  512. begin
  513. if p=nil then
  514. begin
  515. TraceFreeMem:=0;
  516. exit;
  517. end;
  518. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  519. l:=SysMemSize(pp);
  520. dec(l,sizeof(theap_mem_info)+pp^.extra_info_size);
  521. if add_tail then
  522. dec(l,sizeof(ptrint));
  523. { this can never happend normaly }
  524. if pp^.size>l then
  525. begin
  526. dump_wrong_size(pp,l,ptext^);
  527. {$ifdef EXTRA}
  528. dump_wrong_size(pp,l,error_file);
  529. {$endif EXTRA}
  530. end;
  531. TraceFreeMem:=TraceFreeMemSize(p,pp^.size);
  532. end;
  533. {*****************************************************************************
  534. ReAllocMem
  535. *****************************************************************************}
  536. function TraceReAllocMem(var p:pointer;size:ptrint):Pointer;
  537. var
  538. newP: pointer;
  539. allocsize,
  540. movesize,
  541. i : ptrint;
  542. bp : pointer;
  543. pl : pdword;
  544. pp : pheap_mem_info;
  545. oldsize,
  546. oldextrasize,
  547. oldexactsize : ptrint;
  548. old_fill_extra_info_proc : tfillextrainfoproc;
  549. old_display_extra_info_proc : tdisplayextrainfoproc;
  550. begin
  551. { Free block? }
  552. if size=0 then
  553. begin
  554. if p<>nil then
  555. TraceFreeMem(p);
  556. p:=nil;
  557. TraceReallocMem:=P;
  558. exit;
  559. end;
  560. { Allocate a new block? }
  561. if p=nil then
  562. begin
  563. p:=TraceGetMem(size);
  564. TraceReallocMem:=P;
  565. exit;
  566. end;
  567. { Resize block }
  568. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  569. { test block }
  570. if ((pp^.sig<>$DEADBEEF) or usecrc) and
  571. ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
  572. begin
  573. error_in_heap:=true;
  574. dump_error(pp,ptext^);
  575. {$ifdef EXTRA}
  576. dump_error(pp,error_file);
  577. {$endif EXTRA}
  578. { don't release anything in this case !! }
  579. if haltonerror then halt(1);
  580. exit;
  581. end;
  582. { save info }
  583. oldsize:=pp^.size;
  584. oldextrasize:=pp^.extra_info_size;
  585. oldexactsize:=pp^.exact_info_size;
  586. if pp^.extra_info_size>0 then
  587. begin
  588. old_fill_extra_info_proc:=pp^.extra_info^.fillproc;
  589. old_display_extra_info_proc:=pp^.extra_info^.displayproc;
  590. end;
  591. { Do the real ReAllocMem, but alloc also for the info block }
  592. allocsize:=size+sizeof(theap_mem_info)+pp^.extra_info_size;
  593. if add_tail then
  594. inc(allocsize,sizeof(ptrint));
  595. { Try to resize the block, if not possible we need to do a
  596. getmem, move data, freemem }
  597. if not SysTryResizeMem(pp,allocsize) then
  598. begin
  599. { get a new block }
  600. newP := TraceGetMem(size);
  601. { move the data }
  602. if newP <> nil then
  603. begin
  604. movesize:=TraceMemSize(p);
  605. {if the old size is larger than the new size,
  606. move only the new size}
  607. if movesize>size then
  608. movesize:=size;
  609. move(p^,newP^,movesize);
  610. end;
  611. { release p }
  612. traceFreeMem(p);
  613. { return the new pointer }
  614. p:=newp;
  615. traceReAllocMem := newp;
  616. exit;
  617. end;
  618. { Recreate the info block }
  619. pp^.sig:=$DEADBEEF;
  620. pp^.size:=size;
  621. pp^.extra_info_size:=oldextrasize;
  622. pp^.exact_info_size:=oldexactsize;
  623. { add the new extra_info and tail }
  624. if pp^.extra_info_size>0 then
  625. begin
  626. pp^.extra_info:=pointer(pp)+allocsize-pp^.extra_info_size;
  627. fillchar(pp^.extra_info^,extra_info_size,0);
  628. pp^.extra_info^.check:=$12345678;
  629. pp^.extra_info^.fillproc:=old_fill_extra_info_proc;
  630. pp^.extra_info^.displayproc:=old_display_extra_info_proc;
  631. if assigned(pp^.extra_info^.fillproc) then
  632. pp^.extra_info^.fillproc(@pp^.extra_info^.data);
  633. end
  634. else
  635. pp^.extra_info:=nil;
  636. if add_tail then
  637. begin
  638. pl:=pointer(pp)+allocsize-pp^.extra_info_size-sizeof(ptrint);
  639. pl^:=$DEADBEEF;
  640. end;
  641. { adjust like a freemem and then a getmem, so you get correct
  642. results in the summary display }
  643. inc(freemem_size,oldsize);
  644. inc(freemem8_size,((oldsize+7) div 8)*8);
  645. inc(getmem_size,size);
  646. inc(getmem8_size,((size+7) div 8)*8);
  647. { generate new backtrace }
  648. bp:=get_caller_frame(get_frame);
  649. for i:=1 to tracesize do
  650. begin
  651. pp^.calls[i]:=get_caller_addr(bp);
  652. bp:=get_caller_frame(bp);
  653. end;
  654. { regenerate signature }
  655. if usecrc then
  656. pp^.sig:=calculate_sig(pp);
  657. { return the pointer }
  658. p:=pointer(pp)+sizeof(theap_mem_info);
  659. TraceReAllocmem:=p;
  660. end;
  661. {*****************************************************************************
  662. Check pointer
  663. *****************************************************************************}
  664. {$ifndef Unix}
  665. {$S-}
  666. {$endif}
  667. {$ifdef go32v2}
  668. var
  669. __stklen : longword;external name '__stklen';
  670. __stkbottom : longword;external name '__stkbottom';
  671. edata : longword; external name 'edata';
  672. {$endif go32v2}
  673. {$ifdef linux}
  674. var
  675. etext: ptruint; external name '_etext';
  676. edata : ptruint; external name '_edata';
  677. eend : ptruint; external name '_end';
  678. {$endif}
  679. {$ifdef os2}
  680. (* Currently still EMX based - possibly to be changed in the future. *)
  681. var
  682. etext: ptruint; external name '_etext';
  683. edata : ptruint; external name '_edata';
  684. eend : ptruint; external name '_end';
  685. {$endif}
  686. {$ifdef win32}
  687. var
  688. sdata : ptruint; external name '__data_start__';
  689. edata : ptruint; external name '__data_end__';
  690. sbss : ptruint; external name '__bss_start__';
  691. ebss : ptruint; external name '__bss_end__';
  692. {$endif}
  693. procedure CheckPointer(p : pointer); [public, alias : 'FPC_CHECKPOINTER'];
  694. var
  695. i : ptrint;
  696. pp : pheap_mem_info;
  697. {$ifdef go32v2}
  698. get_ebp,stack_top : longword;
  699. data_end : longword;
  700. {$endif go32v2}
  701. label
  702. _exit;
  703. begin
  704. if p=nil then
  705. runerror(204);
  706. i:=0;
  707. {$ifdef go32v2}
  708. if ptruint(p)<$1000 then
  709. runerror(216);
  710. asm
  711. movl %ebp,get_ebp
  712. leal edata,%eax
  713. movl %eax,data_end
  714. end;
  715. stack_top:=__stkbottom+__stklen;
  716. { allow all between start of code and end of data }
  717. if ptruint(p)<=data_end then
  718. goto _exit;
  719. { stack can be above heap !! }
  720. if (ptruint(p)>=get_ebp) and (ptruint(p)<=stack_top) then
  721. goto _exit;
  722. {$endif go32v2}
  723. { I don't know where the stack is in other OS !! }
  724. {$ifdef win32}
  725. { inside stack ? }
  726. if (ptruint(p)>ptruint(get_frame)) and
  727. (ptruint(p)<Win32StackTop) then
  728. goto _exit;
  729. { inside data ? }
  730. if (ptruint(p)>=ptruint(@sdata)) and (ptruint(p)<ptruint(@edata)) then
  731. goto _exit;
  732. { inside bss ? }
  733. if (ptruint(p)>=ptruint(@sbss)) and (ptruint(p)<ptruint(@ebss)) then
  734. goto _exit;
  735. {$endif win32}
  736. {$IFDEF OS2}
  737. { inside stack ? }
  738. if (PtrUInt (P) > PtrUInt (Get_Frame)) and
  739. (PtrUInt (P) < StackTop) then
  740. goto _exit;
  741. { inside data or bss ? }
  742. if (PtrUInt (P) >= PtrUInt (@etext)) and (PtrUInt (P) < PtrUInt (@eend)) then
  743. goto _exit;
  744. {$ENDIF OS2}
  745. {$ifdef linux}
  746. { inside stack ? }
  747. if (ptruint(p)>ptruint(get_frame)) and
  748. (ptruint(p)<$c0000000) then //todo: 64bit!
  749. goto _exit;
  750. { inside data or bss ? }
  751. if (ptruint(p)>=ptruint(@etext)) and (ptruint(p)<ptruint(@eend)) then
  752. goto _exit;
  753. {$endif linux}
  754. { first try valid list faster }
  755. {$ifdef EXTRA}
  756. pp:=heap_valid_last;
  757. while pp<>nil do
  758. begin
  759. { inside this valid block ! }
  760. { we can be changing the extrainfo !! }
  761. if (ptruint(p)>=ptruint(pp)+sizeof(theap_mem_info){+extra_info_size}) and
  762. (ptruint(p)<=ptruint(pp)+sizeof(theap_mem_info)+extra_info_size+pp^.size) then
  763. begin
  764. { check allocated block }
  765. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  766. ((pp^.sig=calculate_sig(pp)) and usecrc) or
  767. { special case of the fill_extra_info call }
  768. ((pp=heap_valid_last) and usecrc and (pp^.sig=$DEADBEEF)
  769. and inside_trace_getmem) then
  770. goto _exit
  771. else
  772. begin
  773. writeln(ptext^,'corrupted heap_mem_info');
  774. dump_error(pp,ptext^);
  775. halt(1);
  776. end;
  777. end
  778. else
  779. pp:=pp^.prev_valid;
  780. inc(i);
  781. if i>getmem_cnt-freemem_cnt then
  782. begin
  783. writeln(ptext^,'error in linked list of heap_mem_info');
  784. halt(1);
  785. end;
  786. end;
  787. i:=0;
  788. {$endif EXTRA}
  789. pp:=heap_mem_root;
  790. while pp<>nil do
  791. begin
  792. { inside this block ! }
  793. if (ptruint(p)>=ptruint(pp)+sizeof(theap_mem_info)+ptruint(extra_info_size)) and
  794. (ptruint(p)<=ptruint(pp)+sizeof(theap_mem_info)+ptruint(extra_info_size)+ptruint(pp^.size)) then
  795. { allocated block }
  796. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  797. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  798. goto _exit
  799. else
  800. begin
  801. writeln(ptext^,'pointer $',hexstr(ptrint(p),2*sizeof(pointer)),' points into invalid memory block');
  802. dump_error(pp,ptext^);
  803. runerror(204);
  804. end;
  805. pp:=pp^.previous;
  806. inc(i);
  807. if i>getmem_cnt then
  808. begin
  809. writeln(ptext^,'error in linked list of heap_mem_info');
  810. halt(1);
  811. end;
  812. end;
  813. writeln(ptext^,'pointer $',hexstr(ptrint(p),2*sizeof(pointer)),' does not point to valid memory block');
  814. runerror(204);
  815. _exit:
  816. end;
  817. {*****************************************************************************
  818. Dump Heap
  819. *****************************************************************************}
  820. procedure dumpheap;
  821. var
  822. pp : pheap_mem_info;
  823. i : ptrint;
  824. ExpectedHeapFree : ptrint;
  825. status : TFPCHeapStatus;
  826. begin
  827. pp:=heap_mem_root;
  828. Writeln(ptext^,'Heap dump by heaptrc unit');
  829. Writeln(ptext^,getmem_cnt, ' memory blocks allocated : ',getmem_size,'/',getmem8_size);
  830. Writeln(ptext^,freemem_cnt,' memory blocks freed : ',freemem_size,'/',freemem8_size);
  831. Writeln(ptext^,getmem_cnt-freemem_cnt,' unfreed memory blocks : ',getmem_size-freemem_size);
  832. status:=SysGetFPCHeapStatus;
  833. Write(ptext^,'True heap size : ',status.CurrHeapSize);
  834. if EntryMemUsed > 0 then
  835. Writeln(ptext^,' (',EntryMemUsed,' used in System startup)')
  836. else
  837. Writeln(ptext^);
  838. Writeln(ptext^,'True free heap : ',status.CurrHeapFree);
  839. ExpectedHeapFree:=status.CurrHeapSize-(getmem8_size-freemem8_size)-
  840. (getmem_cnt-freemem_cnt)*(sizeof(theap_mem_info)+extra_info_size)-EntryMemUsed;
  841. If ExpectedHeapFree<>status.CurrHeapFree then
  842. Writeln(ptext^,'Should be : ',ExpectedHeapFree);
  843. i:=getmem_cnt-freemem_cnt;
  844. while pp<>nil do
  845. begin
  846. if i<0 then
  847. begin
  848. Writeln(ptext^,'Error in heap memory list');
  849. Writeln(ptext^,'More memory blocks than expected');
  850. exit;
  851. end;
  852. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  853. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  854. begin
  855. { this one was not released !! }
  856. if exitcode<>203 then
  857. call_stack(pp,ptext^);
  858. dec(i);
  859. end
  860. else if pp^.sig<>$AAAAAAAA then
  861. begin
  862. dump_error(pp,ptext^);
  863. {$ifdef EXTRA}
  864. dump_error(pp,error_file);
  865. {$endif EXTRA}
  866. error_in_heap:=true;
  867. end
  868. {$ifdef EXTRA}
  869. else if pp^.release_sig<>calculate_release_sig(pp) then
  870. begin
  871. dump_change_after(pp,ptext^);
  872. dump_change_after(pp,error_file);
  873. error_in_heap:=true;
  874. end
  875. {$endif EXTRA}
  876. ;
  877. pp:=pp^.previous;
  878. end;
  879. end;
  880. {*****************************************************************************
  881. AllocMem
  882. *****************************************************************************}
  883. function TraceAllocMem(size:ptrint):Pointer;
  884. begin
  885. TraceAllocMem:=SysAllocMem(size);
  886. end;
  887. {*****************************************************************************
  888. No specific tracing calls
  889. *****************************************************************************}
  890. function TraceGetHeapStatus:THeapStatus;
  891. begin
  892. TraceGetHeapStatus:=SysGetHeapStatus;
  893. end;
  894. function TraceGetFPCHeapStatus:TFPCHeapStatus;
  895. begin
  896. TraceGetFPCHeapStatus:=SysGetFPCHeapStatus;
  897. end;
  898. {*****************************************************************************
  899. Program Hooks
  900. *****************************************************************************}
  901. Procedure SetHeapTraceOutput(const name : string);
  902. var i : ptrint;
  903. begin
  904. if ptext<>@stderr then
  905. begin
  906. ptext:=@stderr;
  907. close(ownfile);
  908. end;
  909. assign(ownfile,name);
  910. {$I-}
  911. append(ownfile);
  912. if IOResult<>0 then
  913. Rewrite(ownfile);
  914. {$I+}
  915. ptext:=@ownfile;
  916. for i:=0 to Paramcount do
  917. write(ptext^,paramstr(i),' ');
  918. writeln(ptext^);
  919. end;
  920. procedure SetHeapExtraInfo( size : ptrint;fillproc : tfillextrainfoproc;displayproc : tdisplayextrainfoproc);
  921. begin
  922. { the total size must stay multiple of 8, also allocate 2 pointers for
  923. the fill and display procvars }
  924. exact_info_size:=size + sizeof(theap_extra_info);
  925. extra_info_size:=((exact_info_size+7) div 8)*8;
  926. fill_extra_info_proc:=fillproc;
  927. display_extra_info_proc:=displayproc;
  928. end;
  929. {*****************************************************************************
  930. Install MemoryManager
  931. *****************************************************************************}
  932. const
  933. TraceManager:TMemoryManager=(
  934. NeedLock : true;
  935. Getmem : @TraceGetMem;
  936. Freemem : @TraceFreeMem;
  937. FreememSize : @TraceFreeMemSize;
  938. AllocMem : @TraceAllocMem;
  939. ReAllocMem : @TraceReAllocMem;
  940. MemSize : @TraceMemSize;
  941. GetHeapStatus : @TraceGetHeapStatus;
  942. GetFPCHeapStatus : @TraceGetFPCHeapStatus;
  943. );
  944. procedure TraceInit;
  945. var
  946. initheapstatus : TFPCHeapStatus;
  947. begin
  948. initheapstatus:=SysGetFPCHeapStatus;
  949. EntryMemUsed:=initheapstatus.CurrHeapUsed;
  950. MakeCRC32Tbl;
  951. SetMemoryManager(TraceManager);
  952. ptext:=@stderr;
  953. if outputstr <> '' then
  954. SetHeapTraceOutput(outputstr);
  955. {$ifdef EXTRA}
  956. Assign(error_file,'heap.err');
  957. Rewrite(error_file);
  958. {$endif EXTRA}
  959. end;
  960. procedure TraceExit;
  961. begin
  962. { no dump if error
  963. because this gives long long listings }
  964. { clear inoutres, in case the program that quit didn't }
  965. ioresult;
  966. if (exitcode<>0) and (erroraddr<>nil) then
  967. begin
  968. Writeln(ptext^,'No heap dump by heaptrc unit');
  969. Writeln(ptext^,'Exitcode = ',exitcode);
  970. if ptext<>@stderr then
  971. begin
  972. ptext:=@stderr;
  973. close(ownfile);
  974. end;
  975. exit;
  976. end;
  977. if not error_in_heap then
  978. Dumpheap;
  979. if error_in_heap and (exitcode=0) then
  980. exitcode:=203;
  981. {$ifdef EXTRA}
  982. Close(error_file);
  983. {$endif EXTRA}
  984. if ptext<>@stderr then
  985. begin
  986. ptext:=@stderr;
  987. close(ownfile);
  988. end;
  989. end;
  990. {$ifdef win32}
  991. function GetEnvironmentStrings : pchar; stdcall;
  992. external 'kernel32' name 'GetEnvironmentStringsA';
  993. function FreeEnvironmentStrings(p : pchar) : longbool; stdcall;
  994. external 'kernel32' name 'FreeEnvironmentStringsA';
  995. Function GetEnv(envvar: string): string;
  996. var
  997. s : string;
  998. i : ptrint;
  999. hp,p : pchar;
  1000. begin
  1001. getenv:='';
  1002. p:=GetEnvironmentStrings;
  1003. hp:=p;
  1004. while hp^<>#0 do
  1005. begin
  1006. s:=strpas(hp);
  1007. i:=pos('=',s);
  1008. if upcase(copy(s,1,i-1))=upcase(envvar) then
  1009. begin
  1010. getenv:=copy(s,i+1,length(s)-i);
  1011. break;
  1012. end;
  1013. { next string entry}
  1014. hp:=hp+strlen(hp)+1;
  1015. end;
  1016. FreeEnvironmentStrings(p);
  1017. end;
  1018. {$else}
  1019. Function GetEnv(P:string):Pchar;
  1020. {
  1021. Searches the environment for a string with name p and
  1022. returns a pchar to it's value.
  1023. A pchar is used to accomodate for strings of length > 255
  1024. }
  1025. var
  1026. ep : ppchar;
  1027. i : ptrint;
  1028. found : boolean;
  1029. Begin
  1030. p:=p+'='; {Else HOST will also find HOSTNAME, etc}
  1031. ep:=envp;
  1032. found:=false;
  1033. if ep<>nil then
  1034. begin
  1035. while (not found) and (ep^<>nil) do
  1036. begin
  1037. found:=true;
  1038. for i:=1 to length(p) do
  1039. if p[i]<>ep^[i-1] then
  1040. begin
  1041. found:=false;
  1042. break;
  1043. end;
  1044. if not found then
  1045. inc(ep);
  1046. end;
  1047. end;
  1048. if found then
  1049. getenv:=ep^+length(p)
  1050. else
  1051. getenv:=nil;
  1052. end;
  1053. {$endif}
  1054. procedure LoadEnvironment;
  1055. var
  1056. i,j : ptrint;
  1057. s : string;
  1058. begin
  1059. s:=Getenv('HEAPTRC');
  1060. if pos('keepreleased',s)>0 then
  1061. keepreleased:=true;
  1062. if pos('disabled',s)>0 then
  1063. useheaptrace:=false;
  1064. if pos('nohalt',s)>0 then
  1065. haltonerror:=false;
  1066. i:=pos('log=',s);
  1067. if i>0 then
  1068. begin
  1069. outputstr:=copy(s,i+4,255);
  1070. j:=pos(' ',outputstr);
  1071. if j=0 then
  1072. j:=length(outputstr)+1;
  1073. delete(outputstr,j,255);
  1074. end;
  1075. end;
  1076. Initialization
  1077. LoadEnvironment;
  1078. { heaptrc can be disabled from the environment }
  1079. if useheaptrace then
  1080. TraceInit;
  1081. finalization
  1082. if useheaptrace then
  1083. TraceExit;
  1084. end.