如何找到在c++中抛出异常的位置?

时间:2022-10-30 09:14:11

I have a program that throws an uncaught exception somewhere. All I get is a report of an exception being thrown, and no information as to where it was thrown. It seems illogical for a program compiled to contain debug symbols not to notify me of where in my code an exception was generated.

我有一个程序在某个地方抛出一个未捕获的异常。我所得到的只是一个异常被抛出的报告,以及它被抛出的位置的信息。如果一个编译为包含调试符号的程序不通知我在代码中哪里生成了异常,这似乎是不合逻辑的。

Is there any way to tell where my exceptions are coming from short of setting 'catch throw' in gdb and calling a backtrace for every single thrown exception?

有什么方法可以告诉我的异常来自哪里,而不是在gdb中设置“catch抛出”,并为每个抛出的异常调用回溯?

7 个解决方案

#1


62  

Here's some info that may be of use in debugging your problem

If an exception is uncaught, the special library function std::terminate() is automatically called. Terminate is actually a pointer to a function and default value is the Standard C library function std::abort(). If no cleanups occur for an uncaught exception, it may actually be helpful in debugging this problem as no destructors are called.
†It is implementation-defined whether or not the stack is unwound before std::terminate() is called.

如果未捕获异常,则自动调用特殊库函数std::terminate()。Terminate实际上是一个指向函数的指针,默认值是标准的C库函数std::abort()。如果未捕获的异常没有发生清理,那么在调试这个问题时可能会很有帮助,因为不会调用析构函数。在调用std::terminate()之前堆栈是否解绕都是实现性定义的。


A call to abort() is often useful in generating a core dump that can be analyzed to determine the cause of the exception. Make sure that you enable core dumps via ulimit -c unlimited (Linux).

对abort()的调用通常在生成可以分析以确定异常原因的核心转储时非常有用。确保通过ulimit -c unlimited (Linux)启用核心转储。


You can install your own terminate() function by using std::set_terminate(). You should be able to set a breakpoint on your terminate function in gdb. You may be able to generate a stack backtrace from your terminate() function and this backtrace may help in identifying the location of the exception.

您可以使用std::set_terminate()安装自己的terminate()函数。您应该能够在gdb中的终止函数上设置断点。您可以从terminate()函数中生成堆栈回溯,而这个回溯可以帮助确定异常的位置。

There is a brief discussion on uncaught exceptions in Bruce Eckel's Thinking in C++, 2nd Ed that may be helpful as well.

关于布鲁斯·埃克尔在C++的思想中未被发现的例外情况,有一个简短的讨论,这可能也有帮助。


Since terminate() calls abort() by default (which will cause a SIGABRT signal by default), you may be able to set a SIGABRT handler and then print a stack backtrace from within the signal handler. This backtrace may help in identifying the location of the exception.

由于terminate()调用abort()在默认情况下(在默认情况下将导致SIGABRT信号),您可以设置一个SIGABRT处理程序,然后从信号处理程序中打印一个堆栈回溯。这个回溯可以帮助识别异常的位置。


Note: I say may because C++ supports non-local error handling through the use of language constructs to separate error handling and reporting code from ordinary code. The catch block can be, and often is, located in a different function/method than the point of throwing. It has also been pointed out to me in the comments (thanks Dan) that it is implementation-defined whether or not the stack is unwound before terminate() is called.

注意:我之所以说may,是因为c++支持非本地错误处理,通过使用语言结构来分离错误处理和从普通代码中报告代码。catch块可以而且通常位于与抛出点不同的函数/方法中。在评论(感谢Dan)中,我还指出,在调用terminate()之前,是否解除堆栈是实现定义的。

Update: I threw together a Linux test program called that generates a backtrace in a terminate() function set via set_terminate() and another in a signal handler for SIGABRT. Both backtraces correctly show the location of the unhandled exception.

更新:我将一个名为terminate()的Linux测试程序放在一起,该程序通过set_terminate()函数集生成回溯,而另一个用于SIGABRT的信号处理程序生成回溯。两个回溯都正确地显示了未处理异常的位置。

Update 2: Thanks to a blog post on Catching uncaught exceptions within terminate, I learned a few new tricks; including the re-throwing of the uncaught exception within the terminate handler. It is important to note that the empty throw statement within the custom terminate handler works with GCC and is not a portable solution.

