heaptrc.pp 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553
  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. {$inline on}
  14. {$ifdef FPC_HEAPTRC_EXTRA}
  15. {$define EXTRA}
  16. {$inline off}
  17. {$endif FPC_HEAPTRC_EXTRA}
  18. {$checkpointer off}
  19. {$TYPEDADDRESS on}
  20. {$if defined(win32) or defined(wince)}
  21. {$define windows}
  22. {$endif}
  23. Procedure DumpHeap;
  24. Procedure DumpHeap(SkipIfNoLeaks : Boolean);
  25. { define EXTRA to add more
  26. tests :
  27. - keep all memory after release and
  28. check by CRC value if not changed after release
  29. WARNING this needs extremely much memory (PM) }
  30. type
  31. tFillExtraInfoProc = procedure(p : pointer);
  32. tdisplayextrainfoProc = procedure (var ptext : text;p : pointer);
  33. { Allows to add info pre memory block, see ppheap.pas of the compiler
  34. for example source }
  35. procedure SetHeapExtraInfo(size : ptruint;fillproc : tfillextrainfoproc;displayproc : tdisplayextrainfoproc);
  36. { Redirection of the output to a file }
  37. procedure SetHeapTraceOutput(const name : string);overload;
  38. procedure SetHeapTraceOutput(var ATextOutput : Text);overload;
  39. procedure CheckPointer(p : pointer);
  40. const
  41. { tracing level
  42. splitted in two if memory is released !! }
  43. {$ifdef EXTRA}
  44. tracesize = 16;
  45. {$else EXTRA}
  46. tracesize = 8;
  47. {$endif EXTRA}
  48. { install heaptrc memorymanager }
  49. useheaptrace : boolean=true;
  50. { less checking }
  51. quicktrace : boolean=true;
  52. { calls halt() on error by default !! }
  53. HaltOnError : boolean = true;
  54. { Halt on exit if any memory was not freed }
  55. HaltOnNotReleased : boolean = false;
  56. { set this to true if you suspect that memory
  57. is freed several times }
  58. {$ifdef EXTRA}
  59. keepreleased : boolean=true;
  60. {$else EXTRA}
  61. keepreleased : boolean=false;
  62. {$endif EXTRA}
  63. { add a small footprint at the end of memory blocks, this
  64. can check for memory overwrites at the end of a block }
  65. add_tail : boolean = true;
  66. { put crc in sig
  67. this allows to test for writing into that part }
  68. usecrc : boolean = true;
  69. printleakedblock: boolean = false;
  70. printfaultyblock: boolean = false;
  71. maxprintedblocklength: integer = 128;
  72. GlobalSkipIfNoLeaks : Boolean = False;
  73. implementation
  74. const
  75. { allows to add custom info in heap_mem_info, this is the size that will
  76. be allocated for this information }
  77. extra_info_size : ptruint = 0;
  78. exact_info_size : ptruint = 0;
  79. EntryMemUsed : ptruint = 0;
  80. { function to fill this info up }
  81. fill_extra_info_proc : TFillExtraInfoProc = nil;
  82. display_extra_info_proc : TDisplayExtraInfoProc = nil;
  83. { indicates where the output will be redirected }
  84. { only set using environment variables }
  85. outputstr : shortstring = '';
  86. type
  87. pheap_extra_info = ^theap_extra_info;
  88. theap_extra_info = record
  89. check : cardinal; { used to check if the procvar is still valid }
  90. fillproc : tfillextrainfoProc;
  91. displayproc : tdisplayextrainfoProc;
  92. data : record
  93. end;
  94. end;
  95. ppheap_mem_info = ^pheap_mem_info;
  96. pheap_mem_info = ^theap_mem_info;
  97. { warning the size of theap_mem_info
  98. must be a multiple of 8
  99. because otherwise you will get
  100. problems when releasing the usual memory part !!
  101. sizeof(theap_mem_info = 16+tracesize*4 so
  102. tracesize must be even !! PM }
  103. theap_mem_info = record
  104. previous,
  105. next : pheap_mem_info;
  106. todolist : ppheap_mem_info;
  107. todonext : pheap_mem_info;
  108. size : ptruint;
  109. sig : longword;
  110. {$ifdef EXTRA}
  111. release_sig : longword;
  112. prev_valid : pheap_mem_info;
  113. {$endif EXTRA}
  114. calls : array [1..tracesize] of codepointer;
  115. exact_info_size : word;
  116. extra_info_size : word;
  117. extra_info : pheap_extra_info;
  118. end;
  119. pheap_info = ^theap_info;
  120. theap_info = record
  121. {$ifdef EXTRA}
  122. heap_valid_first,
  123. heap_valid_last : pheap_mem_info;
  124. {$endif EXTRA}
  125. heap_mem_root : pheap_mem_info;
  126. heap_free_todo : pheap_mem_info;
  127. getmem_cnt,
  128. freemem_cnt : ptruint;
  129. getmem_size,
  130. freemem_size : ptruint;
  131. getmem8_size,
  132. freemem8_size : ptruint;
  133. error_in_heap : boolean;
  134. inside_trace_getmem : boolean;
  135. end;
  136. var
  137. useownfile, useowntextoutput : boolean;
  138. ownfile : text;
  139. {$ifdef EXTRA}
  140. error_file : text;
  141. {$endif EXTRA}
  142. main_orig_todolist: ppheap_mem_info;
  143. main_relo_todolist: ppheap_mem_info;
  144. orphaned_info: theap_info;
  145. todo_lock: trtlcriticalsection;
  146. textoutput : ^text;
  147. threadvar
  148. heap_info: theap_info;
  149. {*****************************************************************************
  150. Crc 32
  151. *****************************************************************************}
  152. var
  153. Crc32Tbl : array[0..255] of longword;
  154. procedure MakeCRC32Tbl;
  155. var
  156. crc : longword;
  157. i,n : byte;
  158. begin
  159. for i:=0 to 255 do
  160. begin
  161. crc:=i;
  162. for n:=1 to 8 do
  163. if odd(crc) then
  164. crc:=(crc shr 1) xor $edb88320
  165. else
  166. crc:=crc shr 1;
  167. Crc32Tbl[i]:=crc;
  168. end;
  169. end;
  170. Function UpdateCrc32(InitCrc:longword;var InBuf;InLen:ptruint):longword;
  171. var
  172. i : ptruint;
  173. p : pchar;
  174. begin
  175. p:=@InBuf;
  176. for i:=1 to InLen do
  177. begin
  178. InitCrc:=Crc32Tbl[byte(InitCrc) xor byte(p^)] xor (InitCrc shr 8);
  179. inc(p);
  180. end;
  181. UpdateCrc32:=InitCrc;
  182. end;
  183. Function calculate_sig(p : pheap_mem_info) : longword;
  184. var
  185. crc : longword;
  186. pl : pptruint;
  187. begin
  188. crc:=cardinal($ffffffff);
  189. crc:=UpdateCrc32(crc,p^.size,sizeof(ptruint));
  190. crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(ptruint));
  191. if p^.extra_info_size>0 then
  192. crc:=UpdateCrc32(crc,p^.extra_info^,p^.exact_info_size);
  193. if add_tail then
  194. begin
  195. { Check also 4 bytes just after allocation !! }
  196. pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info)+p^.size;
  197. crc:=UpdateCrc32(crc,pl^,sizeof(ptruint));
  198. end;
  199. calculate_sig:=crc;
  200. end;
  201. {$ifdef EXTRA}
  202. Function calculate_release_sig(p : pheap_mem_info) : longword;
  203. var
  204. crc : longword;
  205. pl : pptruint;
  206. begin
  207. crc:=$ffffffff;
  208. crc:=UpdateCrc32(crc,p^.size,sizeof(ptruint));
  209. crc:=UpdateCrc32(crc,p^.calls,tracesize*sizeof(ptruint));
  210. if p^.extra_info_size>0 then
  211. crc:=UpdateCrc32(crc,p^.extra_info^,p^.exact_info_size);
  212. { Check the whole of the whole allocation }
  213. pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info);
  214. crc:=UpdateCrc32(crc,pl^,p^.size);
  215. { Check also 4 bytes just after allocation !! }
  216. if add_tail then
  217. begin
  218. { Check also 4 bytes just after allocation !! }
  219. pl:=pointer(p)+p^.extra_info_size+sizeof(theap_mem_info)+p^.size;
  220. crc:=UpdateCrc32(crc,pl^,sizeof(ptruint));
  221. end;
  222. calculate_release_sig:=crc;
  223. end;
  224. {$endif EXTRA}
  225. {*****************************************************************************
  226. Helpers
  227. *****************************************************************************}
  228. function InternalFreeMemSize(loc_info: pheap_info; p: pointer; pp: pheap_mem_info;
  229. size: ptruint; release_todo_lock: boolean): ptruint; forward;
  230. function TraceFreeMem(p: pointer): ptruint; forward;
  231. procedure printhex(p : pointer; const size : PtrUInt; var ptext : text);
  232. var s: PtrUInt;
  233. i: Integer;
  234. begin
  235. s := size;
  236. if s > maxprintedblocklength then
  237. s := maxprintedblocklength;
  238. for i:=0 to s-1 do
  239. write(ptext, hexstr(pbyte(p + i)^,2));
  240. if size > maxprintedblocklength then
  241. writeln(ptext,'.. - ')
  242. else
  243. writeln(ptext, ' - ');
  244. for i:=0 to s-1 do
  245. if pchar(p + sizeof(theap_mem_info) + i)^ < ' ' then
  246. write(ptext, ' ')
  247. else
  248. write(ptext, pchar(p + i)^);
  249. if size > maxprintedblocklength then
  250. writeln(ptext,'..')
  251. else
  252. writeln(ptext);
  253. end;
  254. procedure call_stack(pp : pheap_mem_info;var ptext : text);
  255. var
  256. i : ptruint;
  257. s: PtrUInt;
  258. begin
  259. writeln(ptext,'Call trace for block $',hexstr(pointer(pp)+sizeof(theap_mem_info)),' size ',pp^.size);
  260. if printleakedblock then
  261. begin
  262. write(ptext, 'Block content: ');
  263. printhex(pointer(pp) + sizeof(theap_mem_info), pp^.size, ptext);
  264. end;
  265. for i:=1 to tracesize do
  266. if pp^.calls[i]<>nil then
  267. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  268. { the check is done to be sure that the procvar is not overwritten }
  269. if assigned(pp^.extra_info) and
  270. (pp^.extra_info^.check=$12345678) and
  271. assigned(pp^.extra_info^.displayproc) then
  272. pp^.extra_info^.displayproc(ptext,@pp^.extra_info^.data);
  273. end;
  274. procedure call_free_stack(pp : pheap_mem_info;var ptext : text);
  275. var
  276. i : ptruint;
  277. begin
  278. writeln(ptext,'Call trace for block at $',hexstr(pointer(pp)+sizeof(theap_mem_info)),' size ',pp^.size);
  279. for i:=1 to tracesize div 2 do
  280. if pp^.calls[i]<>nil then
  281. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  282. writeln(ptext,' was released at ');
  283. for i:=(tracesize div 2)+1 to tracesize do
  284. if pp^.calls[i]<>nil then
  285. writeln(ptext,BackTraceStrFunc(pp^.calls[i]));
  286. { the check is done to be sure that the procvar is not overwritten }
  287. if assigned(pp^.extra_info) and
  288. (pp^.extra_info^.check=$12345678) and
  289. assigned(pp^.extra_info^.displayproc) then
  290. pp^.extra_info^.displayproc(ptext,@pp^.extra_info^.data);
  291. end;
  292. procedure dump_already_free(p : pheap_mem_info;var ptext : text);
  293. begin
  294. Writeln(ptext,'Marked memory at $',HexStr(pointer(p)+sizeof(theap_mem_info)),' released');
  295. call_free_stack(p,ptext);
  296. Writeln(ptext,'freed again at');
  297. dump_stack(ptext,1);
  298. end;
  299. procedure dump_error(p : pheap_mem_info;var ptext : text);
  300. begin
  301. Writeln(ptext,'Marked memory at $',HexStr(pointer(p)+sizeof(theap_mem_info)),' invalid');
  302. Writeln(ptext,'Wrong signature $',hexstr(p^.sig,8),' instead of ',hexstr(calculate_sig(p),8));
  303. if printfaultyblock then
  304. begin
  305. write(ptext, 'Block content: ');
  306. printhex(pointer(p) + sizeof(theap_mem_info), p^.size, ptext);
  307. end;
  308. dump_stack(ptext,1);
  309. end;
  310. {$ifdef EXTRA}
  311. procedure dump_change_after(p : pheap_mem_info;var ptext : text);
  312. var pp : pchar;
  313. i : ptruint;
  314. begin
  315. Writeln(ptext,'Marked memory at $',HexStr(pointer(p)+sizeof(theap_mem_info)),' invalid');
  316. Writeln(ptext,'Wrong release CRC $',hexstr(p^.release_sig,8),' instead of ',hexstr(calculate_release_sig(p),8));
  317. Writeln(ptext,'This memory was changed after call to freemem !');
  318. call_free_stack(p,ptext);
  319. pp:=pointer(p)+sizeof(theap_mem_info);
  320. for i:=0 to p^.size-1 do
  321. if byte(pp[i])<>$F0 then
  322. Writeln(ptext,'offset',i,':$',hexstr(i,2*sizeof(pointer)),'"',pp[i],'"');
  323. end;
  324. {$endif EXTRA}
  325. procedure dump_wrong_size(p : pheap_mem_info;size : ptruint;var ptext : text);
  326. begin
  327. Writeln(ptext,'Marked memory at $',HexStr(pointer(p)+sizeof(theap_mem_info)),' invalid');
  328. Writeln(ptext,'Wrong size : ',p^.size,' allocated ',size,' freed');
  329. dump_stack(ptext,1);
  330. { the check is done to be sure that the procvar is not overwritten }
  331. if assigned(p^.extra_info) and
  332. (p^.extra_info^.check=$12345678) and
  333. assigned(p^.extra_info^.displayproc) then
  334. p^.extra_info^.displayproc(ptext,@p^.extra_info^.data);
  335. call_stack(p,ptext);
  336. end;
  337. function is_in_getmem_list (loc_info: pheap_info; p : pheap_mem_info) : boolean;
  338. var
  339. i : ptruint;
  340. pp : pheap_mem_info;
  341. begin
  342. is_in_getmem_list:=false;
  343. pp:=loc_info^.heap_mem_root;
  344. i:=0;
  345. while pp<>nil do
  346. begin
  347. if ((pp^.sig<>$DEADBEEF) or usecrc) and
  348. ((pp^.sig<>calculate_sig(pp)) or not usecrc) and
  349. (pp^.sig <>$AAAAAAAA) then
  350. begin
  351. if useownfile then
  352. writeln(ownfile,'error in linked list of heap_mem_info')
  353. else
  354. writeln(textoutput^,'error in linked list of heap_mem_info');
  355. RunError(204);
  356. end;
  357. if pp=p then
  358. is_in_getmem_list:=true;
  359. pp:=pp^.previous;
  360. inc(i);
  361. if i>loc_info^.getmem_cnt-loc_info^.freemem_cnt then
  362. if useownfile then
  363. writeln(ownfile,'error in linked list of heap_mem_info')
  364. else
  365. writeln(textoutput^,'error in linked list of heap_mem_info');
  366. end;
  367. end;
  368. procedure finish_heap_free_todo_list(loc_info: pheap_info);
  369. var
  370. bp: pointer;
  371. pp: pheap_mem_info;
  372. list: ppheap_mem_info;
  373. begin
  374. list := @loc_info^.heap_free_todo;
  375. repeat
  376. pp := list^;
  377. list^ := list^^.todonext;
  378. bp := pointer(pp)+sizeof(theap_mem_info);
  379. InternalFreeMemSize(loc_info,bp,pp,pp^.size,false);
  380. until list^ = nil;
  381. end;
  382. procedure try_finish_heap_free_todo_list(loc_info: pheap_info);
  383. begin
  384. if loc_info^.heap_free_todo <> nil then
  385. begin
  386. entercriticalsection(todo_lock);
  387. finish_heap_free_todo_list(loc_info);
  388. leavecriticalsection(todo_lock);
  389. end;
  390. end;
  391. {*****************************************************************************
  392. TraceGetMem
  393. *****************************************************************************}
  394. Function TraceGetMem(size:ptruint):pointer;
  395. var
  396. allocsize : ptruint;
  397. pl : pdword;
  398. p : pointer;
  399. pp : pheap_mem_info;
  400. loc_info: pheap_info;
  401. begin
  402. loc_info := @heap_info;
  403. try_finish_heap_free_todo_list(loc_info);
  404. inc(loc_info^.getmem_size,size);
  405. inc(loc_info^.getmem8_size,(size+7) and not 7);
  406. { Do the real GetMem, but alloc also for the info block }
  407. {$ifdef cpuarm}
  408. allocsize:=(size + 3) and not 3+sizeof(theap_mem_info)+extra_info_size;
  409. {$else cpuarm}
  410. allocsize:=size+sizeof(theap_mem_info)+extra_info_size;
  411. {$endif cpuarm}
  412. if add_tail then
  413. inc(allocsize,sizeof(ptruint));
  414. { if ReturnNilIfGrowHeapFails is true
  415. SysGetMem can return nil }
  416. p:=SysGetMem(allocsize);
  417. if (p=nil) then
  418. begin
  419. TraceGetMem:=nil;
  420. exit;
  421. end;
  422. pp:=pheap_mem_info(p);
  423. inc(p,sizeof(theap_mem_info));
  424. { Create the info block }
  425. pp^.sig:=$DEADBEEF;
  426. pp^.todolist:=@loc_info^.heap_free_todo;
  427. pp^.todonext:=nil;
  428. pp^.size:=size;
  429. pp^.extra_info_size:=extra_info_size;
  430. pp^.exact_info_size:=exact_info_size;
  431. {
  432. the end of the block contains:
  433. <tail> 4 bytes
  434. <extra_info> X bytes
  435. }
  436. if extra_info_size>0 then
  437. begin
  438. pp^.extra_info:=pointer(pp)+allocsize-extra_info_size;
  439. fillchar(pp^.extra_info^,extra_info_size,0);
  440. pp^.extra_info^.check:=$12345678;
  441. pp^.extra_info^.fillproc:=fill_extra_info_proc;
  442. pp^.extra_info^.displayproc:=display_extra_info_proc;
  443. if assigned(fill_extra_info_proc) then
  444. begin
  445. loc_info^.inside_trace_getmem:=true;
  446. fill_extra_info_proc(@pp^.extra_info^.data);
  447. loc_info^.inside_trace_getmem:=false;
  448. end;
  449. end
  450. else
  451. pp^.extra_info:=nil;
  452. if add_tail then
  453. begin
  454. pl:=pointer(pp)+allocsize-pp^.extra_info_size-sizeof(ptruint);
  455. unaligned(pl^):=$DEADBEEF;
  456. end;
  457. { clear the memory }
  458. fillchar(p^,size,#255);
  459. { retrieve backtrace info }
  460. CaptureBacktrace(1,tracesize-1,@pp^.calls[1]);
  461. { insert in the linked list }
  462. if loc_info^.heap_mem_root<>nil then
  463. loc_info^.heap_mem_root^.next:=pp;
  464. pp^.previous:=loc_info^.heap_mem_root;
  465. pp^.next:=nil;
  466. {$ifdef EXTRA}
  467. pp^.prev_valid:=loc_info^.heap_valid_last;
  468. loc_info^.heap_valid_last:=pp;
  469. if not assigned(loc_info^.heap_valid_first) then
  470. loc_info^.heap_valid_first:=pp;
  471. {$endif EXTRA}
  472. loc_info^.heap_mem_root:=pp;
  473. { must be changed before fill_extra_info is called
  474. because checkpointer can be called from within
  475. fill_extra_info PM }
  476. inc(loc_info^.getmem_cnt);
  477. { update the signature }
  478. if usecrc then
  479. pp^.sig:=calculate_sig(pp);
  480. TraceGetmem:=p;
  481. end;
  482. {*****************************************************************************
  483. TraceFreeMem
  484. *****************************************************************************}
  485. function CheckFreeMemSize(loc_info: pheap_info; pp: pheap_mem_info;
  486. size, ppsize: ptruint): boolean; inline;
  487. var
  488. ptext : ^text;
  489. {$ifdef EXTRA}
  490. pp2 : pheap_mem_info;
  491. {$endif}
  492. begin
  493. if useownfile then
  494. ptext:=@ownfile
  495. else
  496. ptext:=textoutput;
  497. inc(loc_info^.freemem_size,size);
  498. inc(loc_info^.freemem8_size,(size+7) and not 7);
  499. if not quicktrace then
  500. begin
  501. if not(is_in_getmem_list(loc_info, pp)) then
  502. RunError(204);
  503. end;
  504. if (pp^.sig=$AAAAAAAA) and not usecrc then
  505. begin
  506. loc_info^.error_in_heap:=true;
  507. dump_already_free(pp,ptext^);
  508. if haltonerror then halt(1);
  509. end
  510. else if ((pp^.sig<>$DEADBEEF) or usecrc) and
  511. ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
  512. begin
  513. loc_info^.error_in_heap:=true;
  514. dump_error(pp,ptext^);
  515. {$ifdef EXTRA}
  516. dump_error(pp,error_file);
  517. {$endif EXTRA}
  518. { don't release anything in this case !! }
  519. if haltonerror then halt(1);
  520. exit;
  521. end
  522. else if pp^.size<>size then
  523. begin
  524. loc_info^.error_in_heap:=true;
  525. dump_wrong_size(pp,size,ptext^);
  526. {$ifdef EXTRA}
  527. dump_wrong_size(pp,size,error_file);
  528. {$endif EXTRA}
  529. if haltonerror then halt(1);
  530. { don't release anything in this case !! }
  531. exit;
  532. end;
  533. { now it is released !! }
  534. pp^.sig:=$AAAAAAAA;
  535. if not keepreleased then
  536. begin
  537. if pp^.next<>nil then
  538. pp^.next^.previous:=pp^.previous;
  539. if pp^.previous<>nil then
  540. pp^.previous^.next:=pp^.next;
  541. if pp=loc_info^.heap_mem_root then
  542. loc_info^.heap_mem_root:=loc_info^.heap_mem_root^.previous;
  543. end
  544. else
  545. CaptureBacktrace(1,(tracesize div 2)-1,@pp^.calls[(tracesize div 2)+1]);
  546. inc(loc_info^.freemem_cnt);
  547. { clear the memory, $F0 will lead to GFP if used as pointer ! }
  548. fillchar((pointer(pp)+sizeof(theap_mem_info))^,size,#240);
  549. { this way we keep all info about all released memory !! }
  550. if keepreleased then
  551. begin
  552. {$ifdef EXTRA}
  553. { We want to check if the memory was changed after release !! }
  554. pp^.release_sig:=calculate_release_sig(pp);
  555. if pp=loc_info^.heap_valid_last then
  556. begin
  557. loc_info^.heap_valid_last:=pp^.prev_valid;
  558. if pp=loc_info^.heap_valid_first then
  559. loc_info^.heap_valid_first:=nil;
  560. exit(false);
  561. end;
  562. pp2:=loc_info^.heap_valid_last;
  563. while assigned(pp2) do
  564. begin
  565. if pp2^.prev_valid=pp then
  566. begin
  567. pp2^.prev_valid:=pp^.prev_valid;
  568. if pp=loc_info^.heap_valid_first then
  569. loc_info^.heap_valid_first:=pp2;
  570. exit(false);
  571. end
  572. else
  573. pp2:=pp2^.prev_valid;
  574. end;
  575. {$endif EXTRA}
  576. exit(false);
  577. end;
  578. CheckFreeMemSize:=true;
  579. end;
  580. function InternalFreeMemSize(loc_info: pheap_info; p: pointer; pp: pheap_mem_info;
  581. size: ptruint; release_todo_lock: boolean): ptruint;
  582. var
  583. i,ppsize : ptruint;
  584. extra_size: ptruint;
  585. release_mem: boolean;
  586. begin
  587. { save old values }
  588. extra_size:=pp^.extra_info_size;
  589. ppsize:= size+sizeof(theap_mem_info)+pp^.extra_info_size;
  590. if add_tail then
  591. inc(ppsize,sizeof(ptruint));
  592. { do various checking }
  593. release_mem := CheckFreeMemSize(loc_info, pp, size, ppsize);
  594. if release_todo_lock then
  595. leavecriticalsection(todo_lock);
  596. if release_mem then
  597. begin
  598. { release the normal memory at least }
  599. i:=SysFreeMemSize(pp,ppsize);
  600. { return the correct size }
  601. dec(i,sizeof(theap_mem_info)+extra_size);
  602. if add_tail then
  603. dec(i,sizeof(ptruint));
  604. InternalFreeMemSize:=i;
  605. end else
  606. InternalFreeMemSize:=size;
  607. end;
  608. function TraceFreeMemSize(p:pointer;size:ptruint):ptruint;
  609. var
  610. loc_info: pheap_info;
  611. pp: pheap_mem_info;
  612. release_lock: boolean;
  613. begin
  614. if p=nil then
  615. begin
  616. TraceFreeMemSize:=0;
  617. exit;
  618. end;
  619. loc_info:=@heap_info;
  620. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  621. release_lock:=false;
  622. if @loc_info^.heap_free_todo <> pp^.todolist then
  623. begin
  624. if pp^.todolist = main_orig_todolist then
  625. pp^.todolist := main_relo_todolist;
  626. entercriticalsection(todo_lock);
  627. release_lock:=true;
  628. if pp^.todolist = @orphaned_info.heap_free_todo then
  629. begin
  630. loc_info := @orphaned_info;
  631. end else
  632. if pp^.todolist <> @loc_info^.heap_free_todo then
  633. begin
  634. { allocated in different heap, push to that todolist }
  635. pp^.todonext := pp^.todolist^;
  636. pp^.todolist^ := pp;
  637. TraceFreeMemSize := pp^.size;
  638. leavecriticalsection(todo_lock);
  639. exit;
  640. end;
  641. end;
  642. TraceFreeMemSize:=InternalFreeMemSize(loc_info,p,pp,size,release_lock);
  643. end;
  644. function TraceMemSize(p:pointer):ptruint;
  645. var
  646. pp : pheap_mem_info;
  647. begin
  648. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  649. TraceMemSize:=pp^.size;
  650. end;
  651. function TraceFreeMem(p:pointer):ptruint;
  652. var
  653. l : ptruint;
  654. pp : pheap_mem_info;
  655. begin
  656. if p=nil then
  657. begin
  658. TraceFreeMem:=0;
  659. exit;
  660. end;
  661. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  662. l:=SysMemSize(pp);
  663. dec(l,sizeof(theap_mem_info)+pp^.extra_info_size);
  664. if add_tail then
  665. dec(l,sizeof(ptruint));
  666. { this can never happend normaly }
  667. if pp^.size>l then
  668. begin
  669. if useownfile then
  670. dump_wrong_size(pp,l,ownfile)
  671. else
  672. dump_wrong_size(pp,l,textoutput^);
  673. {$ifdef EXTRA}
  674. dump_wrong_size(pp,l,error_file);
  675. {$endif EXTRA}
  676. end;
  677. TraceFreeMem:=TraceFreeMemSize(p,pp^.size);
  678. end;
  679. {*****************************************************************************
  680. ReAllocMem
  681. *****************************************************************************}
  682. function TraceReAllocMem(var p:pointer;size:ptruint):Pointer;
  683. var
  684. newP: pointer;
  685. allocsize,
  686. movesize : ptruint;
  687. pl : pdword;
  688. pp : pheap_mem_info;
  689. oldsize,
  690. oldextrasize,
  691. oldexactsize : ptruint;
  692. old_fill_extra_info_proc : tfillextrainfoproc;
  693. old_display_extra_info_proc : tdisplayextrainfoproc;
  694. loc_info: pheap_info;
  695. begin
  696. { Free block? }
  697. if size=0 then
  698. begin
  699. if p<>nil then
  700. TraceFreeMem(p);
  701. p:=nil;
  702. TraceReallocMem:=P;
  703. exit;
  704. end;
  705. { Allocate a new block? }
  706. if p=nil then
  707. begin
  708. p:=TraceGetMem(size);
  709. TraceReallocMem:=P;
  710. exit;
  711. end;
  712. { Resize block }
  713. loc_info:=@heap_info;
  714. pp:=pheap_mem_info(p-sizeof(theap_mem_info));
  715. { test block }
  716. if ((pp^.sig<>$DEADBEEF) or usecrc) and
  717. ((pp^.sig<>calculate_sig(pp)) or not usecrc) then
  718. begin
  719. loc_info^.error_in_heap:=true;
  720. if useownfile then
  721. dump_error(pp,ownfile)
  722. else
  723. dump_error(pp,textoutput^);
  724. {$ifdef EXTRA}
  725. dump_error(pp,error_file);
  726. {$endif EXTRA}
  727. { don't release anything in this case !! }
  728. if haltonerror then halt(1);
  729. exit;
  730. end;
  731. { save info }
  732. oldsize:=pp^.size;
  733. oldextrasize:=pp^.extra_info_size;
  734. oldexactsize:=pp^.exact_info_size;
  735. if pp^.extra_info_size>0 then
  736. begin
  737. old_fill_extra_info_proc:=pp^.extra_info^.fillproc;
  738. old_display_extra_info_proc:=pp^.extra_info^.displayproc;
  739. end;
  740. { Do the real ReAllocMem, but alloc also for the info block }
  741. {$ifdef cpuarm}
  742. allocsize:=(size + 3) and not 3+sizeof(theap_mem_info)+pp^.extra_info_size;
  743. {$else cpuarm}
  744. allocsize:=size+sizeof(theap_mem_info)+pp^.extra_info_size;
  745. {$endif cpuarm}
  746. if add_tail then
  747. inc(allocsize,sizeof(ptruint));
  748. { Try to resize the block, if not possible we need to do a
  749. getmem, move data, freemem }
  750. if not SysTryResizeMem(pp,allocsize) then
  751. begin
  752. { get a new block }
  753. newP := TraceGetMem(size);
  754. { move the data }
  755. if newP <> nil then
  756. begin
  757. movesize:=TraceMemSize(p);
  758. {if the old size is larger than the new size,
  759. move only the new size}
  760. if movesize>size then
  761. movesize:=size;
  762. move(p^,newP^,movesize);
  763. end;
  764. { release p }
  765. traceFreeMem(p);
  766. { return the new pointer }
  767. p:=newp;
  768. traceReAllocMem := newp;
  769. exit;
  770. end;
  771. { Recreate the info block }
  772. pp^.sig:=$DEADBEEF;
  773. pp^.size:=size;
  774. pp^.extra_info_size:=oldextrasize;
  775. pp^.exact_info_size:=oldexactsize;
  776. { add the new extra_info and tail }
  777. if pp^.extra_info_size>0 then
  778. begin
  779. pp^.extra_info:=pointer(pp)+allocsize-pp^.extra_info_size;
  780. fillchar(pp^.extra_info^,extra_info_size,0);
  781. pp^.extra_info^.check:=$12345678;
  782. pp^.extra_info^.fillproc:=old_fill_extra_info_proc;
  783. pp^.extra_info^.displayproc:=old_display_extra_info_proc;
  784. if assigned(pp^.extra_info^.fillproc) then
  785. pp^.extra_info^.fillproc(@pp^.extra_info^.data);
  786. end
  787. else
  788. pp^.extra_info:=nil;
  789. if add_tail then
  790. begin
  791. pl:=pointer(pp)+allocsize-pp^.extra_info_size-sizeof(ptruint);
  792. unaligned(pl^):=$DEADBEEF;
  793. end;
  794. { adjust like a freemem and then a getmem, so you get correct
  795. results in the summary display }
  796. inc(loc_info^.freemem_size,oldsize);
  797. inc(loc_info^.freemem8_size,(oldsize+7) and not 7);
  798. inc(loc_info^.getmem_size,size);
  799. inc(loc_info^.getmem8_size,(size+7) and not 7);
  800. { generate new backtrace }
  801. CaptureBacktrace(1,tracesize-1,@pp^.calls[1]);
  802. { regenerate signature }
  803. if usecrc then
  804. pp^.sig:=calculate_sig(pp);
  805. { return the pointer }
  806. p:=pointer(pp)+sizeof(theap_mem_info);
  807. TraceReAllocmem:=p;
  808. end;
  809. {*****************************************************************************
  810. Check pointer
  811. *****************************************************************************}
  812. {$ifndef Unix}
  813. {$S-}
  814. {$endif}
  815. {$ifdef go32v2}
  816. var
  817. __stklen : longword;external name '__stklen';
  818. __stkbottom : longword;external name '__stkbottom';
  819. ebss : longword; external name 'end';
  820. {$endif go32v2}
  821. {$ifdef linux}
  822. var
  823. etext: ptruint; external name '_etext';
  824. edata : ptruint; external name '_edata';
  825. eend : ptruint; external name '_end';
  826. {$endif}
  827. {$ifdef os2}
  828. (* Currently still EMX based - possibly to be changed in the future. *)
  829. var
  830. etext: ptruint; external name '_etext';
  831. edata : ptruint; external name '_edata';
  832. eend : ptruint; external name '_end';
  833. {$endif}
  834. {$ifdef windows}
  835. var
  836. sdata : ptruint; external name '__data_start__';
  837. edata : ptruint; external name '__data_end__';
  838. sbss : ptruint; external name '__bss_start__';
  839. ebss : ptruint; external name '__bss_end__';
  840. TLSKey : DWord; external name '_FPC_TlsKey';
  841. TLSSize : DWord; external name '_FPC_TlsSize';
  842. function TlsGetValue(dwTlsIndex : DWord) : pointer;
  843. {$ifdef wince}cdecl{$else}stdcall{$endif};external KernelDLL name 'TlsGetValue';
  844. {$endif}
  845. {$ifdef BEOS}
  846. const
  847. B_ERROR = -1;
  848. type
  849. area_id = Longint;
  850. function area_for(addr : Pointer) : area_id;
  851. cdecl; external 'root' name 'area_for';
  852. {$endif BEOS}
  853. procedure CheckPointer(p : pointer); [public, alias : 'FPC_CHECKPOINTER'];
  854. var
  855. i : ptruint;
  856. pp : pheap_mem_info;
  857. loc_info: pheap_info;
  858. {$ifdef go32v2}
  859. get_ebp,stack_top : longword;
  860. bss_end : longword;
  861. {$endif go32v2}
  862. {$ifdef windows}
  863. datap : pointer;
  864. {$endif windows}
  865. ptext : ^text;
  866. begin
  867. if p=nil then
  868. runerror(204);
  869. i:=0;
  870. loc_info:=@heap_info;
  871. if useownfile then
  872. ptext:=@ownfile
  873. else
  874. ptext:=textoutput;
  875. {$ifdef go32v2}
  876. if ptruint(p)<$1000 then
  877. runerror(216);
  878. asm
  879. movl %ebp,get_ebp
  880. leal ebss,%eax
  881. movl %eax,bss_end
  882. end;
  883. stack_top:=__stkbottom+__stklen;
  884. { allow all between start of code and end of bss }
  885. if ptruint(p)<=bss_end then
  886. exit;
  887. { stack can be above heap !! }
  888. if (ptruint(p)>=get_ebp) and (ptruint(p)<=stack_top) then
  889. exit;
  890. {$endif go32v2}
  891. { I don't know where the stack is in other OS !! }
  892. {$ifdef windows}
  893. { inside stack ? }
  894. if (ptruint(p)>ptruint(get_frame)) and
  895. (p<StackTop) then
  896. exit;
  897. { inside data ? }
  898. if (ptruint(p)>=ptruint(@sdata)) and (ptruint(p)<ptruint(@edata)) then
  899. exit;
  900. { inside bss ? }
  901. if (ptruint(p)>=ptruint(@sbss)) and (ptruint(p)<ptruint(@ebss)) then
  902. exit;
  903. { is program multi-threaded and p inside Threadvar range? }
  904. if TlsKey<>-1 then
  905. begin
  906. datap:=TlsGetValue(tlskey);
  907. if ((ptruint(p)>=ptruint(datap)) and
  908. (ptruint(p)<ptruint(datap)+TlsSize)) then
  909. exit;
  910. end;
  911. {$endif windows}
  912. {$IFDEF OS2}
  913. { inside stack ? }
  914. if (PtrUInt (P) > PtrUInt (Get_Frame)) and
  915. (PtrUInt (P) < PtrUInt (StackTop)) then
  916. exit;
  917. { inside data or bss ? }
  918. if (PtrUInt (P) >= PtrUInt (@etext)) and (PtrUInt (P) < PtrUInt (@eend)) then
  919. exit;
  920. {$ENDIF OS2}
  921. {$ifdef linux}
  922. { inside stack ? }
  923. if (ptruint(p)>ptruint(get_frame)) and
  924. (ptruint(p)<$c0000000) then //todo: 64bit!
  925. exit;
  926. { inside data or bss ? }
  927. if (ptruint(p)>=ptruint(@etext)) and (ptruint(p)<ptruint(@eend)) then
  928. exit;
  929. {$endif linux}
  930. {$ifdef morphos}
  931. { inside stack ? }
  932. if (ptruint(p)<ptruint(StackTop)) and (ptruint(p)>ptruint(StackBottom)) then
  933. exit;
  934. { inside data or bss ? }
  935. {$WARNING data and bss checking missing }
  936. {$endif morphos}
  937. {$ifdef darwin}
  938. {$warning No checkpointer support yet for Darwin}
  939. exit;
  940. {$endif}
  941. {$ifdef BEOS}
  942. // if we find the address in a known area in our current process,
  943. // then it is a valid one
  944. if area_for(p) <> B_ERROR then
  945. exit;
  946. {$endif BEOS}
  947. { first try valid list faster }
  948. {$ifdef EXTRA}
  949. pp:=loc_info^.heap_valid_last;
  950. while pp<>nil do
  951. begin
  952. { inside this valid block ! }
  953. { we can be changing the extrainfo !! }
  954. if (ptruint(p)>=ptruint(pp)+sizeof(theap_mem_info){+extra_info_size}) and
  955. (ptruint(p)<=ptruint(pp)+sizeof(theap_mem_info)+extra_info_size+pp^.size) then
  956. begin
  957. { check allocated block }
  958. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  959. ((pp^.sig=calculate_sig(pp)) and usecrc) or
  960. { special case of the fill_extra_info call }
  961. ((pp=loc_info^.heap_valid_last) and usecrc and (pp^.sig=$DEADBEEF)
  962. and loc_info^.inside_trace_getmem) then
  963. exit
  964. else
  965. begin
  966. writeln(ptext^,'corrupted heap_mem_info');
  967. dump_error(pp,ptext^);
  968. halt(1);
  969. end;
  970. end
  971. else
  972. pp:=pp^.prev_valid;
  973. inc(i);
  974. if i>loc_info^.getmem_cnt-loc_info^.freemem_cnt then
  975. begin
  976. writeln(ptext^,'error in linked list of heap_mem_info');
  977. halt(1);
  978. end;
  979. end;
  980. i:=0;
  981. {$endif EXTRA}
  982. pp:=loc_info^.heap_mem_root;
  983. while pp<>nil do
  984. begin
  985. { inside this block ! }
  986. if (ptruint(p)>=ptruint(pp)+sizeof(theap_mem_info)+ptruint(extra_info_size)) and
  987. (ptruint(p)<=ptruint(pp)+sizeof(theap_mem_info)+ptruint(extra_info_size)+ptruint(pp^.size)) then
  988. { allocated block }
  989. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  990. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  991. exit
  992. else
  993. begin
  994. writeln(ptext^,'pointer $',hexstr(p),' points into invalid memory block');
  995. dump_error(pp,ptext^);
  996. runerror(204);
  997. end;
  998. pp:=pp^.previous;
  999. inc(i);
  1000. if i>loc_info^.getmem_cnt then
  1001. begin
  1002. writeln(ptext^,'error in linked list of heap_mem_info');
  1003. halt(1);
  1004. end;
  1005. end;
  1006. writeln(ptext^,'pointer $',hexstr(p),' does not point to valid memory block');
  1007. dump_stack(ptext^,1);
  1008. runerror(204);
  1009. end;
  1010. {*****************************************************************************
  1011. Dump Heap
  1012. *****************************************************************************}
  1013. procedure dumpheap;
  1014. begin
  1015. DumpHeap(GlobalSkipIfNoLeaks);
  1016. end;
  1017. procedure dumpheap(SkipIfNoLeaks : Boolean);
  1018. var
  1019. pp : pheap_mem_info;
  1020. i : ptrint;
  1021. ExpectedHeapFree : ptruint;
  1022. status : TFPCHeapStatus;
  1023. ptext : ^text;
  1024. loc_info: pheap_info;
  1025. begin
  1026. loc_info:=@heap_info;
  1027. if useownfile then
  1028. ptext:=@ownfile
  1029. else
  1030. ptext:=textoutput;
  1031. pp:=loc_info^.heap_mem_root;
  1032. if ((loc_info^.getmem_size-loc_info^.freemem_size)=0) and SkipIfNoLeaks then
  1033. exit;
  1034. Writeln(ptext^,'Heap dump by heaptrc unit');
  1035. Writeln(ptext^,loc_info^.getmem_cnt, ' memory blocks allocated : ',
  1036. loc_info^.getmem_size,'/',loc_info^.getmem8_size);
  1037. Writeln(ptext^,loc_info^.freemem_cnt,' memory blocks freed : ',
  1038. loc_info^.freemem_size,'/',loc_info^.freemem8_size);
  1039. Writeln(ptext^,loc_info^.getmem_cnt-loc_info^.freemem_cnt,
  1040. ' unfreed memory blocks : ',loc_info^.getmem_size-loc_info^.freemem_size);
  1041. status:=SysGetFPCHeapStatus;
  1042. Write(ptext^,'True heap size : ',status.CurrHeapSize);
  1043. if EntryMemUsed > 0 then
  1044. Writeln(ptext^,' (',EntryMemUsed,' used in System startup)')
  1045. else
  1046. Writeln(ptext^);
  1047. Writeln(ptext^,'True free heap : ',status.CurrHeapFree);
  1048. ExpectedHeapFree:=status.CurrHeapSize
  1049. -(loc_info^.getmem8_size-loc_info^.freemem8_size)
  1050. -(loc_info^.getmem_cnt-loc_info^.freemem_cnt)*(sizeof(theap_mem_info)+extra_info_size)
  1051. -EntryMemUsed;
  1052. If ExpectedHeapFree<>status.CurrHeapFree then
  1053. Writeln(ptext^,'Should be : ',ExpectedHeapFree);
  1054. i:=loc_info^.getmem_cnt-loc_info^.freemem_cnt;
  1055. while pp<>nil do
  1056. begin
  1057. if i<0 then
  1058. begin
  1059. Writeln(ptext^,'Error in heap memory list');
  1060. Writeln(ptext^,'More memory blocks than expected');
  1061. exit;
  1062. end;
  1063. if ((pp^.sig=$DEADBEEF) and not usecrc) or
  1064. ((pp^.sig=calculate_sig(pp)) and usecrc) then
  1065. begin
  1066. { this one was not released !! }
  1067. if exitcode<>203 then
  1068. call_stack(pp,ptext^);
  1069. dec(i);
  1070. end
  1071. else if pp^.sig<>$AAAAAAAA then
  1072. begin
  1073. dump_error(pp,ptext^);
  1074. {$ifdef EXTRA}
  1075. dump_error(pp,error_file);
  1076. {$endif EXTRA}
  1077. loc_info^.error_in_heap:=true;
  1078. end
  1079. {$ifdef EXTRA}
  1080. else if pp^.release_sig<>calculate_release_sig(pp) then
  1081. begin
  1082. dump_change_after(pp,ptext^);
  1083. dump_change_after(pp,error_file);
  1084. loc_info^.error_in_heap:=true;
  1085. end
  1086. {$endif EXTRA}
  1087. ;
  1088. pp:=pp^.previous;
  1089. end;
  1090. if HaltOnNotReleased and (loc_info^.getmem_cnt<>loc_info^.freemem_cnt) then
  1091. exitcode:=203;
  1092. end;
  1093. {*****************************************************************************
  1094. AllocMem
  1095. *****************************************************************************}
  1096. function TraceAllocMem(size:ptruint):Pointer;
  1097. begin
  1098. TraceAllocMem:=SysAllocMem(size);
  1099. end;
  1100. {*****************************************************************************
  1101. No specific tracing calls
  1102. *****************************************************************************}
  1103. procedure TraceInitThread;
  1104. var
  1105. loc_info: pheap_info;
  1106. begin
  1107. loc_info := @heap_info;
  1108. {$ifdef EXTRA}
  1109. loc_info^.heap_valid_first := nil;
  1110. loc_info^.heap_valid_last := nil;
  1111. {$endif}
  1112. loc_info^.heap_mem_root := nil;
  1113. loc_info^.getmem_cnt := 0;
  1114. loc_info^.freemem_cnt := 0;
  1115. loc_info^.getmem_size := 0;
  1116. loc_info^.freemem_size := 0;
  1117. loc_info^.getmem8_size := 0;
  1118. loc_info^.freemem8_size := 0;
  1119. loc_info^.error_in_heap := false;
  1120. loc_info^.inside_trace_getmem := false;
  1121. EntryMemUsed := SysGetFPCHeapStatus.CurrHeapUsed;
  1122. end;
  1123. procedure TraceRelocateHeap;
  1124. begin
  1125. main_relo_todolist := @heap_info.heap_free_todo;
  1126. initcriticalsection(todo_lock);
  1127. end;
  1128. procedure move_heap_info(src_info, dst_info: pheap_info);
  1129. var
  1130. heap_mem: pheap_mem_info;
  1131. begin
  1132. if src_info^.heap_free_todo <> nil then
  1133. finish_heap_free_todo_list(src_info);
  1134. if dst_info^.heap_free_todo <> nil then
  1135. finish_heap_free_todo_list(dst_info);
  1136. heap_mem := src_info^.heap_mem_root;
  1137. if heap_mem <> nil then
  1138. begin
  1139. repeat
  1140. heap_mem^.todolist := @dst_info^.heap_free_todo;
  1141. if heap_mem^.previous = nil then break;
  1142. heap_mem := heap_mem^.previous;
  1143. until false;
  1144. heap_mem^.previous := dst_info^.heap_mem_root;
  1145. if dst_info^.heap_mem_root <> nil then
  1146. dst_info^.heap_mem_root^.next := heap_mem;
  1147. dst_info^.heap_mem_root := src_info^.heap_mem_root;
  1148. end;
  1149. inc(dst_info^.getmem_cnt, src_info^.getmem_cnt);
  1150. inc(dst_info^.getmem_size, src_info^.getmem_size);
  1151. inc(dst_info^.getmem8_size, src_info^.getmem8_size);
  1152. inc(dst_info^.freemem_cnt, src_info^.freemem_cnt);
  1153. inc(dst_info^.freemem_size, src_info^.freemem_size);
  1154. inc(dst_info^.freemem8_size, src_info^.freemem8_size);
  1155. dst_info^.error_in_heap := dst_info^.error_in_heap or src_info^.error_in_heap;
  1156. {$ifdef EXTRA}
  1157. if assigned(dst_info^.heap_valid_first) then
  1158. dst_info^.heap_valid_first^.prev_valid := src_info^.heap_valid_last
  1159. else
  1160. dst_info^.heap_valid_last := src_info^.heap_valid_last;
  1161. dst_info^.heap_valid_first := src_info^.heap_valid_first;
  1162. {$endif}
  1163. end;
  1164. procedure TraceExitThread;
  1165. var
  1166. loc_info: pheap_info;
  1167. begin
  1168. loc_info := @heap_info;
  1169. entercriticalsection(todo_lock);
  1170. move_heap_info(loc_info, @orphaned_info);
  1171. leavecriticalsection(todo_lock);
  1172. end;
  1173. function TraceGetHeapStatus:THeapStatus;
  1174. begin
  1175. TraceGetHeapStatus:=SysGetHeapStatus;
  1176. end;
  1177. function TraceGetFPCHeapStatus:TFPCHeapStatus;
  1178. begin
  1179. TraceGetFPCHeapStatus:=SysGetFPCHeapStatus;
  1180. end;
  1181. {*****************************************************************************
  1182. Program Hooks
  1183. *****************************************************************************}
  1184. Procedure SetHeapTraceOutput(const name : string);
  1185. var i : ptruint;
  1186. begin
  1187. if useownfile then
  1188. begin
  1189. useownfile:=false;
  1190. close(ownfile);
  1191. end;
  1192. assign(ownfile,name);
  1193. {$I-}
  1194. append(ownfile);
  1195. if IOResult<>0 then
  1196. begin
  1197. Rewrite(ownfile);
  1198. if IOResult<>0 then
  1199. begin
  1200. Writeln(textoutput^,'[heaptrc] Unable to open "',name,'", writing output to stderr instead.');
  1201. useownfile:=false;
  1202. exit;
  1203. end;
  1204. end;
  1205. {$I+}
  1206. useownfile:=true;
  1207. for i:=0 to Paramcount do
  1208. write(ownfile,paramstr(i),' ');
  1209. writeln(ownfile);
  1210. end;
  1211. procedure SetHeapTraceOutput(var ATextOutput : Text);
  1212. Begin
  1213. useowntextoutput := True;
  1214. textoutput := @ATextOutput;
  1215. end;
  1216. procedure SetHeapExtraInfo( size : ptruint;fillproc : tfillextrainfoproc;displayproc : tdisplayextrainfoproc);
  1217. begin
  1218. { the total size must stay multiple of 8, also allocate 2 pointers for
  1219. the fill and display procvars }
  1220. exact_info_size:=size + sizeof(theap_extra_info);
  1221. extra_info_size:=(exact_info_size+7) and not 7;
  1222. fill_extra_info_proc:=fillproc;
  1223. display_extra_info_proc:=displayproc;
  1224. end;
  1225. {*****************************************************************************
  1226. Install MemoryManager
  1227. *****************************************************************************}
  1228. const
  1229. TraceManager:TMemoryManager=(
  1230. NeedLock : true;
  1231. Getmem : @TraceGetMem;
  1232. Freemem : @TraceFreeMem;
  1233. FreememSize : @TraceFreeMemSize;
  1234. AllocMem : @TraceAllocMem;
  1235. ReAllocMem : @TraceReAllocMem;
  1236. MemSize : @TraceMemSize;
  1237. InitThread: @TraceInitThread;
  1238. DoneThread: @TraceExitThread;
  1239. RelocateHeap: @TraceRelocateHeap;
  1240. GetHeapStatus : @TraceGetHeapStatus;
  1241. GetFPCHeapStatus : @TraceGetFPCHeapStatus;
  1242. );
  1243. procedure TraceInit;
  1244. begin
  1245. textoutput := @stderr;
  1246. useowntextoutput := false;
  1247. MakeCRC32Tbl;
  1248. main_orig_todolist := @heap_info.heap_free_todo;
  1249. main_relo_todolist := nil;
  1250. TraceInitThread;
  1251. SetMemoryManager(TraceManager);
  1252. useownfile:=false;
  1253. if outputstr <> '' then
  1254. SetHeapTraceOutput(outputstr);
  1255. {$ifdef EXTRA}
  1256. {$i-}
  1257. Assign(error_file,'heap.err');
  1258. Rewrite(error_file);
  1259. {$i+}
  1260. if IOResult<>0 then
  1261. begin
  1262. writeln('[heaptrc] Unable to create heap.err extra log file, writing output to screen.');
  1263. Assign(error_file,'');
  1264. Rewrite(error_file);
  1265. end;
  1266. {$endif EXTRA}
  1267. { if multithreading was initialized before heaptrc gets initialized (this is currently
  1268. the case for windows dlls), then RelocateHeap gets never called and the lock
  1269. must be initialized already here
  1270. }
  1271. if IsMultithread then
  1272. TraceRelocateHeap;
  1273. end;
  1274. procedure TraceExit;
  1275. begin
  1276. { no dump if error
  1277. because this gives long long listings }
  1278. { clear inoutres, in case the program that quit didn't }
  1279. ioresult;
  1280. if (exitcode<>0) and (erroraddr<>nil) then
  1281. begin
  1282. if useownfile then
  1283. begin
  1284. Writeln(ownfile,'No heap dump by heaptrc unit');
  1285. Writeln(ownfile,'Exitcode = ',exitcode);
  1286. end
  1287. else
  1288. begin
  1289. Writeln(textoutput^,'No heap dump by heaptrc unit');
  1290. Writeln(textoutput^,'Exitcode = ',exitcode);
  1291. end;
  1292. if useownfile then
  1293. begin
  1294. useownfile:=false;
  1295. close(ownfile);
  1296. end;
  1297. exit;
  1298. end;
  1299. move_heap_info(@orphaned_info, @heap_info);
  1300. dumpheap;
  1301. if heap_info.error_in_heap and (exitcode=0) then
  1302. exitcode:=203;
  1303. if main_relo_todolist <> nil then
  1304. donecriticalsection(todo_lock);
  1305. {$ifdef EXTRA}
  1306. Close(error_file);
  1307. {$endif EXTRA}
  1308. if useownfile then
  1309. begin
  1310. useownfile:=false;
  1311. close(ownfile);
  1312. end;
  1313. if useowntextoutput then
  1314. begin
  1315. useowntextoutput := false;
  1316. close(textoutput^);
  1317. end;
  1318. end;
  1319. {$if defined(win32) or defined(win64)}
  1320. function GetEnvironmentStrings : pchar; stdcall;
  1321. external 'kernel32' name 'GetEnvironmentStringsA';
  1322. function FreeEnvironmentStrings(p : pchar) : longbool; stdcall;
  1323. external 'kernel32' name 'FreeEnvironmentStringsA';
  1324. Function GetEnv(envvar: string): string;
  1325. var
  1326. s : string;
  1327. i : ptruint;
  1328. hp,p : pchar;
  1329. begin
  1330. getenv:='';
  1331. p:=GetEnvironmentStrings;
  1332. hp:=p;
  1333. while hp^<>#0 do
  1334. begin
  1335. s:=strpas(hp);
  1336. i:=pos('=',s);
  1337. if upcase(copy(s,1,i-1))=upcase(envvar) then
  1338. begin
  1339. getenv:=copy(s,i+1,length(s)-i);
  1340. break;
  1341. end;
  1342. { next string entry}
  1343. hp:=hp+strlen(hp)+1;
  1344. end;
  1345. FreeEnvironmentStrings(p);
  1346. end;
  1347. {$else defined(win32) or defined(win64)}
  1348. {$ifdef wince}
  1349. Function GetEnv(P:string):Pchar;
  1350. begin
  1351. { WinCE does not have environment strings.
  1352. Add some way to specify heaptrc options? }
  1353. GetEnv:=nil;
  1354. end;
  1355. {$else wince}
  1356. Function GetEnv(P:string):Pchar;
  1357. {
  1358. Searches the environment for a string with name p and
  1359. returns a pchar to it's value.
  1360. A pchar is used to accomodate for strings of length > 255
  1361. }
  1362. var
  1363. ep : ppchar;
  1364. i : ptruint;
  1365. found : boolean;
  1366. Begin
  1367. p:=p+'='; {Else HOST will also find HOSTNAME, etc}
  1368. ep:=envp;
  1369. found:=false;
  1370. if ep<>nil then
  1371. begin
  1372. while (not found) and (ep^<>nil) do
  1373. begin
  1374. found:=true;
  1375. for i:=1 to length(p) do
  1376. if p[i]<>ep^[i-1] then
  1377. begin
  1378. found:=false;
  1379. break;
  1380. end;
  1381. if not found then
  1382. inc(ep);
  1383. end;
  1384. end;
  1385. if found then
  1386. getenv:=ep^+length(p)
  1387. else
  1388. getenv:=nil;
  1389. end;
  1390. {$endif wince}
  1391. {$endif win32}
  1392. procedure LoadEnvironment;
  1393. var
  1394. i,j : ptruint;
  1395. s : string;
  1396. begin
  1397. s:=Getenv('HEAPTRC');
  1398. if pos('keepreleased',s)>0 then
  1399. keepreleased:=true;
  1400. if pos('disabled',s)>0 then
  1401. useheaptrace:=false;
  1402. if pos('nohalt',s)>0 then
  1403. haltonerror:=false;
  1404. if pos('haltonnotreleased',s)>0 then
  1405. HaltOnNotReleased :=true;
  1406. if pos('skipifnoleaks',s)>0 then
  1407. GlobalSkipIfNoLeaks :=true;
  1408. i:=pos('log=',s);
  1409. if i>0 then
  1410. begin
  1411. outputstr:=copy(s,i+4,255);
  1412. j:=pos(' ',outputstr);
  1413. if j=0 then
  1414. j:=length(outputstr)+1;
  1415. delete(outputstr,j,255);
  1416. end;
  1417. end;
  1418. Initialization
  1419. LoadEnvironment;
  1420. { heaptrc can be disabled from the environment }
  1421. if useheaptrace then
  1422. TraceInit;
  1423. finalization
  1424. if useheaptrace then
  1425. TraceExit;
  1426. end.