简体   繁体   中英

rethrow exception preserving backtrace

I need to catch some "fatal" C++ exception , then flush logs and rethrow the former, with its own backtrace .

My current solution, however, displays (correctly) the wrong stacktrace.

#include <exception>
#include <iostream>

void fatal(const std::exception & E)
{
    // do - something - extremely - important
    throw E;
}

int foo()
{
    throw std::runtime_error("yet another foo function");
}

int main()
{
    try
    {
        return foo();
    }
    catch (const std::exception & E)
    {
        fatal(E);
    }
    return -1;
}

Program being wrapped by

  $ cat ./backtrace
  backtrace
  quit

  $ ulimit -c unlimited
  $ ./a.out
  $ gdb -q ./a.out core -x ./backtrace

Result is

Program terminated with signal SIGABRT, Aborted.

..................................................

4 0x00007f496eb53701 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6

5 0x00007f496eb53919 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6

6 0x0000000000400d71 in fatal(std::exception const&) ()

7 0x0000000000400e5b in main ()

I thought rethrowing an exception (by const ref) was a technique to pass the original backtrace; I'm interested in backtracing foo() , not fatal() .

I'm not sure what you are trying to accomplish here, but to rethrow an exception you need to write throw; (without arguments), throw E; will throw a new exception instance by copy constructing it from existing object.

Not sure how any of these may help you with stack traces though. "pass the original backtrace" does not make much sense because exceptions in C++ do not carry any backtraces and by the time your catch block is invoked stack has been already cleanuped.

With your script:

backtrace
quit

... you will only see a stack trace when the inferior is about to exit (or when you use a core file, like in your example, when it has already exited), because you have not told gdb to stop anywhere.

Another approach would be to use the gdb catch throw command, plus a bit of scripting. This way you could capture a stack trace at each throw . You could do it this way:

(gdb) catch throw
Catchpoint 1 (throw)
(gdb) commands
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
> silent
> backtrace
> continue
> end

This will stop at every throw and print a backtrace. However, you did want to not print stack traces from the fatal frame. For this, you could use a gdb convenience function and make the catchpoint conditional:

(gdb) cond 1 $_any_caller_matches("fatal", 10)

(The "10" is just a guess at how many frames may separate fatal from the C++ library innards that handle throwing.)

This question has already been answered, but I want to add that one can create proper backtraces in standard C++11 :

Use std::nested_exception and std::throw_with_nested

It is described on StackOverflow here and here , how you can get a backtrace on your exceptions inside your code without need for a debugger or cumbersome logging, by simply writing a proper exception handler which will rethrow nested exceptions. This requires, however, that you wrap all functions you wish to trace into try/catch blocks, but it lets you do a lot of customisation of what happens and what information is printed. 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 GitHub or my "trace" library , where a backtrace would look something like this:

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"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM