首页 > 代码库 > 借助backtrace和demangle实现异常类Exception
借助backtrace和demangle实现异常类Exception
C++的异常类是没有栈痕迹的,如果需要获取栈痕迹,需要使用以下函数:
#include <execinfo.h>int backtrace(void **buffer, int size);char **backtrace_symbols(void *const *buffer, int size);void backtrace_symbols_fd(void *const *buffer, int size, int fd);
backtrace将当前程序的调用信息存储在buffer中,backtrace_symbols则是将buffer翻译为字符串。后者用到了malloc,所以需要手工释放内存。
man手册中提供了如下的代码:
#include <execinfo.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>voidmyfunc3(void){ int j, nptrs;#define SIZE 100 void *buffer[100]; char **strings; nptrs = backtrace(buffer, SIZE); printf("backtrace() returned %d addresses\n", nptrs); /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO) would produce similar output to the following: */ strings = backtrace_symbols(buffer, nptrs); if (strings == NULL) { perror("backtrace_symbols"); exit(EXIT_FAILURE); } for (j = 0; j < nptrs; j++) printf("%s\n", strings[j]); free(strings);}static void /* "static" means don‘t export the symbol... */myfunc2(void){ myfunc3();}voidmyfunc(int ncalls){ if (ncalls > 1) myfunc(ncalls - 1); else myfunc2();}intmain(int argc, char *argv[]){ if (argc != 2) { fprintf(stderr, "%s num-calls\n", argv[0]); exit(EXIT_FAILURE); } myfunc(atoi(argv[1])); exit(EXIT_SUCCESS);}
编译并执行:
$ cc -rdynamic prog.c -o prog$ ./prog 3
输出如下:
backtrace() returned 8 addresses./prog(myfunc3+0x1f) [0x8048783]./prog() [0x8048810]./prog(myfunc+0x21) [0x8048833]./prog(myfunc+0x1a) [0x804882c]./prog(myfunc+0x1a) [0x804882c]./prog(main+0x52) [0x8048887]/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xb76174d3]./prog() [0x80486d1]
因此我写出以下的异常类,注意上面的打印结果经过了名字改编,所以我们使用abi::__cxa_demangle将名字还原出来。
Exception.h
#ifndef EXCEPTION_H_#define EXCEPTION_H_#include <string>#include <exception>class Exception : public std::exception{public: explicit Exception(const char* what); explicit Exception(const std::string& what); virtual ~Exception() throw(); virtual const char* what() const throw(); const char* stackTrace() const throw();private: void fillStackTrace(); //填充栈痕迹 std::string demangle(const char* symbol); //反名字改编 std::string message_; //异常信息 std::string stack_; //栈trace};#endif // EXCEPTION_H_
Exception.cpp
#include "Exception.h"#include <cxxabi.h>#include <execinfo.h>#include <stdlib.h>#include <stdio.h>using namespace std;Exception::Exception(const char* msg) : message_(msg){ fillStackTrace();}Exception::Exception(const string& msg) : message_(msg){ fillStackTrace();}Exception::~Exception() throw (){}const char* Exception::what() const throw(){ return message_.c_str();}const char* Exception::stackTrace() const throw(){ return stack_.c_str();}//填充栈痕迹void Exception::fillStackTrace(){ const int len = 200; void* buffer[len]; int nptrs = ::backtrace(buffer, len); //列出当前函数调用关系 //将从backtrace函数获取的信息转化为一个字符串数组 char** strings = ::backtrace_symbols(buffer, nptrs); if (strings) { for (int i = 0; i < nptrs; ++i) { // TODO demangle funcion name with abi::__cxa_demangle //strings[i]代表某一层的调用痕迹 stack_.append(demangle(strings[i])); stack_.push_back(‘\n‘); } free(strings); }}//反名字改编string Exception::demangle(const char* symbol){ size_t size; int status; char temp[128]; char* demangled; //first, try to demangle a c++ name if (1 == sscanf(symbol, "%*[^(]%*[^_]%127[^)+]", temp)) { if (NULL != (demangled = abi::__cxa_demangle(temp, NULL, &size, &status))) { string result(demangled); free(demangled); return result; } } //if that didn‘t work, try to get a regular c symbol if (1 == sscanf(symbol, "%127s", temp)) { return temp; } //if all else fails, just return the symbol return symbol;}
测试代码如下:
#include "Exception.h"#include <stdio.h>using namespace std;class Bar{ public: void test() { throw Exception("oops"); }};void foo(){ Bar b; b.test();}int main(){ try { foo(); } catch (const Exception& ex) { printf("reason: %s\n", ex.what()); printf("stack trace: %s\n", ex.stackTrace()); }}
打印结果如下:
reason: oopsstack trace: Exception::fillStackTrace()Exception::Exception(char const*)Bar::test()foo()./a.out(main+0xf)/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)./a.out()
注意编译的时候,加上-rdynamic选项
有了这个类,我们可以在程序中这样处理异常:
try { // } catch (const Exception& ex) { fprintf(stderr, "reason: %s\n", ex.what()); fprintf(stderr, "stack trace: %s\n", ex.stackTrace()); abort(); } catch (const std::exception& ex) { fprintf(stderr, "reason: %s\n", ex.what()); abort(); } catch (...) { fprintf(stderr, "unknown exception caught \n"); throw; // rethrow }
借助backtrace和demangle实现异常类Exception
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。