捕获异常后的堆栈跟踪

时间:2016-12-04 19:21:33

标签: c++ exception stack-trace

我希望在使用调试器抛出异常后看到堆栈跟踪。通常,当从未捕获异常时,调试器在收到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;
}

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

请注意,该示例非常简单。对于复杂和嵌套的调用,try部分中某处发生异常的信息实际上是无用的。我不能简单地避免捕获异常,因为如果我没有捕获它,那么它会被我正在使用的框架捕获并且堆栈跟踪丢失。

4 个答案:

答案 0 :(得分:0)

  

然而,如何在捕获异常之后诊断异常的原因?

你做不到。堆栈已经在捕获点解开。如果你想要诊断抛出的原因,你需要将它们包含在抛出点的异常中(如果你如此倾向,这可能包括完整的堆栈跟踪,但这将取决于系统)。

您可能需要考虑的其他事项:

a)在API调用的调用站点添加更多前置条件检查(您无法修改调用中的代码)

b)在您控制的函数和方法中添加前置条件检查(断言或抛出)。如果性能问题,您可以在发布版本中删除它们。

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

答案 1 :(得分:0)

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

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

特别是在段落

  

显示异常的callstack

     

使用此StackWalker,您还可以在其中显示callstack   异常处理程序你只需要编写一个过滤函数   堆栈行走。

答案 2 :(得分:0)

如果您要处理自己的异常,那么我的libexcept库可能对您有用。该库提供了一些课程。基类能够在实例化时收集堆栈跟踪。 99%的时间,这将为您提供调试问题所需的所有信息。当您仅知道异常发生的位置,而不是异常的调用方式时,知道如何解决问题可能会更加困难。 (是的!因为可能以20种不同的方式调用同一个函数,所以仅了解发生的异常通常是不够的。)

在一个项目中,仅当确实发生了严重错误时才引发异常,这甚至可以保留在您的发行版中。然后,您可以将堆栈跟踪记录记录在客户端的日志中,并使调试远程/最终客户端崩溃容易百倍。

我有一个binary (pre-compiled version) for Ubuntu 16.04 on Launchpad(将PPA安装在您的apt列表中,然后安装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
}

现在,您至少已从此低级捕获获得了堆栈跟踪。

答案 3 :(得分:0)

这个问题已经很老了,但我想补充一点,就是可以在标准C ++ 11 中进行这样的跟踪:

使用std::nested_exceptionstd::throw_with_nested

在StackOverflow herehere上对此进行了描述,如何通过简单的方法在代码内获取异常的回溯,而无需调试器或繁琐的日志记录编写适当的异常处理程序,该处理程序将抛出嵌套异常。

由于您可以使用任何派生的异常类执行此操作,因此可以向此类回溯中添加很多信息! 在生成回溯的相应catch语句中添加断点应该可以正常工作。 您也可以看看我的MWE on GitHub,回溯看起来像这样:

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"

或我的"trace" library,与this answer中给出的内容类似,但是是跨平台的。