我希望有一种方法在抛出异常时向用户报告堆栈跟踪。最好的方法是什么?它是否需要大量额外的代码?

回答问题:

如果可能的话,我希望它是便携的。我想让信息弹出,这样用户就可以复制堆栈跟踪,并在出现错误时通过电子邮件发送给我。


当前回答

我想添加一个标准库选项(即跨平台),如何生成异常回溯,这已经在c++ 11中可用:

使用std::nested_exception和std::throw_with_nested

这不会给你一个堆栈unwind,但在我看来是次好的事情。 StackOverflow在这里和这里都有描述,如何在不需要调试器或繁琐的日志记录的情况下,通过简单地编写一个适当的异常处理程序重新抛出嵌套异常,就可以在代码中对异常进行回溯。

由于可以对任何派生异常类执行此操作,因此可以向这样的回溯添加大量信息! 你也可以看看我在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"

其他回答

OSX的一个工作示例(现在在Catalina 10.15上测试)。显然不能移植到linux/windows。也许它会对某人有用。

在“Mew-exception”字符串中,可以使用backtrace和/或backtrace_symbols函数

#include <stdexcept>
#include <typeinfo>
#include <dlfcn.h>

extern "C" void __cxa_throw(void *thrown_object, std::type_info *tinfo, void (*dest)(void *));
static void (*__cxa_throw_orig)(void *thrown_object, std::type_info *tinfo, void (*dest)(void *));
extern "C" void luna_cxa_throw(void *thrown_object, std::type_info *tinfo, void (*dest)(void *))
{
    printf("Mew-exception you can catch your backtrace here!");
    __cxa_throw_orig(thrown_object, tinfo, dest);
}


//__attribute__ ((used))
//__attribute__ ((section ("__DATA,__interpose")))
static struct replace_pair_t {
    void *replacement, *replacee;
} replace_pair = { (void*)luna_cxa_throw, (void*)__cxa_throw };

extern "C" const struct mach_header __dso_handle;
extern "C" void dyld_dynamic_interpose(const struct mach_header*,
                               const replace_pair_t replacements[],
                               size_t count);

int fn()
{
    int a = 10; ++a;
    throw std::runtime_error("Mew!");
}

int main(int argc, const char * argv[]) {
    __cxa_throw_orig = (void (*)(void *thrown_object, std::type_info *tinfo, void (*dest)(void *)))dlsym(RTLD_DEFAULT, "__cxa_throw");

    dyld_dynamic_interpose(&__dso_handle, &replace_pair, 1);

    fn();
    return 0;
}

Unix:回溯

麦克:回溯

Windows: CaptureBackTrace

在Windows上,查看BugTrap。它不再在原来的链接中,但在CodeProject中仍然可用。

Poppy不仅可以收集堆栈跟踪,还可以收集参数值、局部变量等等——所有导致崩溃的东西。

我也有类似的问题,尽管我喜欢可移植性,但我只需要gcc支持。在gcc中,execinfo.h和回溯调用是可用的。为了要求函数名,Bingmann先生编写了一段很好的代码。为了在异常上转储反向跟踪,我创建了一个异常,该异常在构造函数中打印反向跟踪。如果我期望这与库中抛出的异常一起工作,则可能需要重新构建/链接,以便使用回溯异常。

/******************************************
#Makefile with flags for printing backtrace with function names
# compile with symbols for backtrace
CXXFLAGS=-g
# add symbols to dynamic symbol table for backtrace
LDFLAGS=-rdynamic
turducken: turducken.cc
******************************************/

#include <cstdio>
#include <stdexcept>
#include <execinfo.h>
#include "stacktrace.h" /* https://panthema.net/2008/0901-stacktrace-demangled/ */

// simple exception that prints backtrace when constructed
class btoverflow_error: public std::overflow_error
{
    public:
    btoverflow_error( const std::string& arg ) :
        std::overflow_error( arg )
    {
        print_stacktrace();
    };
};


void chicken(void)
{
    throw btoverflow_error( "too big" );
}

void duck(void)
{
    chicken();
}

void turkey(void)
{
    duck();
}

int main( int argc, char *argv[])
{
    try
    {
        turkey();
    }
    catch( btoverflow_error e)
    {
        printf( "caught exception: %s\n", e.what() );
    }
}

使用gcc 4.8.4编译和运行这个函数会产生一个反向跟踪,其中包含了非常整洁的c++函数名:

stack trace:
 ./turducken : btoverflow_error::btoverflow_error(std::string const&)+0x43
 ./turducken : chicken()+0x48
 ./turducken : duck()+0x9
 ./turducken : turkey()+0x9
 ./turducken : main()+0x15
 /lib/x86_64-linux-gnu/libc.so.6 : __libc_start_main()+0xf5
 ./turducken() [0x401629]