2
0

heaptrc.pp 50 KB

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