Wagner Landgraf 43 Posted May 14, 2021 I wonder what is a good way to get the stack trace when an exception happens? I know about madExcept and EurekaLog but I was willing to have something lighter and simpler. After a long Google-Fu session, it looks like using JclDebug is still the way to go? I also read here and there that madExcept is better at such job than JclDebug, and since DebugEngine is provided by the madExcept author, I wonder if it (DebugEngine) is a better alternative than JclDebug? Or is there even a more direct, straightforward way to get the stack trace these days? Share this post Link to post
aehimself 396 Posted May 14, 2021 (edited) I gave DebugEngine a try and it is very good. Sometimes it provides stack traces strange (method where exception happened, two inner methods of DebugEngine and then the real stack trace) but it's really easy to install and use. It is also unaffected by the two issues I have with MadExcept (TThread.FatalException and Exception.InnerException is rendered useless). It comes with a utility to attach the map (or it's own compressed smap) format to the .EXE itself, however the final executable is larger than with MadExcept (guess the .map compression is not that advanced in DebugEngine than in MadExcept). I don't have much experience with JclDebug... I tried it once, and I disliked every bit of it. Only to be able to see stack traces I had to include ~30 extra files in my project and the stack traces were not accurate.; so I gave up on it. If you can fit into the free usage conditions of MadExcept I'd say go with that. Otherwise, DebugEngine (or a license for MadExcept) would be my choice. P.s.: keep in mind that also MadExcept and DebugEngine is known to have stack issues if the .exe is packed with UPX. Edited May 14, 2021 by aehimself 1 1 Share this post Link to post
Wagner Landgraf 43 Posted May 14, 2021 25 minutes ago, aehimself said: I gave DebugEngine a try and it is very good. Sometimes it provides stack traces strange (method where exception happened, two inner methods of DebugEngine and then the real stack trace) but it's really easy to install and use. It is also unaffected by the two issues I have with MadExcept (TThread.FatalException and Exception.InnerException is rendered useless). Thank you very much for the feedback. Since you mentioned you just "gave it a try", you are not using it anymore, and/or didn't use it in production? I'm also interested in knowing how it is (and the other mentioned tools) when it comes to performance and stability, since I intend to use it in server applications. Share this post Link to post
aehimself 396 Posted May 14, 2021 1 hour ago, Wagner Landgraf said: Thank you very much for the feedback. Since you mentioned you just "gave it a try", you are not using it anymore, and/or didn't use it in production? I'm also interested in knowing how it is (and the other mentioned tools) when it comes to performance and stability, since I intend to use it in server applications. I had ~2 releases with DebugEngine, then I switched back to MadExcept. It's stack traces are less messed up when packed with UPX. I'd personally prefer DebugEngine as it is not installing hooks (thus - not having the issues I mentioned) getting stack traces is it's main purpose. Performance wise - you won't feel a difference tbh. None of these tools will make your application less stable or significantly slower. 1 Share this post Link to post
Uwe Raabe 2057 Posted May 14, 2021 2 hours ago, Wagner Landgraf said: and since DebugEngine is provided by the madExcept author What makes you think that? madExcept is from Matthias Rauen (aka madshi) and DebugEngine is from Mahdi Safsafi. Share this post Link to post
Anders Melander 1783 Posted May 14, 2021 2 minutes ago, Uwe Raabe said: What makes you think that? madExcept is from Matthias Rauen (aka madshi) and DebugEngine is from Mahdi Safsafi. Potato, Phothatho 2 Share this post Link to post
Wagner Landgraf 43 Posted May 14, 2021 11 minutes ago, Uwe Raabe said: What makes you think that? madExcept is from Matthias Rauen (aka madshi) and DebugEngine is from Mahdi Safsafi. Indeed, I google'd a lot and for some reason I set a flag they were both the same person. I thought I read it somewhere, but it looks I was mistaken. Share this post Link to post
Anders Melander 1783 Posted May 15, 2021 14 hours ago, Wagner Landgraf said: I thought I read it somewhere, but it looks I was mistaken. You probably read it here: https://wiert.me/2017/08/02/delphi-call-stack-from-exception/ Share this post Link to post
Wagner Landgraf 43 Posted May 15, 2021 12 hours ago, Anders Melander said: You probably read it here: https://wiert.me/2017/08/02/delphi-call-stack-from-exception/ Spot on! Thanks for letting me know I'm not completely insane yet. 😅 1 Share this post Link to post
Edwin Yip 154 Posted May 16, 2021 (edited) 6 hours ago, Wagner Landgraf said: Spot on! Thanks for letting me know I'm not completely insane yet. 😅 Well, well, well, I had that misconception too! While I was puzzled why madshi provides the open source DebugEngine while he already has the madExcept commercial package, but I didn't think too much since Jeroen said so... Maybe people confused by the name 'Mahdi' with 'madshi' the nickname :) I shouldn't have that misconception - because IIRC I asked Mahdi Safsafi and he's a doctor from the Middle-east, while IIRC madshi is in Germany... Edited May 16, 2021 by Edwin Yip 1 Share this post Link to post
Edwin Yip 154 Posted May 16, 2021 18 hours ago, Anders Melander said: You probably read it here: https://wiert.me/2017/08/02/delphi-call-stack-from-exception/ You really have a good memory :D Share this post Link to post
Uwe Raabe 2057 Posted May 16, 2021 2 hours ago, Edwin Yip said: while IIRC madshi is in Germany Indeed! Perhaps my advantage is that I had communicated with Matthias personally in German, so I never stumbled upon the name similarity. Share this post Link to post
Der schöne Günther 316 Posted May 17, 2021 (edited) On 5/14/2021 at 8:04 PM, Wagner Landgraf said: but I was willing to have something lighter and simpler That's is what we usually do. Initially, we had the good old JCL. It was the only thing we used it for. Later, we figured out we don't really need it at all, it was sufficient to hook into a System.ExceptObjProc (and one or two other procedure variables) to point to our own exception handler. There, you can easily acquire the stack trace with with FastMM_Fulldebugmode.dll. It exports three handy methods to acquire the "frame based" and "raw" stack trace and, of course, to just get a string for the textual representation. Internally, the binary also uses the JCL, but can be recompiled to use Eureka or madExcept. At least, it saves us from installing the JCL at all, and the DLL is just a few kilobytes. Pretty happy with that, since we also have full control of how the stack trace is formatted and what happens with it, once an exception gets raised... Edited May 17, 2021 by Der schöne Günther Share this post Link to post
Edwin Yip 154 Posted May 17, 2021 (edited) 1 hour ago, Der schöne Günther said: That's is what we usually do. Initially, we had the good old JCL. It was the only thing we used it for. Later, we figured out we don't really need it at all, it was sufficient to hook into a System.ExceptObjProc (and one or two other procedure variables) to point to our own exception handler. There, you can easily acquire the stack trace with with FastMM_Fulldebugmode.dll. It exports three handy methods to acquire the "frame based" and "raw" stack trace and, of course, to just get a string for the textual representation. Found the detailed steps: https://stackoverflow.com/a/1130506/133516 Edit 1: Would love to see an example of using `System.ExceptObjProc` and the other related procedure variables ;) Edited May 17, 2021 by Edwin Yip Share this post Link to post
Fr0sT.Brutal 900 Posted May 17, 2021 (edited) FWIW, there's a poor man's OS-based solution that gets addresses only const DBG_STACK_LENGTH = 32; type TDbgInfoStack = array[0..DBG_STACK_LENGTH - 1] of Pointer; PDbgInfoStack = ^TDbgInfoStack; {$IFDEF MSWINDOWS} function RtlCaptureStackBackTrace(FramesToSkip: ULONG; FramesToCapture: ULONG; BackTrace: Pointer; BackTraceHash: PULONG): USHORT; stdcall; external 'kernel32.dll'; {$ENDIF} {$IFDEF MSWINDOWS} procedure GetCallStackOS(var Stack: TDbgInfoStack; FramesToSkip: Integer); begin ZeroMemory(@Stack, SizeOf(Stack)); RtlCaptureStackBackTrace(FramesToSkip, Length(Stack), @Stack, nil); end; {$ENDIF} function CallStackToStr(const Stack: TDbgInfoStack): string; var Ptr: Pointer; begin Result := ''; for Ptr in Stack do if Ptr <> nil then Result := Result + Format('$%p', [Ptr]) + sLineBreak else Break; end; function GetExceptionStackInfo(P: PExceptionRecord): Pointer; begin Result := AllocMem(SizeOf(TDbgInfoStack)); GetCallStackOS(PDbgInfoStack(Result)^, 1); // исключаем саму функцию GetCallStackOS end; function GetStackInfoStringProc(Info: Pointer): string; begin Result := CallStackToStr(PDbgInfoStack(Info)^); end; procedure CleanUpStackInfoProc(Info: Pointer); begin Dispose(PDbgInfoStack(Info)); end; procedure InstallExceptionCallStack; begin SysUtils.Exception.GetExceptionStackInfoProc := GetExceptionStackInfo; SysUtils.Exception.GetStackInfoStringProc := GetStackInfoStringProc; SysUtils.Exception.CleanUpStackInfoProc := CleanUpStackInfoProc; end; procedure UninstallExceptionCallStack; begin SysUtils.Exception.GetExceptionStackInfoProc := nil; SysUtils.Exception.GetStackInfoStringProc := nil; SysUtils.Exception.CleanUpStackInfoProc := nil; end; Edited May 17, 2021 by Fr0sT.Brutal 1 Share this post Link to post
Der schöne Günther 316 Posted May 17, 2021 That does the job of acquiring the stacktrace, yes. This often goes hand in hand with the question "How do I act whenever an exception happens so I can, for example, log them". To intercept both operating system and Delphi exceptions, it should be enough to point System.ExceptObjProc and System.RaiseExceptObjProc to an own handler. Share this post Link to post
Wagner Landgraf 43 Posted May 17, 2021 8 hours ago, Der schöne Günther said: Internally, the binary also uses the JCL, but can be recompiled to use Eureka or madExcept. Thank you for presenting another approach. But that still needs JCL, so in the end, it's using the "JCL way". From what I saw DebugEngine is also very lightweight and even easier to use than this approach. It also looks like it did extra job to gather a "more correct" call stack. It all boils down to reliability, as JCL is widely used, and DebugEngine doesn't seem to be so. I guess I will have to just try it for a while and see it myself. Share this post Link to post
Der schöne Günther 316 Posted May 17, 2021 59 minutes ago, Wagner Landgraf said: But that still needs JCL No, it doesn't. It's just a DLL with no further dependencies. Internally, it might leverage something from the JCL, but it's just a binary 🤷♂️ Share this post Link to post
Wagner Landgraf 43 Posted May 18, 2021 Indeed, it's the dll which is compiled with Jcl. Still, it's the Jcl logic. Share this post Link to post