更新2:感谢一篇关于在terminate中捕获未捕获异常的博文,我学到了一些新技巧;包括在终止处理程序中重新抛出未捕获的异常。需要注意的是,自定义终止处理程序中的空抛出语句与GCC一起工作,并且不是一个可移植的解决方案。

Code:

代码:

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif

#include <execinfo.h>
#include <signal.h>
#include <string.h>

#include <iostream>
#include <cstdlib>
#include <stdexcept>

void my_terminate(void);

namespace {
    // invoke set_terminate as part of global constant initialization
    static const bool SET_TERMINATE = std::set_terminate(my_terminate);
}

// This structure mirrors the one found in /usr/include/asm/ucontext.h
typedef struct _sig_ucontext {
   unsigned long     uc_flags;
   struct ucontext   *uc_link;
   stack_t           uc_stack;
   struct sigcontext uc_mcontext;
   sigset_t          uc_sigmask;
} sig_ucontext_t;

void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext) {
    sig_ucontext_t * uc = (sig_ucontext_t *)ucontext;

    // Get the address at the time the signal was raised from the EIP (x86)
    void * caller_address = (void *) uc->uc_mcontext.eip;

    std::cerr << "signal " << sig_num 
              << " (" << strsignal(sig_num) << "), address is " 
              << info->si_addr << " from " 
              << caller_address << std::endl;

    void * array[50];
    int size = backtrace(array, 50);

    std::cerr << __FUNCTION__ << " backtrace returned " 
              << size << " frames\n\n";

    // overwrite sigaction with caller's address
    array[1] = caller_address;

    char ** messages = backtrace_symbols(array, size);

    // skip first stack frame (points here)
    for (int i = 1; i < size && messages != NULL; ++i) {
        std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
    }
    std::cerr << std::endl;

    free(messages);

    exit(EXIT_FAILURE);
}

void my_terminate() {
    static bool tried_throw = false;

    try {
        // try once to re-throw currently active exception
        if (!tried_throw++) throw;
    }
    catch (const std::exception &e) {
        std::cerr << __FUNCTION__ << " caught unhandled exception. what(): "
                  << e.what() << std::endl;
    }
    catch (...) {
        std::cerr << __FUNCTION__ << " caught unknown/unhandled exception." 
                  << std::endl;
    }

    void * array[50];
    int size = backtrace(array, 50);    

    std::cerr << __FUNCTION__ << " backtrace returned " 
              << size << " frames\n\n";

    char ** messages = backtrace_symbols(array, size);

    for (int i = 0; i < size && messages != NULL; ++i) {
        std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
    }
    std::cerr << std::endl;

    free(messages);

    abort();
}

int throw_exception() {
    // throw an unhandled runtime error
    throw std::runtime_error("RUNTIME ERROR!");
    return 0;
}

int foo2() {
    throw_exception();
    return 0;
}

int foo1() {
    foo2();
    return 0;
}

int main(int argc, char ** argv) {
    struct sigaction sigact;

    sigact.sa_sigaction = crit_err_hdlr;
    sigact.sa_flags = SA_RESTART | SA_SIGINFO;

    if (sigaction(SIGABRT, &sigact, (struct sigaction *)NULL) != 0) {
        std::cerr << "error setting handler for signal " << SIGABRT 
                  << " (" << strsignal(SIGABRT) << ")\n";
        exit(EXIT_FAILURE);
    }

    foo1();

    exit(EXIT_SUCCESS);
}

Output:

输出:

my_terminate caught unhanded exception. what(): RUNTIME ERROR!
my_terminate backtrace returned 10 frames

[bt]: (0) ./test(my_terminate__Fv+0x1a) [0x8048e52]
[bt]: (1) /usr/lib/libstdc++-libc6.2-2.so.3 [0x40045baa]
[bt]: (2) /usr/lib/libstdc++-libc6.2-2.so.3 [0x400468e5]
[bt]: (3) /usr/lib/libstdc++-libc6.2-2.so.3(__rethrow+0xaf) [0x40046bdf]
[bt]: (4) ./test(throw_exception__Fv+0x68) [0x8049008]
[bt]: (5) ./test(foo2__Fv+0xb) [0x8049043]
[bt]: (6) ./test(foo1__Fv+0xb) [0x8049057]
[bt]: (7) ./test(main+0xc1) [0x8049121]
[bt]: (8) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (9) ./test(__eh_alloc+0x3d) [0x8048b21]

signal 6 (Aborted), address is 0x1239 from 0x42029331
crit_err_hdlr backtrace returned 13 frames

[bt]: (1) ./test(kill+0x11) [0x42029331]
[bt]: (2) ./test(abort+0x16e) [0x4202a8c2]
[bt]: (3) ./test [0x8048f9f]
[bt]: (4) /usr/lib/libstdc++-libc6.2-2.so.3 [0x40045baa]
[bt]: (5) /usr/lib/libstdc++-libc6.2-2.so.3 [0x400468e5]
[bt]: (6) /usr/lib/libstdc++-libc6.2-2.so.3(__rethrow+0xaf) [0x40046bdf]
[bt]: (7) ./test(throw_exception__Fv+0x68) [0x8049008]
[bt]: (8) ./test(foo2__Fv+0xb) [0x8049043]
[bt]: (9) ./test(foo1__Fv+0xb) [0x8049057]
[bt]: (10) ./test(main+0xc1) [0x8049121]
[bt]: (11) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (12) ./test(__eh_alloc+0x3d) [0x8048b21]

#2


34  

As you say, we can use 'catch throw' in gdb and call 'backtrace' for every single thrown exception. While that's usually too tedious to do manually, gdb allows automation of the process. That allows seeing the backtrace of all exceptions that are thrown, including the last uncaught one:

正如您所说,我们可以在gdb中使用“catch throw”,并为每个抛出的异常调用“backtrace”。虽然手工操作通常太繁琐,但是gdb允许流程的自动化。这允许查看抛出的所有异常的回溯,包括最后一个未捕获的异常:

gdb>

gdb >

set pagination off
catch throw
commands
backtrace
continue
end
run

Without further manual intervention, this generates lots of backtraces, including one for the last uncaught exception:

如果没有进一步的人工干预,这将产生大量的回溯,包括最后一个未捕获的异常:

Catchpoint 1 (exception thrown), 0x00a30 in __cxa_throw () from libstdc++.so.6
#0  0x0da30 in __cxa_throw () from /usr/.../libstdc++.so.6
#1  0x021f2 in std::__throw_bad_weak_ptr () at .../shared_ptr_base.h:76
[...]
terminate called after throwing an instance of 'std::bad_weak_ptr'
  what():  bad_weak_ptr
Program received signal SIGABRT, Aborted.

Here's a great blog post wrapping this up: http://741mhz.com/throw-stacktrace/

以下是一篇很棒的博客文章:http://741mhz.com/throw-stacktrace/

#3


16  

You can create a macro like:

您可以创建如下宏:

#define THROW(exceptionClass, message) throw exceptionClass(__FILE__, __LINE__, (message) )

...and it will give you the location where the exception is thrown (admittedly not the stack trace). It's necessary for you to derive your exceptions from some base class that takes the above constructor.

…它将给出异常抛出的位置(不可否认的是堆栈跟踪)。您必须从使用上述构造函数的基类派生异常。

#4


5  

You did not pass information about what OS / Compiler you use.

您没有传递关于使用什么操作系统/编译器的信息。

In Visual Studio C++ Exceptions can be instrumented.

在Visual Studio c++中,异常可以被检测。

See "Visual C++ Exception-Handling Instrumentation" on ddj.com

在ddj.com上看到“Visual c++异常处理工具”。

My article "Postmortem Debugging", also on ddj.com includes code to use Win32 structured exception handling (used by the instrumentation) for logging etc.

我在ddj.com上的文章“后期调试”包括使用Win32结构化异常处理(由工具使用)进行日志记录等的代码。

#5


4  

You can mark main tight places in your code as noexcept to locate an exception, then use libunwind (just add -lunwind to linker parameters) (tested with clang++ 3.6):

您可以在代码中标记主要的紧凑位置,以定位异常,然后使用libunwind(只添加-lunwind到linker参数)(使用clang++ 3.6测试):

demagle.hpp:

demagle.hpp:

#pragma once

char const *
get_demangled_name(char const * const symbol) noexcept;

demangle.cpp:

demangle.cpp:

#include "demangle.hpp"

#include <memory>

#include <cstdlib>

#include <cxxabi.h>

namespace
{

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#pragma clang diagnostic ignored "-Wexit-time-destructors"
std::unique_ptr< char, decltype(std::free) & > demangled_name{nullptr, std::free};
#pragma clang diagnostic pop

}

