C++ 异常的调用栈

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3222890/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 12:22:48  来源:igfitidea点击:

Call-stack for exceptions in C++

c++exceptioncallstacktry-catch

提问by Igor Oks

Today, in my C++ multi-platform code, I have a try-catch around every function. In every catch block I add the current function's name to the exception and throw it again, so that in the upmost catch block (where I finally print the exception's details) I have the complete call stack, which helps me to trace the exception's cause.

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

Is it a good practice, or are there better ways to get the call stack for the exception?

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

采纳答案by Igor Oks

No, it is deeply horrible, and I don't see why you need a call stack in the exception itself - I find the exception reason, the line number and the filename of the code where the initial exception occurred quite sufficient.

不,这是非常可怕的,我不明白为什么您需要在异常本身中使用调用堆栈 - 我发现异常原因、行号和发生初始异常的代码的文件名就足够了。

Having said that, if you really must have a stack trace, the thing to do is to generate the call stack info ONCE at the exception throw site. There is no single portable way of doing this, but using something like http://stacktrace.sourceforge.net/combined with and a similar library for VC++ should not be too difficult.

话虽如此,如果你真的必须有一个堆栈跟踪,要做的就是在异常抛出站点生成调用堆栈信息一次。没有单一的可移植方式来做到这一点,但是将http://stacktrace.sourceforge.net/ 之类的东西与 VC++ 的类似库结合使用应该不会太困难。

回答by Michael Aaron Safyan

What you are doing is not good practice. Here's why:

你正在做的不是好的做法。原因如下:

1. It's unnecessary.
If you compile your project in debug mode so that debugging information gets generated, you can easily get backtraces for exception handling in a debugger such as GDB.

1.没必要。
如果您在调试模式下编译项目以便生成调试信息,您可以轻松获得在调试器(如 GDB)中进行异常处理的回溯。

2. It's cumbersome.
This is something you have to remember to add to each and every function. If you happen to miss a function, that could cause a great deal of confusion, especially if that were the function that caused the exception. And anyone looking at your code would have to realize what you are doing. Also, I bet you used something like __FUNC__ or __FUNCTION__ or __PRETTY_FUNCTION__, which sadly to say are all non-standard (there is no standard way in C++ to get the name of the function).

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

3. It's slow.
Exception propagation in C++ is already fairly slow, and adding this logic will only make the codepath slower. This is not an issue if you are using macros to catch and rethrow, where you can easily elide the catch and rethrow in release versions of your code. Otherwise, performance could be a problem.

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

Good practice
While it may not be good practice to catch and rethrow in each and every function to build up a stack trace, it is good practice to attach the file name, line number, and function name at which the exception was originally thrown. If you use boost::exception with BOOST_THROW_EXCEPTION, you will get this behavior for free. It's also good to attach explanatory information to your exception that will assist in debugging and handling the exception. That said, all of this should occur at the time the exception is constructed; once it is constructed, it should be allowed to propagate to its handler... you shouldn't repeatedly catch and rethrow more than stricly necessary. If you need to catch and rethrow in a particular function to attach some crucial information, that's fine, but catching all exceptions in every function and for the purposes of attaching already available information is just too much.

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

回答by Scott Stafford

One solution which may be more graceful is to build a Tracer macro/class. So at the top of each function, you write something like:

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

TRACE()

and the macro looks something like:

宏看起来像:

Tracer t(__FUNCTION__);

and the class Tracer adds the function name to a global stack on construction, and removes itself upon destruction. Then that stack is always available to logging or debugging, maintenance is much simpler (one line), and it doesn't incur exception overhead.

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

Examples of implementations include things like http://www.drdobbs.com/184405270, http://www.codeproject.com/KB/cpp/cmtrace.aspx, and http://www.codeguru.com/cpp/v-s/debug/tracing/article.php/c4429. Also Linux functions like this http://www.linuxjournal.com/article/6391can do it more natively, as described by this Stack Overflow question: How to generate a stacktrace when my gcc C++ app crashes. ACE's ACE_Stack_Trace may be worth looking at too.

