heaptrc.pp 43 KB

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