char const *
get_demangled_name(char const * const symbol) noexcept
{
    if (!symbol) {
        return "<null>";
    }
    int status = -4;
    demangled_name.reset(abi::__cxa_demangle(symbol, demangled_name.release(), nullptr, &status));
    return ((status == 0) ? demangled_name.get() : symbol);
}

backtrace.hpp:

backtrace.hpp:

#pragma once
#include <ostream>

void
backtrace(std::ostream & _out) noexcept;

backtrace.cpp:

backtrace.cpp:

#include "backtrace.hpp"

#include <iostream>
#include <iomanip>
#include <limits>
#include <ostream>

#include <cstdint>

#define UNW_LOCAL_ONLY
#include <libunwind.h>

namespace
{

void
print_reg(std::ostream & _out, unw_word_t reg) noexcept
{
    constexpr std::size_t address_width = std::numeric_limits< std::uintptr_t >::digits / 4;
    _out << "0x" << std::setfill('0') << std::setw(address_width) << reg;
}

char symbol[1024];

}

void
backtrace(std::ostream & _out) noexcept
{
    unw_cursor_t cursor;
    unw_context_t context;
    unw_getcontext(&context);
    unw_init_local(&cursor, &context);
    _out << std::hex << std::uppercase;
    while (0 < unw_step(&cursor)) {
        unw_word_t ip = 0;
        unw_get_reg(&cursor, UNW_REG_IP, &ip);
        if (ip == 0) {
            break;
        }
        unw_word_t sp = 0;
        unw_get_reg(&cursor, UNW_REG_SP, &sp);
        print_reg(_out, ip);
        _out << ": (SP:";
        print_reg(_out, sp);
        _out << ") ";
        unw_word_t offset = 0;
        if (unw_get_proc_name(&cursor, symbol, sizeof(symbol), &offset) == 0) {
            _out << "(" << get_demangled_name(symbol) << " + 0x" << offset << ")\n\n";
        } else {
            _out << "-- error: unable to obtain symbol name for this frame\n\n";
        }
    }
    _out << std::flush;
}

backtrace_on_terminate.hpp:

backtrace_on_terminate.hpp:

#include "demangle.hpp"
#include "backtrace.hpp"

#include <iostream>
#include <type_traits>
#include <exception>
#include <memory>
#include <typeinfo>

#include <cstdlib>

#include <cxxabi.h>

namespace
{

[[noreturn]]
void
backtrace_on_terminate() noexcept;

static_assert(std::is_same< std::terminate_handler, decltype(&backtrace_on_terminate) >{});

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#pragma clang diagnostic ignored "-Wexit-time-destructors"
std::unique_ptr< std::remove_pointer_t< std::terminate_handler >, decltype(std::set_terminate) & > terminate_handler{std::set_terminate(backtrace_on_terminate), std::set_terminate};
#pragma clang diagnostic pop

[[noreturn]]
void
backtrace_on_terminate() noexcept
{
    std::set_terminate(terminate_handler.release()); // to avoid infinite looping if any
    backtrace(std::clog);
    if (std::exception_ptr ep = std::current_exception()) {
        try {
            std::rethrow_exception(ep);
        } catch (std::exception const & e) {
            std::clog << "backtrace: unhandled exception std::exception:what(): " << e.what() << std::endl;
        } catch (...) {
            if (std::type_info * et = abi::__cxa_current_exception_type()) {
                std::clog << "backtrace: unhandled exception type: " << get_demangled_name(et->name()) << std::endl;
            } else {
                std::clog << "backtrace: unhandled unknown exception" << std::endl;
            }
        }
    }
    std::_Exit(EXIT_FAILURE); // change to desired return code
}

}

There is good article concerning the issue.

关于这个问题有很好的文章。

#6


1  

I've got code to do this in Windows/Visual Studio, let me know if you want an outline. Don't know how to do it for dwarf2 code though, a quick google suggests that there's a function _Unwind_Backtrace in libgcc that probably is part of what you need.

我有在Windows/Visual Studio中做这个的代码,如果你想要一个大纲,请告诉我。虽然不知道如何为矮人f2代码做这个,但是快速的谷歌表明在libgcc中有一个函数_Unwind_Backtrace,这可能是您需要的一部分。

#7


1  

Check this thread, perhaps it helps:

