捕获异常后的堆栈跟踪

Stack trace after catching exception

本文关键字:跟踪 堆栈 捕获异常      更新时间:2023-10-16

我也想在使用调试器的情况下抛出异常后的stacktrace。通常,当从未捕获异常时,调试器在收到SIGABRT后停止了程序,我可以看到整个堆栈跟踪并认识出异常的原因。

但是,如何在捕获后如何诊断例外原因?

#include <iostream>
#include <stdexcept>
void foo() {
    throw std::runtime_error("An error message");
}
int main() {
    try {
        foo();
    } catch (const std::exception &e) {
        std::cerr << e.what(); // add breakpoint here
    }        
    return 0;
}

在捕获异常之后,在捕获部分中添加断点自然会停止程序,但是堆栈跟踪不包含foo()调用,因此无法诊断出异常的原因。

请注意,示例确实很小且简单。使用复杂和嵌套的呼叫,try部分中某个地方出现异常的信息实际上是没有用的。而且我不能简单地避免捕获异常,因为如果我不抓住它,那么它会被我使用的框架捕获,并且丢失了。

但是,捕获后的原因是如何诊断出例外原因的?

你不能。在捕获点时,堆栈已经解开了。如果您想诊断出投掷的原因,则需要在投掷时将它们包含在例外(如果您如此倾向,这可能包括完整的堆栈跟踪,但这将是依赖系统的)。

您可能要考虑的其他事情:

a)在API呼叫的呼叫网站上添加更多的前提检查(您无法在呼叫中修改代码)

b)在您控制的功能和方法中添加前提检查(断言或投掷)。如果性能是问题,您可以在发布版本中将它们删除。

c)包括抛出异常信息的全部原因。

我自己从未使用过它,但可能值得一看:

https://www.codeproject.com/articles/11132/walking-the-callstack

特别是在段落

显示异常的呼叫堆

使用此StackWalker,您也可以在 例外处理程序。您只需要写一个滤波器功能 堆栈步行。

如果您正在使用自己的异常,那么我的libexcept库可能对您有用。该图书馆提供几堂课。基类能够在实例化时收集堆栈跟踪。99%的时间,这为您提供了调试问题所需的所有信息。当您只知道例外发生的地方,但不称呼它的方式时,很难难以解决问题。(是的!因为可以以20种不同的方式调用相同的函数,因此只知道确切的例外情况通常还不够。)

在一个项目中,只有在某些事物确实不好发生时,您才能放置异常,甚至可以将其保存在您的发布版本中。然后,您可以记录客户端日志中的堆栈跟踪,并使数百次更容易调试远程/结束客户端 crashes

我在启动板上有一个ubuntu 16.04的二进制(预编译版本)(在APT列表中安装PPA,然后安装libexcept_1.0.5.0~xenial_amd64.deb或当前的任何版本。)

使用,源自提供的两个类别之一:

class my_exception : public libexcept::logic_exception_t
{
   ...
};

,在您的catch()中,您可以使用get_stack_trace()打印堆栈跟踪以获取字符串向量。在我们的情况下,我们实际上将其发送到我们的日志中。

try
{
    ...
    throw my_exception("what happened?!");
    ...
}
catch(my_exception const & e)
{
    stack_trace_t const & stack(e.get_stack_trace());
    for(auto s : stack)
    {
        SNAP_LOG_ERROR(*s);
    }
}

显然,如果您要在许多地方记录堆栈跟踪,您可能会想要一个功能。

除了您不会产生的例外,您实际上无法做太多。但是,如果您想编写包装器,则可以重新浏览自己的例外,以至少缩小例外的位置。

说您正在使用第三方library,并期望library偶尔投掷...

try
{
    library::func();   // may throw
}
catch(library::exception const & e)
{
    throw my_exception(e.what());   // converted exception
}

现在,您至少有一个低水平捕获的堆栈跟踪。

这个问题很旧,但是我想补充一点,可以在标准C 11 中进行此类跟踪:

使用std::nested_exceptionstd::throw_with_nested

在此处和此处的stackoverflow上进行了描述,如何在代码内部进行异常在不需要调试器或笨拙的日志记录的情况下进行回溯,只需编写适当的例外处理程序即嵌套异常。

由于您可以在任何派生的异常类中执行此操作,因此您可以在此类回溯中添加很多信息!在相应的catch陈述中添加一个断点,在您生成回溯。您也可以在Github上查看我的MWE,在这里看起来像这样:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

或我的"跟踪"库,它与此答案中给出的库相似,但是跨平台。