From e10639753d7dbd368f5edc2555d75c4b5905ba3b Mon Sep 17 00:00:00 2001 From: Pixel Date: Mon, 21 Nov 2011 13:27:53 -0800 Subject: GeneralException() will now trace the callstack and store this, for debugging purposes. --- Makefile | 7 +++-- includes/Exceptions.h | 11 +++++-- includes/HttpServer.h | 2 +- includes/Main.h | 3 ++ src/Exceptions.cc | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/HttpServer.cc | 10 +++++-- src/Task.cc | 3 ++ tests/test-Http.cc | 21 +++++++++++-- 8 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 src/Exceptions.cc diff --git a/Makefile b/Makefile index dcdd255..3854f07 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,8 @@ BINEXT = bin CPPFLAGS += -fno-strict-aliasing ifeq ($(DEBUG),) -CPPFLAGS += -O3 -DNDEBUG +CPPFLAGS += -g -O3 -DNDEBUG +LDFLAGS += -g else CPPFLAGS += -g -DDEBUG -DEV_VERIFY=3 LDFLAGS += -g @@ -90,7 +91,7 @@ endif ifeq ($(SYSTEM),Linux) CPPFLAGS += -fPIC - LDFLAGS += -fPIC + LDFLAGS += -fPIC -rdynamic LIBS += pthread dl CONFIG_H = linux-config.h ARCH_FLAGS = -march=i686 -m32 @@ -110,6 +111,8 @@ vpath %.cc src:tests vpath %.c libcoro:libeio:libev:win32/pthreads-win32:win32/iconv:win32/regex BALAU_SOURCES = \ +Exceptions.cc \ +\ Local.cc \ Threads.cc \ \ diff --git a/includes/Exceptions.h b/includes/Exceptions.h index 0563090..c397265 100644 --- a/includes/Exceptions.h +++ b/includes/Exceptions.h @@ -3,22 +3,27 @@ #include #include #include +#include namespace Balau { class GeneralException { public: - GeneralException(const char * msg) : m_msg(::strdup(msg)) { } - GeneralException(const String & msg) : m_msg(msg.strdup()) { } - GeneralException(const GeneralException & e) : m_msg(strdup(e.m_msg)) { } + GeneralException(const char * msg) : m_msg(::strdup(msg)) { genTrace(); } + GeneralException(const String & msg) : m_msg(msg.strdup()) { genTrace(); } + GeneralException(const GeneralException & e) : m_msg(strdup(e.m_msg)), m_trace(e.m_trace) { } ~GeneralException() { if (m_msg) free(m_msg); } const char * getMsg() const { return m_msg; } + const std::vector getTrace() const { return m_trace; } protected: GeneralException() : m_msg(0) { } void setMsg(char * msg) { if (m_msg) free(m_msg); m_msg = msg; } + void genTrace(); + private: char * m_msg; + std::vector m_trace; }; static inline void * malloc(size_t size) throw (GeneralException) { diff --git a/includes/HttpServer.h b/includes/HttpServer.h index f78a816..51c119c 100644 --- a/includes/HttpServer.h +++ b/includes/HttpServer.h @@ -27,7 +27,7 @@ class HttpServer { void unref() { if (Atomic::Decrement(&m_refCount) == 0) delete this; } void ref() { Atomic::Increment(&m_refCount); } void registerMe(HttpServer * server) { server->registerAction(this); } - virtual bool Do(HttpServer * server, Http::Request & req, ActionMatch & match, IO out) = 0; + virtual bool Do(HttpServer * server, Http::Request & req, ActionMatch & match, IO out) throw (GeneralException) = 0; private: Regex m_regex, m_host; volatile int m_refCount; diff --git a/includes/Main.h b/includes/Main.h index 5fa64c1..1c902ec 100644 --- a/includes/Main.h +++ b/includes/Main.h @@ -88,6 +88,9 @@ class Main { catch (GeneralException e) { m_status = STOPPING; Printer::log(M_ERROR | M_ALERT, "The application caused an exception: %s", e.getMsg()); + std::vector trace = e.getTrace(); + for (std::vector::iterator i = trace.begin(); i != trace.end(); i++) + Printer::log(M_DEBUG, "%s", i->to_charp()); r = -1; } catch (...) { diff --git a/src/Exceptions.cc b/src/Exceptions.cc new file mode 100644 index 0000000..94de408 --- /dev/null +++ b/src/Exceptions.cc @@ -0,0 +1,81 @@ +#include "Exceptions.h" + +#define MAXTRACE 128 + +#ifdef _WIN32 + +#include + +void Balau::GeneralException::genTrace() { + // taken from http://stackoverflow.com/questions/5693192/win32-backtrace-from-c-code + unsigned int i; + void * stack[MAXTRACE]; + unsigned short frames; + SYMBOL_INFO * symbol; + HANDLE process; + + process = GetCurrentProcess(); + + SymInitialize(process, NULL, TRUE); + + frames = CaptureStackBackTrace(0, MAXTRACE, stack, NULL); + symbol = (SYMBOL_INFO *) calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1); + symbol->MaxNameLen = 255; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + + for (i = 0; i < frames; i++) { + SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol); + + int status; + String line; + char * demangled = abi::__cxa_demangle(symbol->Name, 0, 0, &status); + line.set("%i: 0x%08x (%s)", i, symbol->Address, status == 0 ? demangled : symbol->Name); + free(demangled); + m_trace.push_back(line); + } + + free(symbol); +} + +#else + +#include +#include + +void Balau::GeneralException::genTrace() { + void * trace[MAXTRACE]; + int n = backtrace(trace, MAXTRACE); + char ** symbols = backtrace_symbols(trace, MAXTRACE); + + if (!symbols) + return; + + String line; + for (int i = 0; i < n; i++) + line += String().set("%08x ", trace[i]); + + m_trace.push_back(line); + Dl_info info; + + for (int i = 0; i < n; i++) { + int status; + String line; + dladdr(trace[i], &info); + long dist = ((char *) trace[i]) - ((char *) info.dli_saddr); + char * demangled; + if (info.dli_sname) { + demangled = abi::__cxa_demangle(info.dli_sname, 0, 0, &status); + } else { + demangled = NULL; + } + line.set("%i: %s(%s%c0x%x) [0x%08x]", i, info.dli_fname, info.dli_sname ? (demangled ? (status == 0 ? demangled : info.dli_sname) : info.dli_sname) : "??", dist < 0 ? '-' : '+', dist < 0 ? -dist : dist, trace[i]); + m_trace.push_back(line); + if (demangled) + free(demangled); + } + + free(symbols); +} + + +#endif diff --git a/src/HttpServer.cc b/src/HttpServer.cc index 18dc6c8..7df6406 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -7,6 +7,7 @@ class OutputCheck : public Balau::Handle { public: OutputCheck(Balau::IO h) : m_h(h), m_wrote(false) { Assert(m_h->canWrite()); m_name.set("OutputCheck(%s)", m_h->getName()); } virtual void close() throw (Balau::GeneralException) { m_h->close(); } + virtual bool isClosed() { return m_h->isClosed(); } virtual bool isEOF() { return m_h->isEOF(); } virtual bool canWrite() { return true; } virtual const char * getName() { return m_name.to_charp(); } @@ -429,7 +430,7 @@ bool Balau::HttpWorker::handleClient() { HttpServer::ActionFound f = m_server->findAction(uri.to_charp(), host.to_charp()); if (f.first) { - IO out(m_socket); + IO out(new OutputCheck(m_socket)); Http::Request req; req.method = method; req.host = host; @@ -443,13 +444,16 @@ bool Balau::HttpWorker::handleClient() { persistent = false; } catch (GeneralException e) { - Balau::Printer::elog(Balau::E_HTTPSERVER, "%s got an exception while processing its request: `%s'", m_name.to_charp(), e.getMsg()); + Printer::log(M_ERROR, "%s got an exception while processing its request: `%s'", m_name.to_charp(), e.getMsg()); + std::vector trace = e.getTrace(); + for (std::vector::iterator i = trace.begin(); i != trace.end(); i++) + Printer::log(M_DEBUG, "%s", i->to_charp()); if (!out->wrote()) send500(e.getMsg()); return false; } catch (...) { - Balau::Printer::elog(Balau::E_HTTPSERVER, "%s got an un unknow exception while processing its request: `%s'", m_name.to_charp()); + Printer::log(M_ERROR, "%s got an unknow exception while processing its request: `%s'", m_name.to_charp()); if (!out->wrote()) send500("unknow exception"); return false; diff --git a/src/Task.cc b/src/Task.cc index 6a94267..5623698 100644 --- a/src/Task.cc +++ b/src/Task.cc @@ -61,6 +61,9 @@ void Balau::Task::coroutine() { } catch (GeneralException & e) { Printer::log(M_WARNING, "Task %s at %p caused an exception: `%s' - stopping.", getName(), this, e.getMsg()); + std::vector trace = e.getTrace(); + for (std::vector::iterator i = trace.begin(); i != trace.end(); i++) + Printer::log(M_DEBUG, "%s", i->to_charp()); m_status = FAULTED; } catch (...) { diff --git a/tests/test-Http.cc b/tests/test-Http.cc index de3c6f3..15ef7de 100644 --- a/tests/test-Http.cc +++ b/tests/test-Http.cc @@ -10,10 +10,10 @@ using namespace Balau; class TestAction : public HttpServer::Action { public: TestAction() : Action(Regexes::any) { } - virtual bool Do(HttpServer * server, Http::Request & req, HttpServer::Action::ActionMatch & match, IO out); + virtual bool Do(HttpServer * server, Http::Request & req, HttpServer::Action::ActionMatch & match, IO out) throw (GeneralException); }; -bool TestAction::Do(HttpServer * server, Http::Request & req, HttpServer::Action::ActionMatch & match, IO out) { +bool TestAction::Do(HttpServer * server, Http::Request & req, HttpServer::Action::ActionMatch & match, IO out) throw (GeneralException) { static const char str[] = "HTTP/1.1 200 Found\r\n" "Content-Type: text/html; charset=UTF-8\r\n" @@ -36,7 +36,20 @@ bool TestAction::Do(HttpServer * server, Http::Request & req, HttpServer::Action return true; } +Balau::Regex testFailureURL("^/failure.html$"); + +class TestFailure : public HttpServer::Action { + public: + TestFailure() : Action(testFailureURL) { } + virtual bool Do(HttpServer * server, Http::Request & req, HttpServer::Action::ActionMatch & match, IO out) throw (GeneralException); +}; + +bool TestFailure::Do(HttpServer * server, Http::Request & req, HttpServer::Action::ActionMatch & match, IO out) throw (GeneralException) { + throw GeneralException("Test..."); +} + void MainTask::Do() { + Printer::enable(M_DEBUG); Printer::log(M_STATUS, "Test::Http running."); Thread * tms[4]; @@ -45,8 +58,10 @@ void MainTask::Do() { tms[i] = TaskMan::createThreadedTaskMan(); HttpServer * s = new HttpServer(); - TestAction * a = new TestAction(); + HttpServer::Action * a = new TestAction(); + HttpServer::Action * f = new TestFailure(); a->registerMe(s); + f->registerMe(s); s->setPort(8080); s->setLocal("localhost"); s->start(); -- cgit v1.2.3