检查这个线程,它可能会有帮助:

Catching all unhandled C++ exceptions?

捕获所有未处理的c++异常?

I made good experiences with that software:

我对这个软件有很好的体验:

http://www.codeproject.com/KB/applications/blackbox.aspx

http://www.codeproject.com/KB/applications/blackbox.aspx

It can print out a stack trace to a file for any unhandled exception.

它可以为任何未处理的异常打印一个堆栈跟踪到文件。

#1


62  

Here's some info that may be of use in debugging your problem

If an exception is uncaught, the special library function std::terminate() is automatically called. Terminate is actually a pointer to a function and default value is the Standard C library function std::abort(). If no cleanups occur for an uncaught exception, it may actually be helpful in debugging this problem as no destructors are called.
†It is implementation-defined whether or not the stack is unwound before std::terminate() is called.

如果未捕获异常,则自动调用特殊库函数std::terminate()。Terminate实际上是一个指向函数的指针,默认值是标准的C库函数std::abort()。如果未捕获的异常没有发生清理,那么在调试这个问题时可能会很有帮助,因为不会调用析构函数。在调用std::terminate()之前堆栈是否解绕都是实现性定义的。


A call to abort() is often useful in generating a core dump that can be analyzed to determine the cause of the exception. Make sure that you enable core dumps via ulimit -c unlimited (Linux).

对abort()的调用通常在生成可以分析以确定异常原因的核心转储时非常有用。确保通过ulimit -c unlimited (Linux)启用核心转储。


You can install your own terminate() function by using std::set_terminate(). You should be able to set a breakpoint on your terminate function in gdb. You may be able to generate a stack backtrace from your terminate() function and this backtrace may help in identifying the location of the exception.

您可以使用std::set_terminate()安装自己的terminate()函数。您应该能够在gdb中的终止函数上设置断点。您可以从terminate()函数中生成堆栈回溯,而这个回溯可以帮助确定异常的位置。

There is a brief discussion on uncaught exceptions in Bruce Eckel's Thinking in C++, 2nd Ed that may be helpful as well.

关于布鲁斯·埃克尔在C++的思想中未被发现的例外情况,有一个简短的讨论,这可能也有帮助。


Since terminate() calls abort() by default (which will cause a SIGABRT signal by default), you may be able to set a SIGABRT handler and then print a stack backtrace from within the signal handler. This backtrace may help in identifying the location of the exception.

由于terminate()调用abort()在默认情况下(在默认情况下将导致SIGABRT信号),您可以设置一个SIGABRT处理程序,然后从信号处理程序中打印一个堆栈回溯。这个回溯可以帮助识别异常的位置。


Note: I say may because C++ supports non-local error handling through the use of language constructs to separate error handling and reporting code from ordinary code. The catch block can be, and often is, located in a different function/method than the point of throwing. It has also been pointed out to me in the comments (thanks Dan) that it is implementation-defined whether or not the stack is unwound before terminate() is called.

注意:我之所以说may,是因为c++支持非本地错误处理,通过使用语言结构来分离错误处理和从普通代码中报告代码。catch块可以而且通常位于与抛出点不同的函数/方法中。在评论(感谢Dan)中,我还指出,在调用terminate()之前,是否解除堆栈是实现定义的。

Update: I threw together a Linux test program called that generates a backtrace in a terminate() function set via set_terminate() and another in a signal handler for SIGABRT. Both backtraces correctly show the location of the unhandled exception.

更新:我将一个名为terminate()的Linux测试程序放在一起,该程序通过set_terminate()函数集生成回溯,而另一个用于SIGABRT的信号处理程序生成回溯。两个回溯都正确地显示了未处理异常的位置。

Update 2: Thanks to a blog post on Catching uncaught exceptions within terminate, I learned a few new tricks; including the re-throwing of the uncaught exception within the terminate handler. It is important to note that the empty throw statement within the custom terminate handler works with GCC and is not a portable solution.

更新2:感谢一篇关于在terminate中捕获未捕获异常的博文,我学到了一些新技巧;包括在终止处理程序中重新抛出未捕获的异常。需要注意的是,自定义终止处理程序中的空抛出语句与GCC一起工作,并且不是一个可移植的解决方案。

Code:

代码:

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif

#include <execinfo.h>
#include <signal.h>
#include <string.h>

#include <iostream>
#include <cstdlib>
#include <stdexcept>

