exception stack trace
This commit is contained in:
@@ -79,3 +79,4 @@ pvDataApp/misc/byteBuffer.cpp
|
||||
pvDataApp/misc/bitSet.cpp
|
||||
pvDataApp/test/testByteBuffer.cpp
|
||||
pvDataApp/test/testBitSet.cpp
|
||||
pvDataApp/test/testBaseException.cpp
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user