Delphi在异常后获取堆栈跟踪

Orw*_*ell 8 delphi exception stack-trace

我试图弄清楚如何在Delphi中抛出异常后获取堆栈跟踪.但是,当我尝试使用下面的函数在Application.OnException事件中读取堆栈时,堆栈似乎已被刷新并被抛出过程替换.

function GetStackReport: AnsiString;
var
    retaddr, walker: ^pointer;
begin

    // ...

    // History of stack, ignore esp frame
    asm
        mov walker, ebp
    end;

    // assume return address is present above ebp
    while Cardinal(walker^) <> 0 do begin
        retaddr := walker;
        Inc(retaddr);
        result := result + AddressInfo(Cardinal(retaddr^));
        walker := walker^;
    end;
end;
Run Code Online (Sandbox Code Playgroud)

这是我得到的结果:

001A63E3: TApplication.HandleException (Forms)
00129072: StdWndProc (Classes)
001A60B0: TApplication.ProcessMessage (Forms)
Run Code Online (Sandbox Code Playgroud)

这显然不是我想要的,虽然它是正确的.我想在抛出异常之前检索堆栈,或者换句话说,之前(之后也会执行)OnException调用的内容.

有没有办法做到这一点?

我知道我正在重新发明轮子,因为在madExcept/Eurekalog/jclDebug上的人已经这样做了,但我想知道它是如何完成的.

Rem*_*eau 17

无法从OnException事件内部手动获取可行的堆栈跟踪.正如您已经注意到的那样,错误发生时的堆栈在触发事件时已经消失.您要查找的内容需要在引发异常时获取堆栈跟踪.第三方异常记录器,如MadExcept,EurekaLog等,通过挂钩RTL本身内部的关键函数和核心异常处理程序来处理这些细节.

在最近的Delphi版本中,SysUtils.Exception该类确实具有公共StackTraceStackInfo属性,这在OnException事件中很有用,除了Embarcadero选择不以原因未知原因实现这些属性的事实.它需要第三方异常记录器将处理程序分配给Exception类公开的各种回调,以生成属性的堆栈跟踪数据.但是,如果您安装了JclDebug,那么您可以在自己的代码中提供自己的回调处理程序,这些处理程序使用JCL的堆栈跟踪函数来生成属性的堆栈数据.

  • Embacadero最适合实现堆栈跟踪,因为它是首先构建编译器的堆栈跟踪.在暴露回调之前,第三方挂钩是唯一的选择.随着回调到位,不提供本机解决方案是一个警察恕我直言.Embarcadero可以而且应该,但他们似乎选择不这样做. (16认同)
  • 只有在Embarcadero的世界中才能提供异常堆栈跟踪,才能被视为第三方库的工作. (5认同)
  • 至少,我希望看到Embarcadero向RTL添加一个新单元,如果需要,用户可以选择将其添加到`uses`子句中,它会将自己的处理程序分配给回调并生成基本的调用堆栈信息.对于那些不想购买/安装第三方记录器的用户. (4认同)
  • @WarrenP我不希望看到这一点.因为Emba将负责开发它.而且我认为掌握Mathias会更好. (4认同)
  • @David Heffernan:"这是第三方图书馆的工作".这与Delphi帮助采用的方法相同吗?这是用户的工作?'Embarcadero没有关于这个话题的进一步信息......'如果Embarcadero没有,谁应该有这些信息?不可原谅的.同样在这里 - 正如雷米说的那样"他们选择不这样做" - 也就是说,他们选择偷工减料而没有信息 - 像往常一样...... (3认同)
  • 我认为他们应该给Madshi一堆现金,然后建立MadExcept.(当然那会剔除EurekaLog的忠实粉丝,但在所有神圣的战争中,似乎我是对的,而你EurekaLog的粉丝也是错的.);-) (2认同)

Dav*_*nan 7

我想在抛出异常之前检索堆栈,或者换句话说,之前(之后也会执行)OnException调用的内容.

实际上,在OnException调用之前,您不需要堆栈.这就是你已经拥有的.您希望堆栈位于引发异常的位置.这需要在加注后尽快发生堆栈跟踪.在OnException调用中为时已晚,因为异常已一直传播到顶级处理程序.

madExcept通过挂钩处理异常的所有RTL函数来工作.它挂钩了最低级别的功能.这需要一些认真的努力才能实现.通过这些例程挂钩,代码可以捕获堆栈跟踪等.请注意,挂钩是特定于版本的,需要对RTL进行逆向工程.

堆叠步行比基本代码更先进.我并不是说这是一种贬义的方式,只是在x86上行走的栈是一件棘手的事情而且madExcept代码得到了很好的磨练.

这是基本的想法.如果您想了解更多信息,可以免费获取JclDebug的源代码.或者购买madExcept并获取其来源.