void my_terminate(void);

namespace {
    // invoke set_terminate as part of global constant initialization
    static const bool SET_TERMINATE = std::set_terminate(my_terminate);
}

// This structure mirrors the one found in /usr/include/asm/ucontext.h
typedef struct _sig_ucontext {
   unsigned long     uc_flags;
   struct ucontext   *uc_link;
   stack_t           uc_stack;
   struct sigcontext uc_mcontext;
   sigset_t          uc_sigmask;
} sig_ucontext_t;

void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext) {
    sig_ucontext_t * uc = (sig_ucontext_t *)ucontext;

    // Get the address at the time the signal was raised from the EIP (x86)
    void * caller_address = (void *) uc->uc_mcontext.eip;

    std::cerr << "signal " << sig_num 
              << " (" << strsignal(sig_num) << "), address is " 
              << info->si_addr << " from " 
              << caller_address << std::endl;

    void * array[50];
    int size = backtrace(array, 50);

    std::cerr << __FUNCTION__ << " backtrace returned " 
              << size << " frames\n\n";

    // overwrite sigaction with caller's address
    array[1] = caller_address;

    char ** messages = backtrace_symbols(array, size);

    // skip first stack frame (points here)
    for (int i = 1; i < size && messages != NULL; ++i) {
        std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
    }
    std::cerr << std::endl;

    free(messages);

    exit(EXIT_FAILURE);
}

void my_terminate() {
    static bool tried_throw = false;

    try {
        // try once to re-throw currently active exception
        if (!tried_throw++) throw;
    }
    catch (const std::exception &e) {
        std::cerr << __FUNCTION__ << " caught unhandled exception. what(): "
                  << e.what() << std::endl;
    }
    catch (...) {
        std::cerr << __FUNCTION__ << " caught unknown/unhandled exception." 
                  << std::endl;
    }

    void * array[50];
    int size = backtrace(array, 50);    

    std::cerr << __FUNCTION__ << " backtrace returned " 
              << size << " frames\n\n";

    char ** messages = backtrace_symbols(array, size);

    for (int i = 0; i < size && messages != NULL; ++i) {
        std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
    }
    std::cerr << std::endl;

    free(messages);

    abort();
}

int throw_exception() {
    // throw an unhandled runtime error
    throw std::runtime_error("RUNTIME ERROR!");
    return 0;
}

int foo2() {
    throw_exception();
    return 0;
}

int foo1() {
    foo2();
    return 0;
}

int main(int argc, char ** argv) {
    struct sigaction sigact;

    sigact.sa_sigaction = crit_err_hdlr;
    sigact.sa_flags = SA_RESTART | SA_SIGINFO;

    if (sigaction(SIGABRT, &sigact, (struct sigaction *)NULL) != 0) {
        std::cerr << "error setting handler for signal " << SIGABRT 
                  << " (" << strsignal(SIGABRT) << ")\n";
        exit(EXIT_FAILURE);
    }

    foo1();

    exit(EXIT_SUCCESS);
}

Output:

输出:

my_terminate caught unhanded exception. what(): RUNTIME ERROR!
my_terminate backtrace returned 10 frames

[bt]: (0) ./test(my_terminate__Fv+0x1a) [0x8048e52]
[bt]: (1) /usr/lib/libstdc++-libc6.2-2.so.3 [0x40045baa]
[bt]: (2) /usr/lib/libstdc++-libc6.2-2.so.3 [0x400468e5]
[bt]: (3) /usr/lib/libstdc++-libc6.2-2.so.3(__rethrow+0xaf) [0x40046bdf]
[bt]: (4) ./test(throw_exception__Fv+0x68) [0x8049008]
[bt]: (5) ./test(foo2__Fv+0xb) [0x8049043]
[bt]: (6) ./test(foo1__Fv+0xb) [0x8049057]
[bt]: (7) ./test(main+0xc1) [0x8049121]
[bt]: (8) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (9) ./test(__eh_alloc+0x3d) [0x8048b21]

signal 6 (Aborted), address is 0x1239 from 0x42029331
crit_err_hdlr backtrace returned 13 frames

