C++的异常类是没有栈痕迹的,如果需要获取栈痕迹,需要使用以下函数:
#include
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
#include
#include
#include
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);
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
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
#include
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_;
};
#endif
Exception.cpp
#include "Exception.h"
#include
#include
#include
#include
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);
char** strings = ::backtrace_symbols(buffer, nptrs);
if (strings)
{
for (int i = 0; i < nptrs; ++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;
if (1 == sscanf(symbol, "%*[^(]%*[^_]%127[^)+]", temp)) {
if (NULL != (demangled = abi::__cxa_demangle(temp, NULL, &size, &status))) {
string result(demangled);
free(demangled);
return result;
}
}
if (1 == sscanf(symbol, "%127s", temp)) {
return temp;
}
return symbol;
}
测试代码如下:
#include "Exception.h"
#include
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()
./test_demangle1(main+0xe)
/lib64/libc.so.6(__libc_start_main+0xf5)
./test_demangle1()
注意编译的时候,加上-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;
}