heaptrc.pp 44 KB

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