[bt]: (1) ./test(kill+0x11) [0x42029331]
[bt]: (2) ./test(abort+0x16e) [0x4202a8c2]
[bt]: (3) ./test [0x8048f9f]
[bt]: (4) /usr/lib/libstdc++-libc6.2-2.so.3 [0x40045baa]
[bt]: (5) /usr/lib/libstdc++-libc6.2-2.so.3 [0x400468e5]
[bt]: (6) /usr/lib/libstdc++-libc6.2-2.so.3(__rethrow+0xaf) [0x40046bdf]
[bt]: (7) ./test(throw_exception__Fv+0x68) [0x8049008]
[bt]: (8) ./test(foo2__Fv+0xb) [0x8049043]
[bt]: (9) ./test(foo1__Fv+0xb) [0x8049057]
[bt]: (10) ./test(main+0xc1) [0x8049121]
[bt]: (11) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (12) ./test(__eh_alloc+0x3d) [0x8048b21]

#2


34  

As you say, we can use 'catch throw' in gdb and call 'backtrace' for every single thrown exception. While that's usually too tedious to do manually, gdb allows automation of the process. That allows seeing the backtrace of all exceptions that are thrown, including the last uncaught one:

正如您所说,我们可以在gdb中使用“catch throw”,并为每个抛出的异常调用“backtrace”。虽然手工操作通常太繁琐,但是gdb允许流程的自动化。这允许查看抛出的所有异常的回溯,包括最后一个未捕获的异常:

gdb>

gdb >

set pagination off
catch throw
commands
backtrace
continue
end
run

Without further manual intervention, this generates lots of backtraces, including one for the last uncaught exception:

如果没有进一步的人工干预,这将产生大量的回溯,包括最后一个未捕获的异常:

Catchpoint 1 (exception thrown), 0x00a30 in __cxa_throw () from libstdc++.so.6
#0  0x0da30 in __cxa_throw () from /usr/.../libstdc++.so.6
#1  0x021f2 in std::__throw_bad_weak_ptr () at .../shared_ptr_base.h:76
[...]
terminate called after throwing an instance of 'std::bad_weak_ptr'
  what():  bad_weak_ptr
Program received signal SIGABRT, Aborted.

Here's a great blog post wrapping this up: http://741mhz.com/throw-stacktrace/

以下是一篇很棒的博客文章:http://741mhz.com/throw-stacktrace/

#3


16  

You can create a macro like:

您可以创建如下宏:

#define THROW(exceptionClass, message) throw exceptionClass(__FILE__, __LINE__, (message) )

...and it will give you the location where the exception is thrown (admittedly not the stack trace). It's necessary for you to derive your exceptions from some base class that takes the above constructor.

…它将给出异常抛出的位置(不可否认的是堆栈跟踪)。您必须从使用上述构造函数的基类派生异常。

#4


5  

You did not pass information about what OS / Compiler you use.

您没有传递关于使用什么操作系统/编译器的信息。

In Visual Studio C++ Exceptions can be instrumented.

在Visual Studio c++中,异常可以被检测。

See "Visual C++ Exception-Handling Instrumentation" on ddj.com

在ddj.com上看到“Visual c++异常处理工具”。

My article "Postmortem Debugging", also on ddj.com includes code to use Win32 structured exception handling (used by the instrumentation) for logging etc.

我在ddj.com上的文章“后期调试”包括使用Win32结构化异常处理(由工具使用)进行日志记录等的代码。

#5


4  

You can mark main tight places in your code as noexcept to locate an exception, then use libunwind (just add -lunwind to linker parameters) (tested with clang++ 3.6):

您可以在代码中标记主要的紧凑位置,以定位异常,然后使用libunwind(只添加-lunwind到linker参数)(使用clang++ 3.6测试):

demagle.hpp:

demagle.hpp:

#pragma once

char const *
get_demangled_name(char const * const symbol) noexcept;

demangle.cpp:

demangle.cpp:

#include "demangle.hpp"

#include <memory>

#include <cstdlib>

#include <cxxabi.h>

namespace
{

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#pragma clang diagnostic ignored "-Wexit-time-destructors"
std::unique_ptr< char, decltype(std::free) & > demangled_name{nullptr, std::free};
#pragma clang diagnostic pop

}

char const *
get_demangled_name(char const * const symbol) noexcept
{
    if (!symbol) {
        return "<null>";
    }
    int status = -4;
    demangled_name.reset(abi::__cxa_demangle(symbol, demangled_name.release(), nullptr, &status));
    return ((status == 0) ? demangled_name.get() : symbol);
}

backtrace.hpp:

backtrace.hpp:

