heaptrc.pp 49 KB

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