exception stack trace

This commit is contained in:
Matej Sekoranja
2010-10-25 00:16:43 +02:00
parent ef709eb6b9
commit d97e9c12ed
3 changed files with 169 additions and 9 deletions

View File

@@ -79,3 +79,4 @@ pvDataApp/misc/byteBuffer.cpp
pvDataApp/misc/bitSet.cpp
pvDataApp/test/testByteBuffer.cpp
pvDataApp/test/testBitSet.cpp
pvDataApp/test/testBaseException.cpp

View File

@@ -13,6 +13,11 @@
#include <stdexcept>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>
#include <cxxabi.h>
namespace epics { namespace pvData {
@@ -22,6 +27,7 @@ public:
BaseException(const char* message, const char* file, int line, std::exception* cause = 0)
: m_msg(message), m_file(file), m_line(line), m_cause(cause)
{
getStackTrace(&m_stackTrace);
}
virtual ~BaseException() throw()
@@ -34,7 +40,7 @@ public:
const char* getFile() const { return m_file.c_str(); }
int getLine() const { return m_line; }
void toString(std::string& str) {
void toString(std::string& str, unsigned int depth = 0) {
str.append("BaseException: ");
str.append(m_msg);
str.append("\n\tat ");
@@ -43,22 +49,156 @@ public:
char sline[10];
snprintf(sline, 10, "%d", m_line);
str.append(sline);
str.append("\n");
str.append(m_stackTrace);
if (m_cause)
{
str.append("\ncaused by: ");
str.append("caused by: ");
BaseException *be = dynamic_cast<BaseException*>(m_cause);
if (be)
be->toString(str);
be->toString(str, depth+1);
else
str.append(m_cause->what());
}
}
/** Get stack trace, i.e. demangled backtrace of the caller. */
static inline void getStackTrace(std::string* trace, unsigned int skip_frames = 0, unsigned int max_frames = 63)
{
#ifdef DISABLE_STACK_TRACE
trace += "(stack trace disabled)";
#else
// storage array for stack trace address data
void* addrlist[max_frames+1];
// retrieve current stack addresses
int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*));
if (addrlen == 0) {
trace->append("(stack trace not available)");
return;
}
// resolve addresses into strings containing "filename(function+address)",
// this array must be free()-ed
char** symbollist = backtrace_symbols(addrlist, addrlen);
// allocate string which will be filled with the demangled function name
size_t funcnamesize = 256;
char* funcname = (char*)malloc(funcnamesize);
// iterate over the returned symbol lines. skip the first, it is the
// address of this function.
for (int i = (1 + skip_frames); i < addrlen; i++)
{
char *module = 0, *fname = 0, *offset = 0;
#ifdef __APPLE__
int stage = 0;
for (char *p = symbollist[i]; *p; ++p)
{
// find spaces and separate
// 0 a.out 0x0000000100000bbc _Z11print_tracev + 22
switch (stage)
{
case 0: // skip frame index
if (*p == ' ') stage++;
break;
case 1: // skip spaces
if (*p != ' ') { module = p; stage++; }
break;
case 2: // module name
if (*p == ' ') { *p = '\0'; stage++; }
break;
case 3: // skip spaces
if (*p != ' ') stage++;
break;
case 4: // address
if (*p == ' ') { fname = p+1; stage++; }
break;
case 5: // function
if (*p == ' ') { *p = '\0'; stage++; }
break;
case 6: // "+ "
if (*p == '+') { p++; offset = p+1; };
break;
}
}
#else
// find parentheses and +address offset surrounding the mangled name:
// ./module(function+0x15c) [0x8048a6d]
module = symbollist[i];
for (char *p = symbollist[i]; *p; ++p)
{
if (*p == '(') {
// terminate module
*p = '\0'
fname = p+1;
}
else if (*p == '+') {
// terminate fname
*p = '\0'
offset = p+1;
}
else if (*p == ')' && offset) {
// terminate offset
*p = '\0';
break;
}
}
#endif
if (fname && offset && offset && fname < offset)
{
// mangled name is now in [begin_name, begin_offset) and caller
// offset in [begin_offset, end_offset). now apply
// __cxa_demangle():
int status;
char* ret = abi::__cxa_demangle(fname,
funcname, &funcnamesize, &status);
if (status == 0) {
trace->append("\t ");
*trace += module;
trace->append(": ");
*trace += ret; // use possibly realloc()-ed string
trace->append("+");
*trace += offset;
trace->append("\n");
}
else {
// demangling failed. Output function name as a C function with
// no arguments.
trace->append("\t ");
*trace += module;
trace->append(": ");
*trace += fname;
*trace += "()+";
*trace += offset;
trace->append("\n");
}
}
else
{
// couldn't parse the line? print the whole line.
trace->append("\t ");
*trace += symbollist[i];
trace->append("\n");
}
}
free(funcname);
free(symbollist);
#endif
}
private:
std::string m_msg;
std::string m_file;
int m_line;
std::exception* m_cause;
std::string m_stackTrace;
};

View File

@@ -13,6 +13,29 @@
using namespace epics::pvData;
struct Unroller
{
template <int N>
void unroll(double d) {
unroll<N-1>(d);
}
};
template<>
void Unroller::unroll<0>(double d) {
THROW_BASE_EXCEPTION("the root cause");
}
void internalTestBaseException(int unused = 0)
{
try {
// NOTE: 5, 4, 3, 2, 1 calls will be optimized and not shown
Unroller().unroll<5>(42.0);
} catch (BaseException *be3) {
THROW_BASE_EXCEPTION_CAUSE("exception 1", be3);
}
}
void testBaseException() {
printf("testBaseException... ");
@@ -26,13 +49,9 @@ void testBaseException() {
try {
try {
try {
THROW_BASE_EXCEPTION("the root cause");
} catch (BaseException *be3) {
THROW_BASE_EXCEPTION_CAUSE("exception 1", be3);
}
internalTestBaseException();
} catch (BaseException *be2) {
THROW_BASE_EXCEPTION_CAUSE("excepton 2", be2);
THROW_BASE_EXCEPTION_CAUSE("exception 2", be2);
}
} catch (BaseException *be) {
std::string str;