#pragma once
#include <ostream>

void
backtrace(std::ostream & _out) noexcept;

backtrace.cpp:

backtrace.cpp:

#include "backtrace.hpp"

#include <iostream>
#include <iomanip>
#include <limits>
#include <ostream>

#include <cstdint>

#define UNW_LOCAL_ONLY
#include <libunwind.h>

namespace
{

void
print_reg(std::ostream & _out, unw_word_t reg) noexcept
{
    constexpr std::size_t address_width = std::numeric_limits< std::uintptr_t >::digits / 4;
    _out << "0x" << std::setfill('0') << std::setw(address_width) << reg;
}

char symbol[1024];

}

void
backtrace(std::ostream & _out) noexcept
{
    unw_cursor_t cursor;
    unw_context_t context;
    unw_getcontext(&context);
    unw_init_local(&cursor, &context);
    _out << std::hex << std::uppercase;
    while (0 < unw_step(&cursor)) {
        unw_word_t ip = 0;
        unw_get_reg(&cursor, UNW_REG_IP, &ip);
        if (ip == 0) {
            break;
        }
        unw_word_t sp = 0;
        unw_get_reg(&cursor, UNW_REG_SP, &sp);
        print_reg(_out, ip);
        _out << ": (SP:";
        print_reg(_out, sp);
        _out << ") ";
        unw_word_t offset = 0;
        if (unw_get_proc_name(&cursor, symbol, sizeof(symbol), &offset) == 0) {
            _out << "(" << get_demangled_name(symbol) << " + 0x" << offset << ")\n\n";
        } else {
            _out << "-- error: unable to obtain symbol name for this frame\n\n";
        }
    }
    _out << std::flush;
}

backtrace_on_terminate.hpp:

backtrace_on_terminate.hpp:

#include "demangle.hpp"
#include "backtrace.hpp"

#include <iostream>
#include <type_traits>
#include <exception>
#include <memory>
#include <typeinfo>

#include <cstdlib>

#include <cxxabi.h>

namespace
{

[[noreturn]]
void
backtrace_on_terminate() noexcept;

static_assert(std::is_same< std::terminate_handler, decltype(&backtrace_on_terminate) >{});

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#pragma clang diagnostic ignored "-Wexit-time-destructors"
std::unique_ptr< std::remove_pointer_t< std::terminate_handler >, decltype(std::set_terminate) & > terminate_handler{std::set_terminate(backtrace_on_terminate), std::set_terminate};
#pragma clang diagnostic pop

[[noreturn]]
void
backtrace_on_terminate() noexcept
{
    std::set_terminate(terminate_handler.release()); // to avoid infinite looping if any
    backtrace(std::clog);
    if (std::exception_ptr ep = std::current_exception()) {
        try {
            std::rethrow_exception(ep);
        } catch (std::exception const & e) {
            std::clog << "backtrace: unhandled exception std::exception:what(): " << e.what() << std::endl;
        } catch (...) {
            if (std::type_info * et = abi::__cxa_current_exception_type()) {
                std::clog << "backtrace: unhandled exception type: " << get_demangled_name(et->name()) << std::endl;
            } else {
                std::clog << "backtrace: unhandled unknown exception" << std::endl;
            }
        }
    }
    std::_Exit(EXIT_FAILURE); // change to desired return code
}

}

There is good article concerning the issue.

关于这个问题有很好的文章。

#6


1  

I've got code to do this in Windows/Visual Studio, let me know if you want an outline. Don't know how to do it for dwarf2 code though, a quick google suggests that there's a function _Unwind_Backtrace in libgcc that probably is part of what you need.

我有在Windows/Visual Studio中做这个的代码,如果你想要一个大纲,请告诉我。虽然不知道如何为矮人f2代码做这个,但是快速的谷歌表明在libgcc中有一个函数_Unwind_Backtrace,这可能是您需要的一部分。

#7


1  

Check this thread, perhaps it helps:

检查这个线程,它可能会有帮助:

Catching all unhandled C++ exceptions?

捕获所有未处理的c++异常?

I made good experiences with that software:

我对这个软件有很好的体验:

http://www.codeproject.com/KB/applications/blackbox.aspx

http://www.codeproject.com/KB/applications/blackbox.aspx

It can print out a stack trace to a file for any unhandled exception.

它可以为任何未处理的异常打印一个堆栈跟踪到文件。