Add a facility to automatically create backtraces for exceptions
authorMikko Rasa <tdb@tdb.fi>
Tue, 17 Sep 2019 14:22:55 +0000 (17:22 +0300)
committerMikko Rasa <tdb@tdb.fi>
Tue, 17 Sep 2019 14:30:56 +0000 (17:30 +0300)
Obtaining a backtrace in the catch block won't show where the exception
happened.  Hooking into the exception handler internals is necessary.
Because this is somewhat disruptive to the language runtime, it is an
optional feature and disabled by default.

Build
source/debug/exceptiontrace.cpp [new file with mode: 0644]
source/debug/exceptiontrace.h [new file with mode: 0644]

diff --git a/Build b/Build
index 12e3114909efbd16356a498602d166db710ddf1e..6ece10b05e07c70993ee64c9032015c436b9c73a 100644 (file)
--- a/Build
+++ b/Build
@@ -41,6 +41,8 @@ package "mspcore"
                require "zlib";
        };
 
+       feature "exception_trace" "Support exception backtraces";
+
        library "mspcore"
        {
                source "source/core";
diff --git a/source/debug/exceptiontrace.cpp b/source/debug/exceptiontrace.cpp
new file mode 100644 (file)
index 0000000..30df4b6
--- /dev/null
@@ -0,0 +1,71 @@
+#include <string>
+#if !defined(_WIN32) && defined(__GLIBC__)
+#include <dlfcn.h>
+#endif
+#include "backtrace.h"
+#include "exceptiontrace.h"
+
+using namespace std;
+
+namespace {
+
+#if defined(WITH_EXCEPTION_TRACE) && !defined(_WIN32) && defined(__GLIBC__)
+typedef void CxaThrowType(void *, type_info *, void (*)(void *));
+CxaThrowType *orig_cxa_throw;
+
+struct HookThrow
+{
+       HookThrow();
+};
+
+HookThrow::HookThrow()
+{
+       void *handle = dlopen(0, RTLD_LAZY);
+       orig_cxa_throw = reinterpret_cast<CxaThrowType *>(dlvsym(handle, "__cxa_throw", "CXXABI_1.3"));
+       dlclose(handle);
+}
+
+HookThrow hook;
+#endif
+
+bool trace_enabled = false;
+
+Msp::Debug::Backtrace &get_thread_backtrace()
+{
+#if __cplusplus>=201103L
+       static thread_local Msp::Debug::Backtrace backtrace;
+#elif defined(__GNUC__)
+       static __thread Msp::Debug::Backtrace backtrace;
+#else
+       static Msp::Debug::Backtrace backtrace;
+#endif
+       return backtrace;
+}
+
+}
+
+namespace Msp {
+namespace Debug {
+
+void enable_exception_trace(bool e)
+{
+       trace_enabled = e;
+}
+
+const Backtrace &get_exception_trace()
+{
+       return get_thread_backtrace();
+}
+
+} // namespace Debug
+} // namespace Msp
+
+#if defined(WITH_EXCEPTION_TRACE) && !defined(_WIN32) && defined(__GLIBC__)
+extern "C" void __cxa_throw(void *exc, std::type_info *type, void (*dest) (void *))
+{
+       if(trace_enabled)
+               get_thread_backtrace() = Msp::Debug::Backtrace::create();
+       orig_cxa_throw(exc, type, dest);
+       std::terminate();
+}
+#endif
diff --git a/source/debug/exceptiontrace.h b/source/debug/exceptiontrace.h
new file mode 100644 (file)
index 0000000..52664b6
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef MSP_DEBUG_EXCEPTIONTRACE_H_
+#define MSP_DEBUG_EXCEPTIONTRACE_H_
+
+namespace Msp {
+namespace Debug {
+
+class Backtrace;
+
+void enable_exception_trace(bool);
+const Backtrace &get_exception_trace();
+
+} // namespace Debug
+} // namespace Msp
+
+#endif