如何找到在C ++中抛出异常的地方? [英] How do I find where an exception was thrown in C++?

查看:163
本文介绍了如何找到在C ++中抛出异常的地方?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个程序,在某处抛出未捕获的异常。我得到的是一个异常抛出的报告,没有关于它被抛出的信息。对于编译为包含调试符号的程序,不通知我在我的代码中生成异常的位置似乎不合逻辑。



有没有什么方法来告诉我的异常在哪里来自短缺设置'catch throw'在gdb和调用回溯跟踪每个单一的抛出异常?

解决方案

这里有一些信息可以用于调试您的问题


$ b

如果未捕获异常,则特殊库函数 std :: terminate() 被自动调用。 Terminate实际上是一个指向函数的指针,默认值是标准C库函数 std :: abort() 。如果没有对未捕获的异常进行清除,它可能实际上在调试此问题时有用,因为没有调用析构函数。

†它是实现定义的是否在 std :: terminate()被调用之前堆栈是否解开。






调用 abort()在生成核心转储中通常很有用,可以通过分析来确定原因的异常。请确保您通过 ulimit -c unlimited (Linux)启用核心转储。






您可以使用 terminate() / error / set_terminate> std :: set_terminate() 。您应该能够在gdb中的terminate函数上设置断点。您可以可以从 terminate()函数中生成堆栈回溯,此回溯可以 帮助识别



有关 Bruce Eckel在C ++中的思考,第2版中的未捕获异常,这也可能有所帮助。






由于 terminate()调用默认情况下,这将会导致一个 SIGABRT 信号,abort(),你可以 SIGABRT 处理程序,然后从信号处理程序中打印堆栈回溯跟踪






注意: 我说可能,因为C ++通过使用语言结构来支持非本地错误处理,将错误处理和报告代码与普通代码分离。捕捉块可以并且经常位于与投掷点不同的功能/方法中。在评论中也指出了(感谢 Dan ),它是实现定义的,不管栈是否

更新:。我将一个Linux测试程序调用,它通过 set_terminate() terminate()函数集中生成一个回溯 SIGABRT 的处理程序。

更新2:由于

/cplusplus.co.il/2010/03/21/catching-uncaught-exceptions-within-terminate/\">在终止期间捕获未捕获的异常
,我学到了一些新的技巧;包括在终止处理程序中重新抛出未捕获的异常。重要的是注意,自定义终止处理程序中的空的 throw 语句与GCC一起使用,并且不是可移植的解决方案。



代码:



  #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 {
//调用set_terminate作为全局常量初始化的一部分
static const bool SET_TERMINATE = std :: set_terminate(my_terminate);
}

//这个结构镜像在/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;

//从EIP引发信号时获取地址(x86)
void * caller_address =(void *)uc-> uc_mcontext.eip;

std :: cerr<< signal<< sig_num
<< (<< strsignal(sig_num)<),地址是
< info-> si_addr<< from
<< caller_address<< std :: endl;

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

std :: cerr<< __FUNCTION__<< backtrace returned
<<尺寸< frames\\\
\\\
;

//用调用方地址重写sigaction
array [1] = caller_address;

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

//跳过第一个堆栈框架(这里指的是)
for(int i = 1; i std :: cerr<< [bt]:(<< i<)<消息[i] std :: endl;
}
std :: cerr<< std :: endl;

免费(消息);

exit(EXIT_FAILURE);
}

void my_terminate(){
static bool tried_throw = false;

try {
//尝试一次,重新抛出当前活动的异常
if(!tried_throw ++)throw;
}
catch(const std :: exception& e){
std :: cerr< __FUNCTION__<< catch unhandled exception。what():
<< e.what()< std :: endl;
}
catch(...){
std :: cerr<< __FUNCTION__<< 捕获未知/未处理的异常。
<< std :: endl;
}

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

std :: cerr<< __FUNCTION__<< backtrace returned
<<尺寸< frames\\\
\\\
;

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

for(int i = 0; i< size&&&&&&& [bt]:(<< i<)<消息[i] std :: endl;
}
std :: cerr<< std :: endl;

免费(消息);

abort();
}

int throw_exception(){
//抛出一个未处理的运行时错误
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& 信号的错误设置处理程序< SIGABRT
<< (<< strsignal(SIGABRT)<<)\\\
;
exit(EXIT_FAILURE);
}

foo1();

exit(EXIT_SUCCESS);
}

输出

 
my_terminate捕获未处理的异常。 what():RUNTIME ERROR!
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+0xb)[0x8049043]
[bt]:(6)./test(foo1__Fv+0xb)[0x8049057]
[bt]:(7)./test(main+0xc1)[0x8049121]
[bt]:(8)./test(__libc_start_main+0x95)[0x42017589]
[bt] )./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+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]


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?

解决方案

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.


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).


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.

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


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.


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.

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.

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.

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]

这篇关于如何找到在C ++中抛出异常的地方?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