From d97e9c12edd6973f8fb8d474ab2c2b77e00dbbcc Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Mon, 25 Oct 2010 00:16:43 +0200 Subject: [PATCH] exception stack trace --- QtC-pvData.files | 1 + pvDataApp/misc/epicsException.h | 146 ++++++++++++++++++++++++++- pvDataApp/test/testBaseException.cpp | 31 ++++-- 3 files changed, 169 insertions(+), 9 deletions(-) diff --git a/QtC-pvData.files b/QtC-pvData.files index c767546..41caef7 100644 --- a/QtC-pvData.files +++ b/QtC-pvData.files @@ -79,3 +79,4 @@ pvDataApp/misc/byteBuffer.cpp pvDataApp/misc/bitSet.cpp pvDataApp/test/testByteBuffer.cpp pvDataApp/test/testBitSet.cpp +pvDataApp/test/testBaseException.cpp diff --git a/pvDataApp/misc/epicsException.h b/pvDataApp/misc/epicsException.h index 8a98840..b3936f0 100644 --- a/pvDataApp/misc/epicsException.h +++ b/pvDataApp/misc/epicsException.h @@ -13,6 +13,11 @@ #include #include +#include +#include +#include +#include + 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(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; }; diff --git a/pvDataApp/test/testBaseException.cpp b/pvDataApp/test/testBaseException.cpp index 9a2c6be..bd16b51 100644 --- a/pvDataApp/test/testBaseException.cpp +++ b/pvDataApp/test/testBaseException.cpp @@ -13,6 +13,29 @@ using namespace epics::pvData; +struct Unroller +{ + template + void unroll(double d) { + unroll(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;