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. 关于0x*** 十六进制的运算。为什么枚举多用十六进制的运算原因。。

    1.看个人爱好 2.可以看出布尔运算的结果. 3.可以更快进行and和or 运算

  2. new Date在ios下的兼容bug

    今天发现在ios下new Date("2019-03-06").getTime()返回NaN 原因是ios下不支持“-”必须用"/" 记录备忘 var d = ...

  3. 用 gdb 调试 GCC 程序【转】

    用 GDB 调试程序 原著:Rick McMullin 用 gdb 调试 GCC 程序 转自:http://blog.csdn.net/bonnshore/article/details/795542 ...

  4. Python-mysql索引

    MySQL索引的概念 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针.更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询 ...

  5. mysql 事务、游标

    mysql 事务 在MySQL中只有使用了Innodb数据库引擎的数据库或表才支持事务,所以很多情况下我们都使用innodb引擎. 事务处理可以用来维护数据库的完整性,保证成批的SQL语句要么全部执行 ...

  6. C#中一些关键字和罕见类

    1.@ 在处理字符串时,那些个字符串转义字符我们可以使用这个,那@就是为解决这个而诞生的,直接让字符串原样输出有木有?什么sql字符串了,还有路径啥了的,统统搞定 string sql = @&quo ...

  7. 关于Banner设计的促销氛围

    banner的促销氛围有很多种创意方向和排版布局,如下图: 如下面这种banner,背景大面积留白,细小的字体,单调的颜色,高冷的模特,感觉就不一样了.并不让人感觉这是在卖49元的衣服,文案和画面也搭 ...

  8. ros结合catkin_make和qtcreator

    首先是ros官网关于IDE的教程: http://wiki.ros.org/IDEs#QtCreator 1.qtcreator安装 从官网上下载.run文件, https://info.qt.io/ ...

  9. 通过GitHub部署项目到Nginx服务器

    1.更新源: 2.安装nginx 3.安装成功 4.DNS域名解析 5.访问域名就会找到相应IP地址的主机,一个IP可对应多个域名 6.提交到gitHub 复制这两行 填上邮箱和密码 7.提交成功 8 ...

  10. Openstack ceilometer

    https://www.cnblogs.com/liguangsunls/p/6879879.html