FastMM_FullDebugMode.dpr 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. {
  2. Fast Memory Manager: FullDebugMode Support DLL 1.62
  3. Description:
  4. Support DLL for FastMM. With this DLL available, FastMM will report debug info
  5. (unit name, line numbers, etc.) for stack traces.
  6. Usage:
  7. 1) To compile you will need the JCL library (http://sourceforge.net/projects/jcl/)
  8. 2) Place in the same location as the replacement borlndmm.dll or your
  9. application's executable module.
  10. Change log:
  11. Version 1.00 (9 July 2005):
  12. - Initial release.
  13. Version 1.01 (13 July 2005):
  14. - Added the option to use madExcept instead of the JCL Debug library. (Thanks
  15. to Martin Aignesberger.)
  16. Version 1.02 (30 September 2005):
  17. - Changed options to display detail for addresses inside libraries as well.
  18. Version 1.03 (13 October 2005):
  19. - Added a raw stack trace procedure that implements raw stack traces.
  20. Version 1.10 (14 October 2005):
  21. - Improved the program logic behind the skipping of stack levels to cause
  22. less incorrect entries in raw stack traces. (Thanks to Craig Peterson.)
  23. Version 1.20 (17 October 2005):
  24. - Improved support for madExcept stack traces. (Thanks to Mathias Rauen.)
  25. Version 1.30 (26 October 2005):
  26. - Changed name to FastMM_FullDebugMode to reflect the fact that there is now
  27. a static dependency on this DLL for FullDebugMode. The static dependency
  28. solves a DLL unload order issue. (Thanks to Bart van der Werf.)
  29. Version 1.40 (31 October 2005):
  30. - Added support for EurekaLog. (Thanks to Fabio Dell'Aria.)
  31. Version 1.42 (23 June 2006):
  32. - Fixed a bug in the RawStackTraces code that may have caused an A/V in some
  33. rare circumstances. (Thanks to Primoz Gabrijelcic.)
  34. Version 1.44 (16 November 2006):
  35. - Changed the RawStackTraces code to prevent it from modifying the Windows
  36. "GetLastError" error code. (Thanks to Primoz Gabrijelcic.)
  37. Version 1.50 (14 August 2008):
  38. - Added support for Delphi 2009. (Thanks to Mark Edington.)
  39. Version 1.60 (5 May 2009):
  40. - Improved the code used to identify call instructions in the stack trace
  41. code. (Thanks to the JCL team.)
  42. Version 1.61 (5 September 2010):
  43. - Recompiled using the latest JCL in order to fix a possible crash on shutdown
  44. when the executable contains no debug information. (Thanks to Hanspeter
  45. Widmer.)
  46. Version 1.62 (19 July 2012):
  47. - Added a workaround for QC 107209 (Thanks to David Heffernan.)
  48. }
  49. {--------------------Start of options block-------------------------}
  50. {Select the stack tracing library to use. The JCL, madExcept and EurekaLog are
  51. supported. Only one can be used at a time.}
  52. {$define JCLDebug}
  53. {.$define madExcept}
  54. {.$define EurekaLog}
  55. {--------------------End of options block-------------------------}
  56. // JCL_DEBUG_EXPERT_INSERTJDBG OFF
  57. library FastMM_FullDebugMode;
  58. uses
  59. {$ifdef JCLDebug}JCLDebug{$endif}
  60. {$ifdef madExcept}madStackTrace{$endif}
  61. {$ifdef EurekaLog}ExceptionLog{$endif},
  62. SysUtils, Windows;
  63. {$R *.res}
  64. {$stackframes on}
  65. {The name of the 64-bit DLL has a '64' at the end.}
  66. {$if SizeOf(Pointer) = 8}
  67. {$LIBSUFFIX '64'}
  68. {$ifend}
  69. {$if CompilerVersion < 20}
  70. type
  71. PNativeUInt = ^Cardinal;
  72. {$ifend}
  73. {--------------------------Stack Tracing Subroutines--------------------------}
  74. procedure GetStackRange(var AStackBaseAddress, ACurrentStackPointer: NativeUInt);
  75. asm
  76. {$if SizeOf(Pointer) = 8}
  77. mov rax, gs:[abs 8]
  78. mov [rcx], rax
  79. mov [rdx], rbp
  80. {$else}
  81. mov ecx, fs:[4]
  82. mov [eax], ecx
  83. mov [edx], ebp
  84. {$ifend}
  85. end;
  86. {--------------------------Frame Based Stack Tracing--------------------------}
  87. {$if SizeOf(Pointer) = 8}
  88. function CaptureStackBackTrace(FramesToSkip, FramesToCapture: DWORD;
  89. BackTrace: Pointer; BackTraceHash: PDWORD): Word;
  90. external kernel32 name 'RtlCaptureStackBackTrace';
  91. {We use the Windows API to do frame based stack tracing under 64-bit.}
  92. procedure GetFrameBasedStackTrace(AReturnAddresses: PNativeUInt;
  93. AMaxDepth, ASkipFrames: Cardinal);
  94. begin
  95. CaptureStackBackTrace(ASkipFrames, AMaxDepth, AReturnAddresses, nil);
  96. end;
  97. {$else}
  98. {Dumps the call stack trace to the given address. Fills the list with the
  99. addresses where the called addresses can be found. This is the fast stack
  100. frame based tracing routine.}
  101. procedure GetFrameBasedStackTrace(AReturnAddresses: PNativeUInt;
  102. AMaxDepth, ASkipFrames: Cardinal);
  103. var
  104. LStackTop, LStackBottom, LCurrentFrame: NativeUInt;
  105. begin
  106. {Get the call stack top and current bottom}
  107. GetStackRange(LStackTop, LStackBottom);
  108. Dec(LStackTop, SizeOf(Pointer) - 1);
  109. {Get the current frame start}
  110. LCurrentFrame := LStackBottom;
  111. {Fill the call stack}
  112. while (AMaxDepth > 0)
  113. and (LCurrentFrame >= LStackBottom)
  114. and (LCurrentFrame < LStackTop) do
  115. begin
  116. {Ignore the requested number of levels}
  117. if ASkipFrames = 0 then
  118. begin
  119. AReturnAddresses^ := PNativeUInt(LCurrentFrame + SizeOf(Pointer))^;
  120. Inc(AReturnAddresses);
  121. Dec(AMaxDepth);
  122. end
  123. else
  124. Dec(ASkipFrames);
  125. {Get the next frame}
  126. LCurrentFrame := PNativeUInt(LCurrentFrame)^;
  127. end;
  128. {Clear the remaining entries}
  129. while AMaxDepth > 0 do
  130. begin
  131. AReturnAddresses^ := 0;
  132. Inc(AReturnAddresses);
  133. Dec(AMaxDepth);
  134. end;
  135. end;
  136. {$ifend}
  137. {-----------------------------Raw Stack Tracing-----------------------------}
  138. const
  139. {Hexadecimal characters}
  140. HexTable: array[0..15] of AnsiChar = '0123456789ABCDEF';
  141. type
  142. {The state of a memory page. Used by the raw stack tracing mechanism to
  143. determine whether an address is a valid call site or not.}
  144. TMemoryPageAccess = (mpaUnknown, mpaNotExecutable, mpaExecutable);
  145. var
  146. {There are a total of 1M x 4K pages in the (low) 4GB address space}
  147. MemoryPageAccessMap: array[0..1024 * 1024 - 1] of TMemoryPageAccess;
  148. {Updates the memory page access map. Currently only supports the low 4GB of
  149. address space.}
  150. procedure UpdateMemoryPageAccessMap(AAddress: NativeUInt);
  151. var
  152. LMemInfo: TMemoryBasicInformation;
  153. LAccess: TMemoryPageAccess;
  154. LStartPage, LPageCount: NativeUInt;
  155. begin
  156. {Query the page}
  157. if VirtualQuery(Pointer(AAddress), LMemInfo, SizeOf(LMemInfo)) <> 0 then
  158. begin
  159. {Get access type}
  160. if (LMemInfo.State = MEM_COMMIT)
  161. and (LMemInfo.Protect and (PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE
  162. or PAGE_EXECUTE_WRITECOPY or PAGE_EXECUTE) <> 0)
  163. and (LMemInfo.Protect and PAGE_GUARD = 0) then
  164. begin
  165. LAccess := mpaExecutable
  166. end
  167. else
  168. LAccess := mpaNotExecutable;
  169. {Update the map}
  170. LStartPage := NativeUInt(LMemInfo.BaseAddress) div 4096;
  171. LPageCount := LMemInfo.RegionSize div 4096;
  172. if LStartPage < NativeUInt(Length(MemoryPageAccessMap)) then
  173. begin
  174. if (LStartPage + LPageCount) >= NativeUInt(Length(MemoryPageAccessMap)) then
  175. LPageCount := NativeUInt(Length(MemoryPageAccessMap)) - LStartPage;
  176. FillChar(MemoryPageAccessMap[LStartPage], LPageCount, Ord(LAccess));
  177. end;
  178. end
  179. else
  180. begin
  181. {Invalid address}
  182. MemoryPageAccessMap[AAddress div 4096] := mpaNotExecutable;
  183. end;
  184. end;
  185. {Thread-safe version that avoids the global variable Default8087CW.}
  186. procedure Set8087CW(ANewCW: Word);
  187. var
  188. L8087CW: Word;
  189. asm
  190. mov L8087CW, ANewCW
  191. fnclex
  192. fldcw L8087CW
  193. end;
  194. {$if CompilerVersion > 22}
  195. {Thread-safe version that avoids the global variable DefaultMXCSR.}
  196. procedure SetMXCSR(ANewMXCSR: Cardinal);
  197. var
  198. LMXCSR: Cardinal;
  199. asm
  200. {$if SizeOf(Pointer) <> 8}
  201. cmp System.TestSSE, 0
  202. je @exit
  203. {$ifend}
  204. {Remove the flag bits}
  205. and ANewMXCSR, $ffc0
  206. mov LMXCSR, ANewMXCSR
  207. ldmxcsr LMXCSR
  208. @exit:
  209. end;
  210. {$ifend}
  211. {Returns true if the return address is a valid call site. This function is only
  212. safe to call while exceptions are being handled.}
  213. function IsValidCallSite(AReturnAddress: NativeUInt): boolean;
  214. var
  215. LCallAddress: NativeUInt;
  216. LCode8Back, LCode4Back, LTemp: Cardinal;
  217. LOld8087CW: Word;
  218. {$if CompilerVersion > 22}
  219. LOldMXCSR: Cardinal;
  220. {$ifend}
  221. begin
  222. {We assume (for now) that all code will execute within the first 4GB of
  223. address space.}
  224. if (AReturnAddress > $ffff) and (AReturnAddress <= $ffffffff) then
  225. begin
  226. {The call address is up to 8 bytes before the return address}
  227. LCallAddress := AReturnAddress - 8;
  228. {Update the page map}
  229. if MemoryPageAccessMap[LCallAddress div 4096] = mpaUnknown then
  230. UpdateMemoryPageAccessMap(LCallAddress);
  231. {Check the page access}
  232. if (MemoryPageAccessMap[LCallAddress div 4096] = mpaExecutable)
  233. and (MemoryPageAccessMap[(LCallAddress + 8) div 4096] = mpaExecutable) then
  234. begin
  235. {Try to determine what kind of call it is (if any), more or less in order
  236. of frequency of occurrence. (Code below taken from the Jedi Code Library
  237. (jcl.sourceforge.net).) We need to retrieve the current floating point
  238. control registers, since any external exception will reset it to the
  239. DLL defaults which may not otherwise correspond to the defaults of the
  240. main application (QC 107198).}
  241. LOld8087CW := Get8087CW;
  242. {$if CompilerVersion > 22}
  243. LOldMXCSR := GetMXCSR;
  244. {$ifend}
  245. try
  246. {5 bytes, CALL NEAR REL32}
  247. if PByteArray(LCallAddress)[3] = $E8 then
  248. begin
  249. Result := True;
  250. Exit;
  251. end;
  252. {Get the 4 bytes before the return address}
  253. LCode4Back := PCardinal(LCallAddress + 4)^;
  254. {2 byte call?}
  255. LTemp := LCode4Back and $F8FF0000;
  256. {2 bytes, CALL NEAR EAX}
  257. if LTemp = $D0FF0000 then
  258. begin
  259. Result := True;
  260. Exit;
  261. end;
  262. {2 bytes, CALL NEAR [EAX]}
  263. if LTemp = $10FF0000 then
  264. begin
  265. LTemp := LCode4Back - LTemp;
  266. if (LTemp <> $04000000) and (LTemp <> $05000000) then
  267. begin
  268. Result := True;
  269. Exit;
  270. end;
  271. end;
  272. {3 bytes, CALL NEAR [EAX+EAX*i]}
  273. if (LCode4Back and $00FFFF00) = $0014FF00 then
  274. begin
  275. Result := True;
  276. Exit;
  277. end;
  278. {3 bytes, CALL NEAR [EAX+$12]}
  279. if ((LCode4Back and $00F8FF00) = $0050FF00)
  280. and ((LCode4Back and $00070000) <> $00040000) then
  281. begin
  282. Result := True;
  283. Exit;
  284. end;
  285. {4 bytes, CALL NEAR [EAX+EAX+$12]}
  286. if Word(LCode4Back) = $54FF then
  287. begin
  288. Result := True;
  289. Exit;
  290. end;
  291. {6 bytes, CALL NEAR [$12345678]}
  292. LCode8Back := PCardinal(LCallAddress)^;
  293. if (LCode8Back and $FFFF0000) = $15FF0000 then
  294. begin
  295. Result := True;
  296. Exit;
  297. end;
  298. {6 bytes, CALL NEAR [EAX+$12345678]}
  299. if ((LCode8Back and $F8FF0000) = $90FF0000)
  300. and ((LCode8Back and $07000000) <> $04000000) then
  301. begin
  302. Result := True;
  303. Exit;
  304. end;
  305. {7 bytes, CALL NEAR [EAX+EAX+$1234567]}
  306. if (LCode8Back and $00FFFF00) = $0094FF00 then
  307. begin
  308. Result := True;
  309. Exit;
  310. end;
  311. {7 bytes, CALL FAR $1234:12345678}
  312. if (LCode8Back and $0000FF00) = $00009A00 then
  313. begin
  314. Result := True;
  315. Exit;
  316. end;
  317. {Not a valid call site}
  318. Result := False;
  319. except
  320. {The access has changed}
  321. UpdateMemoryPageAccessMap(LCallAddress);
  322. {The RTL sets the FPU control words to the default values if an
  323. external exception occurs. Reset their values here to the values on
  324. entry to this call.}
  325. Set8087CW(LOld8087CW);
  326. {$if CompilerVersion > 22}
  327. SetMXCSR(LOldMXCSR);
  328. {$ifend}
  329. {Not executable}
  330. Result := False;
  331. end;
  332. end
  333. else
  334. Result := False;
  335. end
  336. else
  337. Result := False;
  338. end;
  339. {Dumps the call stack trace to the given address. Fills the list with the
  340. addresses where the called addresses can be found. This is the "raw" stack
  341. tracing routine.}
  342. procedure GetRawStackTrace(AReturnAddresses: PNativeUInt;
  343. AMaxDepth, ASkipFrames: Cardinal);
  344. var
  345. LStackTop, LStackBottom, LCurrentFrame, LNextFrame, LReturnAddress,
  346. LStackAddress: NativeUInt;
  347. LLastOSError: Cardinal;
  348. begin
  349. {Are exceptions being handled? Can only do a raw stack trace if the possible
  350. access violations are going to be handled.}
  351. if Assigned(ExceptObjProc) then
  352. begin
  353. {Save the last Windows error code}
  354. LLastOSError := GetLastError;
  355. {Get the call stack top and current bottom}
  356. GetStackRange(LStackTop, LStackBottom);
  357. Dec(LStackTop, SizeOf(Pointer) - 1);
  358. {Get the current frame start}
  359. LCurrentFrame := LStackBottom;
  360. {Fill the call stack}
  361. while (AMaxDepth > 0)
  362. and (LCurrentFrame < LStackTop) do
  363. begin
  364. {Get the next frame}
  365. LNextFrame := PNativeUInt(LCurrentFrame)^;
  366. {Is it a valid stack frame address?}
  367. if (LNextFrame < LStackTop)
  368. and (LNextFrame > LCurrentFrame) then
  369. begin
  370. {The pointer to the next stack frame appears valid: Get the return
  371. address of the current frame}
  372. LReturnAddress := PNativeUInt(LCurrentFrame + SizeOf(Pointer))^;
  373. {Does this appear to be a valid return address}
  374. if (LReturnAddress > $ffff) and (LReturnAddress <= $ffffffff) then
  375. begin
  376. {Is the map for this return address incorrect? It may be unknown or marked
  377. as non-executable because a library was previously not yet loaded, or
  378. perhaps this is not a valid stack frame.}
  379. if MemoryPageAccessMap[(LReturnAddress - 8) div 4096] <> mpaExecutable then
  380. UpdateMemoryPageAccessMap(LReturnAddress - 8);
  381. {Is this return address actually valid?}
  382. if IsValidCallSite(LReturnAddress) then
  383. begin
  384. {Ignore the requested number of levels}
  385. if ASkipFrames = 0 then
  386. begin
  387. AReturnAddresses^ := LReturnAddress;
  388. Inc(AReturnAddresses);
  389. Dec(AMaxDepth);
  390. end;
  391. end
  392. else
  393. begin
  394. {If the return address is invalid it implies this stack frame is
  395. invalid after all.}
  396. LNextFrame := LStackTop;
  397. end;
  398. end
  399. else
  400. begin
  401. {The return address is bad - this is not a valid stack frame}
  402. LNextFrame := LStackTop;
  403. end;
  404. end
  405. else
  406. begin
  407. {This is not a valid stack frame}
  408. LNextFrame := LStackTop;
  409. end;
  410. {Do not check intermediate entries if there are still frames to skip}
  411. if ASkipFrames <> 0 then
  412. begin
  413. Dec(ASkipFrames);
  414. end
  415. else
  416. begin
  417. {Check all stack entries up to the next stack frame}
  418. LStackAddress := LCurrentFrame + 2 * SizeOf(Pointer);
  419. while (AMaxDepth > 0)
  420. and (LStackAddress < LNextFrame) do
  421. begin
  422. {Get the return address}
  423. LReturnAddress := PNativeUInt(LStackAddress)^;
  424. {Is this a valid call site?}
  425. if IsValidCallSite(LReturnAddress) then
  426. begin
  427. AReturnAddresses^ := LReturnAddress;
  428. Inc(AReturnAddresses);
  429. Dec(AMaxDepth);
  430. end;
  431. {Check the next stack address}
  432. Inc(LStackAddress, SizeOf(Pointer));
  433. end;
  434. end;
  435. {Do the next stack frame}
  436. LCurrentFrame := LNextFrame;
  437. end;
  438. {Clear the remaining entries}
  439. while AMaxDepth > 0 do
  440. begin
  441. AReturnAddresses^ := 0;
  442. Inc(AReturnAddresses);
  443. Dec(AMaxDepth);
  444. end;
  445. {Restore the last Windows error code, since a VirtualQuery call may have
  446. modified it.}
  447. SetLastError(LLastOSError);
  448. end
  449. else
  450. begin
  451. {Exception handling is not available - do a frame based stack trace}
  452. GetFrameBasedStackTrace(AReturnAddresses, AMaxDepth, ASkipFrames);
  453. end;
  454. end;
  455. {-----------------------------Stack Trace Logging----------------------------}
  456. {Gets the textual representation of the stack trace into ABuffer and returns
  457. a pointer to the position just after the last character.}
  458. {$ifdef JCLDebug}
  459. {Converts an unsigned integer to a hexadecimal string at the buffer location,
  460. returning the new buffer position.}
  461. function NativeUIntToHexBuf(ANum: NativeUInt; APBuffer: PAnsiChar): PAnsiChar;
  462. const
  463. MaxDigits = 16;
  464. var
  465. LDigitBuffer: array[0..MaxDigits - 1] of AnsiChar;
  466. LCount: Cardinal;
  467. LDigit: NativeUInt;
  468. begin
  469. {Generate the digits in the local buffer}
  470. LCount := 0;
  471. repeat
  472. LDigit := ANum;
  473. ANum := ANum div 16;
  474. LDigit := LDigit - ANum * 16;
  475. Inc(LCount);
  476. LDigitBuffer[MaxDigits - LCount] := HexTable[LDigit];
  477. until ANum = 0;
  478. {Copy the digits to the output buffer and advance it}
  479. System.Move(LDigitBuffer[MaxDigits - LCount], APBuffer^, LCount);
  480. Result := APBuffer + LCount;
  481. end;
  482. {Subroutine used by LogStackTrace}
  483. procedure AppendInfoToString(var AString: string; const AInfo: string);
  484. begin
  485. if AInfo <> '' then
  486. AString := Format('%s[%s]', [AString, AInfo]);
  487. end;
  488. function LogStackTrace(AReturnAddresses: PNativeUInt; AMaxDepth: Cardinal;
  489. ABuffer: PAnsiChar): PAnsiChar;
  490. var
  491. LInd: Cardinal;
  492. LAddress: NativeUInt;
  493. LNumChars: Integer;
  494. LInfo: TJCLLocationInfo;
  495. LTempStr: string;
  496. begin
  497. Result := ABuffer;
  498. for LInd := 0 to AMaxDepth - 1 do
  499. begin
  500. LAddress := AReturnAddresses^;
  501. if LAddress = 0 then
  502. Exit;
  503. Result^ := #13;
  504. Inc(Result);
  505. Result^ := #10;
  506. Inc(Result);
  507. Result := NativeUIntToHexBuf(LAddress, Result);
  508. {Get location info for the caller (at least one byte before the return
  509. address).}
  510. GetLocationInfo(Pointer(Cardinal(LAddress) - 1), LInfo);
  511. {Build the result string}
  512. LTempStr := ' ';
  513. AppendInfoToString(LTempStr, LInfo.SourceName);
  514. AppendInfoToString(LTempStr, LInfo.UnitName);
  515. AppendInfoToString(LTempStr, LInfo.ProcedureName);
  516. if LInfo.LineNumber <> 0 then
  517. AppendInfoToString(LTempStr, IntToStr(LInfo.LineNumber));
  518. {Return the result}
  519. if Length(LTempStr) < 256 then
  520. LNumChars := Length(LTempStr)
  521. else
  522. LNumChars := 255;
  523. StrLCopy(Result, PAnsiChar(AnsiString(LTempStr)), LNumChars);
  524. Inc(Result, LNumChars);
  525. {Next address}
  526. Inc(AReturnAddresses);
  527. end;
  528. end;
  529. {$endif}
  530. {$ifdef madExcept}
  531. function LogStackTrace(AReturnAddresses: PNativeUInt;
  532. AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
  533. begin
  534. {Needs madExcept 2.7i or madExcept 3.0a or a newer build}
  535. Result := madStackTrace.FastMM_LogStackTrace(
  536. AReturnAddresses,
  537. AMaxDepth,
  538. ABuffer,
  539. {madExcept stack trace fine tuning}
  540. false, //hide items which have no line number information?
  541. true, //show relative address offset to procedure entrypoint?
  542. true, //show relative line number offset to procedure entry point?
  543. false //skip special noise reduction processing?
  544. );
  545. end;
  546. {$endif}
  547. {$ifdef EurekaLog}
  548. function LogStackTrace(AReturnAddresses: PNativeUInt; AMaxDepth: Cardinal;
  549. ABuffer: PAnsiChar): PAnsiChar;
  550. begin
  551. {Needs EurekaLog 5.0.5 or a newer build}
  552. Result := ExceptionLog.FastMM_LogStackTrace(
  553. AReturnAddresses, AMaxDepth, ABuffer,
  554. {EurekaLog stack trace fine tuning}
  555. False, // Show the DLLs functions call. <--|
  556. // |-- See the note below!
  557. False, // Show the BPLs functions call. <--|
  558. True // Show relative line no. offset to procedure start point.
  559. );
  560. // NOTE:
  561. // -----
  562. // With these values set both to "False", EurekaLog try to returns the best
  563. // call-stack available.
  564. //
  565. // To do this EurekaLog execute the following points:
  566. // --------------------------------------------------
  567. // 1)...try to fill all call-stack items using only debug data with line no.
  568. // 2)...if remains some empty call-stack items from the previous process (1),
  569. // EurekaLog try to fill these with the BPLs functions calls;
  570. // 3)...if remains some empty call-stack items from the previous process (2),
  571. // EurekaLog try to fill these with the DLLs functions calls;
  572. end;
  573. {$endif}
  574. {-----------------------------Exported Functions----------------------------}
  575. exports
  576. GetFrameBasedStackTrace,
  577. GetRawStackTrace,
  578. LogStackTrace;
  579. begin
  580. {$ifdef JCLDebug}
  581. JclStackTrackingOptions := JclStackTrackingOptions + [stAllModules];
  582. {$endif}
  583. end.