如何find在C ++中抛出exception?

我有一个程序抛出一个未捕获的exception的地方。 我所得到的只是一个抛出exception的报告,并没有关于抛出的地方的信息。 编译为包含debugging符号的程序似乎不合逻辑,不会通知我在代码中生成exception的地方。

有没有什么办法可以告诉我哪些exception是由于在gdb中设置'catch throw'而引起的,并且为每一个抛出的exception调用一个回溯?

这里有一些信息可能会用于debugging您的问题

如果一个exception未被捕获,则自动调用特殊库函数std::terminate() 。 Terminate实际上是一个指向函数的指针,默认值是标准C库函数std::abort() 。 如果没有清理出现未捕获的exception ,则可能实际上有助于debugging此问题,因为不会调用析构函数。
†在std::terminate()被调用之前,是否实现定义了堆栈是否展开。


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


你可以使用std::set_terminate()来安装你自己的terminate()函数。 你应该可以在gdb的终止函数中设置一个断点。 您可能能够从terminate()函数生成堆栈回溯,并且此回溯可以帮助识别exception的位置。

在Bruce Eckel的Thinking in C ++,2nd Ed中有一个关于未被捕获的exception的简短讨论,可能也会有所帮助。


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


注:我说可能因为C ++通过使用语言结构来分隔error handling和报告代码与普通代码的支持非本地error handling。 catch块可以并经常位于与投掷点不同的function/方法中。 在评论中也有人指出(感谢Dan ),在调用terminate()之前,是否实现了堆栈是否展开。

更新:我把一个Linuxtesting程序放在一起调用,在通过set_terminate()设置的terminate()函数中产生一个回溯,而在SIGABRT的信号处理程序中产生另一个。 这两个回溯正确显示未处理的exception的位置。

更新2:感谢关于在终止捕捉未捕获的exception的博客文章,我学到了一些新的技巧; 包括在终止处理程序中重新抛出未捕获的exception。 请注意,自定义终止处理程序中的空throw语句适用于GCC,而不是可移植的解决scheme。

码:

 #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); } 

输出:

 my_terminate引起了不必要的例外。 什么():运行时错误!
 my_terminate backtrace返回了10个帧

 [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 + 0xx)[0x8049043]
 [bt]:(6)./test(foo1__Fv + 0xxb)[0x8049057]
 [bt]:(7)./test(main + 0xc1)[0x8049121]
 [bt]:(8)./test(__libc_start_main+0x95)[0x42017589]
 [bt]:(9)./test(__eh_alloc+0x3d)[0x8048b21]

信号6(中止),地址是0x42029331的0x1239
 crit_err_hdlr backtrace返回13帧

 [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 + 0xxb)[0x8049043]
 [bt]:(9)./test(foo1__Fv + 0xxb)[0x8049057]
 [bt]:(10)./test(main + 0xc1)[0x8049121]
 [bt]:(11)./test(__libc_start_main+0x95)[0x42017589]
 [bt]:(12)./test(__eh_alloc+0x3d)[0x8048b21]

正如你所说的,我们可以在gdb中使用'catch throw',并为每一个抛出的exception调用'backtrace'。 虽然手动执行通常太繁琐,但gdb允许进程的自动化。 这可以看到所有抛出的exception的回溯,包括最后一个未被捕获的exception:

GDB>

 set pagination off catch throw commands backtrace continue end run 

没有进一步的手动干预,这会产生大量的回溯,其中包括最后一个未捕获的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. 

这里有一个很棒的博客文章:http: //741mhz.com/throw-stacktrace/

您可以创build一个macros,如:

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

…它会给你的位置抛出exception(诚然不是堆栈跟踪)。 你有必要从一些基类中派生你的exception,这个基类有上面的构造函数。

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

在Visual Studio C ++中可以检测exception。

请参见ddj.com上的“Visual C ++ Exception-Handling Instrumentation”

我的文章“ Postmortem Debugging”也在ddj.com上,包含使用Win32结构化exception处理(由检测工具使用)进行日志logging等的代码。

你可以在你的代码中把主要的紧张地方标记为noexcept来定位一个exception,然后使用libunwind (只需在链接器参数中join-lunwind )(用clang++ 3.6testing):

demagle.hpp:

 #pragma once char const * get_demangled_name(char const * const symbol) noexcept; 

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:

 #pragma once #include <ostream> void backtrace(std::ostream & _out) noexcept; 

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:

 #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 } } 

有关这个问题的好文章 。

我有代码在Windows / Visual Studio中做到这一点,让我知道如果你想要一个大纲。 不知道如何为dwarf2代码做,但一个快速谷歌表明,有一个函数_Unwind_Backtrace在libgcc可能是你需要的一部分。

检查这个线程,也许它有帮助:

捕获所有未处理的C ++exception?

我用这个软件做了很好的经验:

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

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