C ++中异常的调用栈

时间:2010-07-11 11:31:46

标签: c++ exception callstack try-catch

今天,在我的C ++多平台代码中,我对每个函数都有一个try-catch。在每个catch块中,我将当前函数的名称添加到异常中并再次抛出它,以便在最上面的catch块(我最终打印异常的详细信息)中,我有完整的调用堆栈,这有助于我跟踪异常的原因。

这是一个好习惯,还是有更好的方法来获取异常的调用堆栈?

10 个答案:

答案 0 :(得分:24)

不,它非常可怕,我不明白为什么你需要在异常中调用堆栈 - 我发现异常原因,初始异常发生的代码的行号和文件名已经足够了。

话虽如此,如果你真的必须有一个堆栈跟踪,那么要做的是在异常抛出站点生成调用堆栈信息ONCE。没有一种可移植的方法可以做到这一点,但使用类似http://stacktrace.sourceforge.net/和VC ++的类似库这样的东西应该不会太困难。

答案 1 :(得分:21)

你在做什么不是好习惯。原因如下:

1。这是不必要的。
如果在调试模式下编译项目以便生成调试信息,则可以轻松地在调试器(例如GDB)中进行异常处理的回溯。

2。这很麻烦。
这是你必须记住添加到每个功能的东西。如果你碰巧错过了一个函数,那么可能会引起很多混乱,特别是如果那是导致异常的函数的话。任何看你的代码的人都必须意识到你在做什么。另外,我打赌你使用了像__FUNC__或__FUNCTION__或__PRETTY_FUNCTION__之类的东西,遗憾的是这些都是非标准的(C ++中没有标准方法来获取函数的名称)。

3。这很慢。
C ++中的异常传播已经相当慢,添加此逻辑只会使代码路径变慢。如果您使用宏来捕获和重新抛出,这不是问题,您可以轻松地删除捕获并重新抛出代码的发行版本。否则,性能可能会成为一个问题。

良好做法
虽然捕获和重新抛出每个函数来构建堆栈跟踪可能不是一个好习惯,但最好附加最初抛出异常的文件名,行号和函数名。如果你在BOOST_THROW_EXCEPTION中使用boost :: exception,你将免费获得这种行为。将解释性信息附加到您的异常也有助于调试和处理异常。也就是说,所有这些都应该在异常构建时发生;一旦它被构造,它应该被允许传播到它的处理程序......你不应该重复捕获和重新抛出超过严格必要的。如果你需要捕获并重新抛出一个特定的函数来附加一些关键信息,那很好,但是在每个函数中捕获所有异常并且为了附加已经可用的信息的目的太多了。

答案 2 :(得分:7)

可能更优雅的一种解决方案是构建Tracer宏/类。所以在每个函数的顶部,你写了类似的东西:

TRACE()

,宏看起来像:

Tracer t(__FUNCTION__);

并且类Tracer在构造时将函数名称添加到全局堆栈,并在销毁时自行删除。然后该堆栈始终可用于记录或调试,维护更简单(一行),并且不会产生异常开销。

实施示例包括http://www.drdobbs.com/184405270http://www.codeproject.com/KB/cpp/cmtrace.aspxhttp://www.codeguru.com/cpp/v-s/debug/tracing/article.php/c4429等内容。此类http://www.linuxjournal.com/article/6391之类的Linux函数也可以更原生地执行此操作,如此Stack Overflow问题所述:How to generate a stacktrace when my gcc C++ app crashes。 ACE的ACE_Stack_Trace也值得关注。

无论如何,异常处理方法粗糙,不灵活且计算成本高。类构造/宏解决方案要快得多,如果需要,可以编译出版本。

答案 3 :(得分:2)

所有问题的答案都是一个很好的调试器,通常是Linux上的http://www.gnu.org/software/gdb/或Windows上的Visual Studio。它们可以在程序中的任何位置按需提供堆栈跟踪。

您当前的方法是一个真正的性能和维护问题。发明调试器是为了实现您的目标,但没有开销。

答案 4 :(得分:1)

看看这个SO Question。这可能接近你正在寻找的东西。它不是跨平台的,但答案为gcc和Visual Studio提供了解决方案。

答案 5 :(得分:0)

未处理的异常留待调用函数处理。这种情况一直持续到处理异常为止。无论是否有函数调用的try / catch都会发生这种情况。换句话说,如果调用的函数不在try块中,则该函数中发生的异常将自动传递给调用堆栈。因此,您需要做的就是将最顶层的函数放在try块中,并在catch块中处理异常“...”。该异常将捕获所有异常。所以,你最顶层的功能看起来像

int main()
{
  try
  {
    top_most_func()
  }
  catch(...)
  {
    // handle all exceptions here
  }
}

如果您想为某些例外设置特定的代码块,您也可以这样做。只需确保它们出现在“...”异常捕获块之前。

答案 6 :(得分:0)

有一个很好的小项目可以提供漂亮的堆栈跟踪:

https://github.com/bombela/backward-cpp

答案 7 :(得分:0)

支持堆栈跟踪的另一个项目:ex_diag。没有宏,存在跨平台,没有巨大的代码需求,工具快速,清晰,易于使用。

在这里,您只需要追踪需要跟踪的对象,如果发生异常,它们将被跟踪。

答案 8 :(得分:0)

与libcs​​dbg库链接(请参阅https://stackoverflow.com/a/18959030/364818获取原始答案)看起来像是获取堆栈跟踪的最简洁方法,而无需修改源代码或第三方源代码(即STL)。

这使用编译器来检测实际的堆栈集合,这真的是你想要做的。

我还没有使用它并且它受到了GPL的污染,但它看起来是正确的想法。

答案 9 :(得分:0)

尽管此处的答案中有很多反驳的观点,但我想指出的是,由于使用 C ++ 11 提出了此问题,因此添加了一些方法,使您可以以跨平台的方式获得良好的回溯,而无需调试器或繁琐的日志记录:

使用std::nested_exceptionstd::throw_with_nested

它在StackOverflow herehere上进行了介绍,如何通过简单地编写适当的异常处理程序(将其重新抛出嵌套)来在代码内对异常进行回溯例外情况。 但是,它将要求您在要跟踪的函数中插入try/catch语句。

由于您可以使用任何派生的异常类执行此操作,因此可以向此类回溯中添加很多信息! 您还可以看看我的MWE on GitHub或我的"trace" library,其中的回溯看起来像这样:

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"