实现的示例包括像http://www.drdobbs.com/184405270http://www.codeproject.com/KB/cpp/cmtrace.aspxhttp://www.codeguru.com/cpp/vs /debug/tracing/article.php/c4429。像这样http://www.linuxjournal.com/article/6391这样的Linux 函数也可以更本地化,如堆栈溢出问题所述:How to generate a stacktrace when my gcc C++ app crashes。ACE 的 ACE_Stack_Trace 可能也值得一看。

Regardless, the exception-handling method is crude, inflexible, and computationally expensive. Class-construction/macro solutions are much faster and can be compiled out for release builds if desired.

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

回答by Scott Stafford

The answer to all your problems is a good debugger, usually http://www.gnu.org/software/gdb/on linux or Visual Studio on Windows. They can give you stack traces on demand at any point in the program.

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

Your current method is a real performance and maintenance headache. Debuggers are invented to accomplish your goal, but without the overhead.

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

回答by Scott Stafford

There's a nice little project that gives a pretty stack trace:

有一个不错的小项目,提供了一个漂亮的堆栈跟踪:

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

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

回答by zooropa

Look at this SO Question. This might be close to what you're looking for. It isn't cross-platform but the answer gives solutions for gcc and Visual Studio.

看看这个SO Question。这可能与您要查找的内容相近。它不是跨平台的,但答案为 gcc 和 Visual Studio 提供了解决方案。

回答by Boris

One more project for stack-trace support: ex_diag. There are no macros, cross-platform is present, no huge code needs, tool is fast, clear and easy in use.

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

Here you need only wrap objects, which are need to trace, and they will be traced if exception occurs.

这里只需要对需要跟踪的对象进行包装,如果发生异常就会被跟踪。

回答by Mark Lakata

Linking with the libcsdbg library (see https://stackoverflow.com/a/18959030/364818for original answer) looks like the cleanest way of getting a stack trace without modifying your source code or 3rd party source code (ie STL).

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

This uses the compiler to instrument the actual stack collection, which is really want you want to do.

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

I haven't used it and it is GPL tainted, but it looks like the right idea.

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

回答by GPMueller

While quite a few counter-arguments have been made in the answers here, I want to note that since this question was asked, with C++11, methods have been added which allow you to get nice backtraces in a cross-platform way and without the need for a debugger or cumbersome logging:

虽然在这里的答案中已经提出了很多反驳,但我想指出,自从提出这个问题以来,使用C++11,已经添加了一些方法,这些方法允许您以跨平台的方式获得很好的回溯,并且无需调试器或繁琐的日志记录:

Use std::nested_exceptionand std::throw_with_nested

使用std::nested_exceptionstd::throw_with_nested

It is described on StackOverflow hereand here, how you can get a backtrace on your exceptionsinside your code by simply writing a proper exception handler which will rethrow nested exceptions. It will, however, require that you insert try/catchstatements at the functions you wish to trace.

此处此处的StackOverflow 上进行描述,您可以通过简单地编写将重新抛出嵌套异常的适当异常处理程序来获取代码中异常的回溯。但是,它要求您try/catch在要跟踪的函数中插入语句。

Since you can do this with any derived exception class, you can add a lot of information to such a backtrace! You may also take a look at my MWE on GitHubor my "trace" library, where a backtrace would look something like this:

由于您可以使用任何派生的异常类来执行此操作,因此您可以向此类回溯添加大量信息!您还可以查看我在 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"

回答by zooropa

An exception that isn't handled is left for the calling function to handle. That continues until the exception is handled. This happens with or without try/catch around a function call. In other words, if a function is called that isn't in a try block, an exception that happens in that function will automatically be passed up to call stack. So, all you need to do is put the top-most function in a try block and handle the exception "..." in the catch block. That exception will catch all exceptions. So, your top-most function will look something like

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

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

If you want to have specific code blocks for certain exceptions, you can do that too. Just make sure those occur before the "..." exception catch block.

如果您想为某些异常设置特定的代码块,您也可以这样做。只要确保这些发生在“...”异常捕获块之前。