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> void
myfunc3(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();
} void
myfunc(int ncalls)
{
if (ncalls > 1)
myfunc(ncalls - 1);
else
myfunc2();
} int
main(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: oops
stack 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的更多相关文章

  1. 【转】【C#】异常类 Exception 枚举所有类型的异常

    一.基础 在C# 里,异常处理就是C# 为处理错误情况提供的一种机制.它为每种错误情况提供了定制的处理方式,并且把标识错误的代码与处理错误的代码分离开来. 对.NET类来说,一般的 异常类System ...

  2. C++异常第二篇---C++标准库异常类exception的使用

    1 继承图示 2 具体讲解 C++标准库异常类继承层次中的根类为exception,其定义在exception头文件中,它是C++标准库所有函数抛出异常的基类,exception的接口定义如下: na ...

  3. 异常类Exception(String message, Throwable cause)中的cause理解

    这个在构造函数里面竟然有一个Throwable,感觉有些奇怪. 1. Throwable cause 这里cause要传一个Throwable的子类异常进去么? 是引起这个异常的异常,如果这个值是空值 ...

  4. 每天一点点java---继承exception类来实现自己的异常类

    package prac_1; /** * <p>Title: 捕获异常和实现自己的异常类</p> * <p>Description: 通过继承Exception类 ...

  5. 2019-2-21.NET中异常类(Exception)

    .NET中异常类(Exception) 异常:程序在运行期间发生的错误.异常对象就是封装这些错误的对象. try{}catch{}是非常重要的,捕获try程序块中所有发生的异常,如果没有捕获异常的话, ...

  6. .NET中异常类(Exception)

    异常:程序在运行期间发生的错误.异常对象就是封装这些错误的对象. try{}catch{}是非常重要的,捕获try程序块中所有发生的异常,如果没有捕获异常的话,程序运行的线程将会挂掉,更严重的是这些错 ...

  7. 异常类的构建——顶层父类Exception的实现

    异常类构建异常的类型可以是自定义类类型对于类类型异常的匹配依旧是从上到下严格的匹配赋值兼容性原则在异常匹配中依然适用一般而言-匹配子类异常的catch放在上部-匹配父类异常的catch放在下部 现代C ...

  8. poco json 中文字符,抛异常JSON Exception -->iconv 转换 备忘录。

    起因 最近linux服务器通信需要用到json. jsoncpp比较出名,但poco 1.5版本以后已经带有json库,所以决定使用poco::json(linux 上已经用到了poco这一套框架). ...

  9. Visual C++ 异常(Exception)常见问题 (原文标题:A Visual C++ Exception FAQ)

    Visual C++ 异常(Exception)常见问题 版权:Doug Harrison 2001 – 2007 翻译:magictong(童磊) 2011年3月 原文地址:http://membe ...

随机推荐

  1. mac使用基础

    Mac 系统的桌面 Mac 的桌面是一个很炫的3D, 背景是一张“星空”图. 2 Dock:  在桌面的下方有一排图标, 叫Dock, 用来快速启动程序, 进入文件夹, 它同时还可以停靠正在运行的程序 ...

  2. bzoj3643 Phi的反函数

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3643 [题解] n = p1^a1*p2^a2*...*pm^am phi(n) = p1( ...

  3. lightgbm 学习笔记

    首先是配置文件参数.参考自https://lightgbm.apachecn.org/#/docs/6 配置参数实在是太多了,大家还是去原文档查表吧orz 参数名 可选参数 作用 config= 自填 ...

  4. 牧场行走(LCA)

    神奇传送门 好吧,这题很有意思.. 第一眼撇的时候还以为是(SPFA)呜.... 然后发现要Q次询问就想到了LCA 但是发现不是求LCA.. 于是想到了一个神奇的定律: 两点的LCA一定在u到v的最短 ...

  5. 杭电oj2032、2040、2042、2054、2055

    2032  杨辉三角 #include <stdio.h> int main(){ ][],i,j,n; while(~scanf("%d",&n)){ ;i& ...

  6. 将log4j2的配置文件log4j2.xml拆分成多个xml文件

    在日常的项目开发中,我们可能会使用log4j2分离系统日志与业务日志 ,这样一来,log4j2.xml 这个配置文件可能就会变得非常臃肿.庞大,那么我们可以将这个文件拆分成多个配置文件吗? 答案是肯定 ...

  7. Table is marked as crashed and should be repaire (

    https://www.cnblogs.com/cxchanpin/p/6894747.html

  8. Codeforces Gym100952 B. New Job (2015 HIAST Collegiate Programming Contest)

      B. New Job   time limit per test 1 second memory limit per test 64 megabytes input standard input ...

  9. (6)C#方法,作用域,方法重载

    1.方法 返回类型 方法名(参数列表) 例如:int a(int b,int c) { 方法体 return b+c; } 函数如果有返回类型则最后要用return一个和返回类型一样的数据. 2.无返 ...

  10. HDU 6268 Master of Subgraph (2017 CCPC 杭州 E题,树分治 + 树上背包)

    题目链接  2017 CCPC Hangzhou  Problem E 题意  给定一棵树,每个点有一个权值,现在我们可以选一些连通的点,并且把这点选出来的点的权值相加,得到一个和. 求$[1, m] ...