From edffb40ef2dd3789a590ce877b680b5991b5e46d Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 27 Jul 2015 13:48:42 +0300 Subject: [PATCH] On uncaught exception, execute finally clauses and collect backtrace. --- lit-test/libartiq_personality/Makefile | 2 +- .../libartiq_personality/artiq_terminate.c | 17 +++++++- lit-test/test/exceptions/finally_uncaught.py | 12 ++++++ soc/runtime/artiq_personality.c | 42 ++++++++++++++++++- soc/runtime/artiq_personality.h | 9 +++- 5 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 lit-test/test/exceptions/finally_uncaught.py diff --git a/lit-test/libartiq_personality/Makefile b/lit-test/libartiq_personality/Makefile index 4fa32ab96..2a72a7185 100644 --- a/lit-test/libartiq_personality/Makefile +++ b/lit-test/libartiq_personality/Makefile @@ -1,4 +1,4 @@ CC ?= clang libartiq_personality.so: ../../soc/runtime/artiq_personality.c artiq_terminate.c - $(CC) -Wall -Werror -I. -I../../soc/runtime -g -fPIC -shared -o $@ $^ + $(CC) -std=c99 -Wall -Werror -I. -I../../soc/runtime -g -fPIC -shared -o $@ $^ diff --git a/lit-test/libartiq_personality/artiq_terminate.c b/lit-test/libartiq_personality/artiq_terminate.c index c4c2bf7db..5b1315131 100644 --- a/lit-test/libartiq_personality/artiq_terminate.c +++ b/lit-test/libartiq_personality/artiq_terminate.c @@ -4,11 +4,26 @@ #include #include -void __artiq_terminate(struct artiq_exception *exn) { +#define __USE_GNU +#include + +void __artiq_terminate(struct artiq_exception *exn, + struct artiq_backtrace_item *backtrace, + size_t backtrace_size) { printf("Uncaught %s: %s (%"PRIi64", %"PRIi64", %"PRIi64")\n" "at %s:%"PRIi32":%"PRIi32"\n", exn->name, exn->message, exn->param[0], exn->param[1], exn->param[1], exn->file, exn->line, exn->column + 1); + + for(size_t i = 0; i < backtrace_size; i++) { + Dl_info info; + if(dladdr((void*) backtrace[i].function, &info) && info.dli_sname) { + printf("at %s+%p\n", info.dli_sname, (void*)backtrace[i].offset); + } else { + printf("at %p+%p\n", (void*)backtrace[i].function, (void*)backtrace[i].offset); + } + } + exit(1); } diff --git a/lit-test/test/exceptions/finally_uncaught.py b/lit-test/test/exceptions/finally_uncaught.py new file mode 100644 index 000000000..1eb211663 --- /dev/null +++ b/lit-test/test/exceptions/finally_uncaught.py @@ -0,0 +1,12 @@ +# RUN: %not %python -m artiq.compiler.testbench.jit %s >%t +# RUN: OutputCheck %s --file-to-check=%t +# REQUIRES: exceptions + +def f(): + try: + 1/0 + finally: + print("f-fin") + +# CHECK-L: f-fin +f() diff --git a/soc/runtime/artiq_personality.c b/soc/runtime/artiq_personality.c index a8c19cfd0..64ee13c31 100644 --- a/soc/runtime/artiq_personality.c +++ b/soc/runtime/artiq_personality.c @@ -200,11 +200,17 @@ static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) { #define ARTIQ_EXCEPTION_CLASS 0x4152545141525451LL // 'ARTQARTQ' static void __artiq_cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception *exc); +static _Unwind_Reason_Code __artiq_uncaught_exception( + int version, _Unwind_Action actions, uint64_t exceptionClass, + struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context, + void *stop_parameter); struct artiq_raised_exception { struct _Unwind_Exception unwind; struct artiq_exception artiq; int handled; + struct artiq_backtrace_item backtrace[1024]; + size_t backtrace_size; }; static struct artiq_raised_exception inflight; @@ -216,13 +222,17 @@ void __artiq_raise(struct artiq_exception *artiq_exn) { inflight.unwind.exception_class = ARTIQ_EXCEPTION_CLASS; inflight.unwind.exception_cleanup = &__artiq_cleanup; inflight.handled = 0; + inflight.backtrace_size = 0; _Unwind_Reason_Code result = _Unwind_RaiseException(&inflight.unwind); EH_ASSERT((result == _URC_END_OF_STACK) && "Unexpected error during unwinding"); // If we're here, there are no handlers, only cleanups. - __artiq_terminate(&inflight.artiq); + // Force unwinding anyway; we shall stop at nothing except the end of stack. + result = _Unwind_ForcedUnwind(&inflight.unwind, &__artiq_uncaught_exception, + NULL); + EH_FAIL("_Unwind_ForcedUnwind should not return"); } void __artiq_reraise() { @@ -238,7 +248,7 @@ void __artiq_reraise() { } } -/* Catching */ +/* Unwinding */ // The code below does not refer to the `inflight` global. @@ -250,6 +260,34 @@ static void __artiq_cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception memset(&inflight->artiq, 0, sizeof(struct artiq_exception)); } +static _Unwind_Reason_Code __artiq_uncaught_exception( + int version, _Unwind_Action actions, uint64_t exceptionClass, + struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context, + void *stop_parameter) { + struct artiq_raised_exception *inflight = + (struct artiq_raised_exception*)exceptionObject; + EH_ASSERT(inflight->backtrace_size < + sizeof(inflight->backtrace) / sizeof(inflight->backtrace[0]) && + "Out of space for backtrace"); + + uintptr_t pc = _Unwind_GetIP(context); + uintptr_t funcStart = _Unwind_GetRegionStart(context); + uintptr_t pcOffset = pc - funcStart; + EH_LOG("===> uncaught (pc=%p+%p)", (void*)funcStart, (void*)pcOffset); + + inflight->backtrace[inflight->backtrace_size].function = funcStart; + inflight->backtrace[inflight->backtrace_size].offset = pcOffset; + ++inflight->backtrace_size; + + if(actions & _UA_END_OF_STACK) { + EH_LOG0("end of stack"); + __artiq_terminate(&inflight->artiq, inflight->backtrace, inflight->backtrace_size); + } else { + EH_LOG0("continue"); + return _URC_NO_REASON; + } +} + _Unwind_Reason_Code __artiq_personality( int version, _Unwind_Action actions, uint64_t exceptionClass, struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context) { diff --git a/soc/runtime/artiq_personality.h b/soc/runtime/artiq_personality.h index 2e3fc0f45..82a5a7124 100644 --- a/soc/runtime/artiq_personality.h +++ b/soc/runtime/artiq_personality.h @@ -15,6 +15,11 @@ struct artiq_exception { int64_t param[3]; }; +struct artiq_backtrace_item { + intptr_t function; + intptr_t offset; +}; + #ifdef __cplusplus extern "C" { #endif @@ -26,7 +31,9 @@ void __artiq_reraise() __attribute__((noreturn)); /* Called by the runtime */ -void __artiq_terminate(struct artiq_exception *artiq_exn) +void __artiq_terminate(struct artiq_exception *artiq_exn, + struct artiq_backtrace_item *backtrace, + size_t backtrace_size) __attribute__((noreturn)); #ifdef __cplusplus