【问题标题】:Delphi obtain stack trace after exceptionDelphi在异常后获取堆栈跟踪
【发布时间】:2013-04-08 22:18:59
【问题描述】:

我试图弄清楚如何在 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;

这是我得到的结果:

001A63E3: TApplication.HandleException (Forms)
00129072: StdWndProc (Classes)
001A60B0: TApplication.ProcessMessage (Forms)

这显然不是我想要的,尽管它是正确的。我想在抛出异常之前检索堆栈,或者换句话说,在 OnException 调用之前(之后也可以)的内容。

有什么办法吗?

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

【问题讨论】:

  • 您在阅读汇编方面的能力如何? ;-)
  • 我一直不明白为什么 RTL 没有内置这个...
  • @WarrenP:相当不错,或者至少足以解决我认为的问题,但我似乎不走运。 :(
  • “我想知道它是怎么做的。” - 阅读来源。 JCL 源是开放的,对于 mORMot 和一些免费的记录器和分析器也是如此。有许多 FLOSS 产品结合了展开堆栈。你可以阅读他们的代码并从中学习。
  • 如果您需要做某事而没有时间自己解决,您可以联系 madshi(MadExcept 的作者)并请他设计解决方案。这就是我会做的。这东西很难。

标签: delphi exception stack-trace


【解决方案1】:

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

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

【讨论】:

  • Embacadero 最适合实现堆栈跟踪,因为它首先制作了编译器。在公开回调之前,第三方挂钩是唯一的选择。回调到位后,不提供原生解决方案是一种逃避恕我直言。 Embarcadero 可以而且应该,但他们似乎选择不这样做。
  • 至少,我希望看到 Embarcadero 向 RTL 添加一个新单元,如果需要,用户可以选择将其添加到他们的 uses 子句中,并且它将自己的处理程序分配给回调和生成基本调用堆栈信息。对于那些不想购买/安装第三方记录器的用户。
  • @WarrenP 我不想看到那个。因为那时 Emba 将负责开发它。而且我认为最好由 Mathias 掌舵。
  • @David Heffernan:“这是第三方图书馆的工作”。这与他们在 Delphi Help 中采用的方法相同吗?这是用户的工作? 'Embarcadero 没有关于这个话题的更多信息......' 如果 Embarcadero 没有,谁应该有这些信息?不可原谅。同样在这里——正如雷米所说的“他们选择不这样做”——也就是说,他们选择了偷工减料,让我们没有任何信息——像往常一样..
  • 只有在 Embarcadero 的世界里,提供异常堆栈跟踪才能被认为是第三方库的工作。
【解决方案2】:

我想检索堆栈,因为它就在 抛出异常,或者换句话说,之前的内容 (之后也会这样做)OnException 调用。

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

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

此外,堆栈遍历比您的基本代码高级得多。我不是贬义的意思,只是在 x86 上的堆栈遍历是一件棘手的事情,而且 madExcept 代码磨练得非常好。

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

【讨论】:

    猜你喜欢
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 2011-06-01
    • 2011-01-05
    • 1970-01-01
    • 1970-01-01
    • 2017-08-06
    • 2010-09-13
    相关资源
    最近更新 更多