捕获异常后的堆栈跟踪

问题描述 投票:0回答:5

当然,我也想使用调试器查看抛出异常后的堆栈跟踪。通常,当异常从未被捕获时,调试器在收到

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
部分中某处发生异常的信息实际上是无用的。我不能简单地避免捕获异常,因为如果我没有捕获它,那么它就会被我正在使用的框架捕获,并且堆栈跟踪会丢失。

c++ exception stack-trace
5个回答
0
投票

但是捕获异常后如何诊断异常原因呢?

你不能。堆栈在捕获时已被展开。如果您想诊断抛出的原因,您需要将它们包含在抛出点的异常中(如果您愿意,这可能包括完整的堆栈跟踪,但这将取决于系统)。

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

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

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

c) 在抛出异常的消息中包含抛出的完整原因。


0
投票

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

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

尤其是段落

显示异常的调用堆栈

使用此 StackWalker,您还可以在 异常处理程序。您只需要编写一个过滤函数即可 堆栈遍历。


0
投票

如果您正在处理自己的异常,那么我的 libexcept 库可能对您有用。这个图书馆提供了一些课程。基类能够在实例化时收集堆栈跟踪。 99% 的情况下,这会为您提供调试问题所需的所有信息。当您只知道异常发生在哪里,而不知道异常是如何调用时,知道如何解决问题可能会困难得多。 (是的!因为同一个函数可能以 20 种不同的方式调用,所以仅仅确切地知道发生了哪个异常通常是不够的。)

在一个项目中,只有当发生“非常糟糕”的事情时才抛出异常,这甚至可以保留在您的发布版本中。然后,您可以在客户端日志中记录堆栈跟踪,并使调试远程/最终客户端崩溃变得容易数百倍。 我在 Launchpad 上有一个适用于 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); } }

显然,如果您要在很多地方记录堆栈跟踪,您可能会需要一个函数。

对于您不生成的异常,您实际上无能为力。但是,如果您想编写一个包装器,则可以重新抛出自己的异常,以便至少缩小异常来源的位置。

假设您正在使用第 3 方

library

并期望

library
偶尔会抛出...

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

现在你至少有一个来自这个低级捕获的堆栈跟踪。


0
投票
标准 C++11 中进行这样的跟踪

使用

std::nested_exception
std::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"

或者我的
“trace”库

,它与这个答案中给出的类似,但是是跨平台的。


0
投票
cpptrace

提供跟踪异常对象: #include <cpptrace/cpptrace.hpp> void foo() { throw cpptrace::runtime_error("An error message"); }

© www.soinside.com 2019 - 2024. All rights reserved.