heaptrc.pp 44 KB

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