Commit 083ef4e1 authored by Chris Toshok's avatar Chris Toshok

trace through the exception ferry during GC

store the exception ferry (as an ExcInfo, not ExcData) within our internal
thread state, and explicit visit type/value/traceback at gc time.

This allows the gc to run during an unwind (a necessity since
we're allocating new BoxedTraceback instances)
parent dd0f439e
...@@ -79,10 +79,16 @@ public: ...@@ -79,10 +79,16 @@ public:
std::vector<StackInfo> previous_stacks; std::vector<StackInfo> previous_stacks;
pthread_t pthread_id; pthread_t pthread_id;
ExcInfo exc_info;
PyThreadState* public_thread_state; PyThreadState* public_thread_state;
ThreadStateInternal(void* stack_start, pthread_t pthread_id, PyThreadState* public_thread_state) ThreadStateInternal(void* stack_start, pthread_t pthread_id, PyThreadState* public_thread_state)
: saved(false), stack_start(stack_start), pthread_id(pthread_id), public_thread_state(public_thread_state) {} : saved(false),
stack_start(stack_start),
pthread_id(pthread_id),
exc_info(NULL, NULL, NULL),
public_thread_state(public_thread_state) {}
void saveCurrent() { void saveCurrent() {
assert(!saved); assert(!saved);
...@@ -115,14 +121,14 @@ public: ...@@ -115,14 +121,14 @@ public:
void accept(gc::GCVisitor* v) { void accept(gc::GCVisitor* v) {
auto pub_state = public_thread_state; auto pub_state = public_thread_state;
if (pub_state->curexc_type) v->visitIf(pub_state->curexc_type);
v->visit(pub_state->curexc_type); v->visitIf(pub_state->curexc_value);
if (pub_state->curexc_value) v->visitIf(pub_state->curexc_traceback);
v->visit(pub_state->curexc_value); v->visitIf(pub_state->dict);
if (pub_state->curexc_traceback)
v->visit(pub_state->curexc_traceback); v->visitIf(exc_info.type);
if (pub_state->dict) v->visitIf(exc_info.value);
v->visit(pub_state->dict); v->visitIf(exc_info.traceback);
for (auto& stack_info : previous_stacks) { for (auto& stack_info : previous_stacks) {
v->visit(stack_info.next_generator); v->visit(stack_info.next_generator);
...@@ -149,6 +155,10 @@ void popGenerator() { ...@@ -149,6 +155,10 @@ void popGenerator() {
current_internal_thread_state->popGenerator(); current_internal_thread_state->popGenerator();
} }
ExcInfo* getExceptionFerry() {
return &current_internal_thread_state->exc_info;
}
// These are guarded by threading_lock // These are guarded by threading_lock
static int signals_waiting(0); static int signals_waiting(0);
static gc::GCVisitor* cur_visitor = NULL; static gc::GCVisitor* cur_visitor = NULL;
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "core/common.h" #include "core/common.h"
#include "core/thread_utils.h" #include "core/thread_utils.h"
#include "core/types.h"
namespace pyston { namespace pyston {
class Box; class Box;
...@@ -54,6 +55,7 @@ void visitAllStacks(gc::GCVisitor* v); ...@@ -54,6 +55,7 @@ void visitAllStacks(gc::GCVisitor* v);
void pushGenerator(BoxedGenerator* g, void* new_stack_start, void* old_stack_limit); void pushGenerator(BoxedGenerator* g, void* new_stack_start, void* old_stack_limit);
void popGenerator(); void popGenerator();
ExcInfo* getExceptionFerry();
#ifndef THREADING_USE_GIL #ifndef THREADING_USE_GIL
#define THREADING_USE_GIL 1 #define THREADING_USE_GIL 1
......
...@@ -88,6 +88,10 @@ public: ...@@ -88,6 +88,10 @@ public:
GCVisitor(TraceStack* stack) : stack(stack) {} GCVisitor(TraceStack* stack) : stack(stack) {}
// These all work on *user* pointers, ie pointers to the user_data section of GCAllocations // These all work on *user* pointers, ie pointers to the user_data section of GCAllocations
void visitIf(void* p) {
if (p)
visit(p);
}
void visit(void* p); void visit(void* p);
void visitRange(void* const* start, void* const* end); void visitRange(void* const* start, void* const* end);
void visitPotential(void* p); void visitPotential(void* p);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "codegen/unwinding.h" // getCFForAddress #include "codegen/unwinding.h" // getCFForAddress
#include "core/ast.h" #include "core/ast.h"
#include "core/stats.h" // StatCounter #include "core/stats.h" // StatCounter
#include "core/threading.h" // for getExceptionFerry
#include "core/types.h" // for ExcInfo #include "core/types.h" // for ExcInfo
#include "core/util.h" // Timer #include "core/util.h" // Timer
#include "runtime/generator.h" // generatorEntry #include "runtime/generator.h" // generatorEntry
...@@ -37,9 +38,6 @@ ...@@ -37,9 +38,6 @@
#define NORETURN __attribute__((__noreturn__)) #define NORETURN __attribute__((__noreturn__))
// canary used in ExcData in debug mode to catch exception-value corruption.
#define CANARY_VALUE 0xdeadbeef
// An action of 0 in the LSDA action table indicates cleanup. // An action of 0 in the LSDA action table indicates cleanup.
#define CLEANUP_ACTION 0 #define CLEANUP_ACTION 0
...@@ -75,33 +73,13 @@ template <typename T> static inline void check(T x) { ...@@ -75,33 +73,13 @@ template <typename T> static inline void check(T x) {
namespace pyston { namespace pyston {
struct ExcData; void checkExcInfo(const ExcInfo* exc) {
extern thread_local ExcData exception_ferry; assert(exc);
assert(exc->type && exc->value && exc->traceback);
struct ExcData { ASSERT(gc::isValidGCObject(exc->type), "%p", exc->type);
ExcInfo exc; ASSERT(gc::isValidGCObject(exc->value), "%p", exc->value);
#ifndef NDEBUG ASSERT(gc::isValidGCObject(exc->traceback), "%p", exc->traceback);
unsigned canary = CANARY_VALUE; }
#endif
ExcData() : exc(nullptr, nullptr, nullptr) {}
ExcData(ExcInfo e) : exc(e) {}
ExcData(Box* type, Box* value, Box* traceback) : exc(type, value, traceback) {}
void check() const {
assert(this);
assert(canary == CANARY_VALUE);
assert(exc.type && exc.value && exc.traceback);
ASSERT(gc::isValidGCObject(exc.type), "%p", exc.type);
ASSERT(gc::isValidGCObject(exc.value), "%p", exc.value);
ASSERT(gc::isValidGCObject(exc.traceback), "%p", exc.traceback);
assert(this == &exception_ferry);
}
};
thread_local ExcData exception_ferry;
static_assert(offsetof(ExcData, exc) == 0, "wrong offset");
static StatCounter us_unwind_loop("us_unwind_loop"); static StatCounter us_unwind_loop("us_unwind_loop");
static StatCounter us_unwind_resume_catch("us_unwind_resume_catch"); static StatCounter us_unwind_resume_catch("us_unwind_resume_catch");
...@@ -415,8 +393,8 @@ static inline bool find_call_site_entry(const lsda_info_t* info, const uint8_t* ...@@ -415,8 +393,8 @@ static inline bool find_call_site_entry(const lsda_info_t* info, const uint8_t*
} }
static inline NORETURN void resume(unw_cursor_t* cursor, const uint8_t* landing_pad, int64_t switch_value, static inline NORETURN void resume(unw_cursor_t* cursor, const uint8_t* landing_pad, int64_t switch_value,
const ExcData* exc_data) { const ExcInfo* exc_data) {
exc_data->check(); checkExcInfo(exc_data);
assert(landing_pad); assert(landing_pad);
if (VERBOSITY("cxx_unwind") >= 4) if (VERBOSITY("cxx_unwind") >= 4)
printf(" * RESUMED: ip %p switch_value %ld\n", (const void*)landing_pad, (long)switch_value); printf(" * RESUMED: ip %p switch_value %ld\n", (const void*)landing_pad, (long)switch_value);
...@@ -502,7 +480,7 @@ static inline int64_t determine_action(const lsda_info_t* info, const call_site_ ...@@ -502,7 +480,7 @@ static inline int64_t determine_action(const lsda_info_t* info, const call_site_
} }
// The stack-unwinding loop. // The stack-unwinding loop.
static inline void unwind_loop(ExcData* exc_data) { static inline void unwind_loop(ExcInfo* exc_data) {
// NB. https://monoinfinito.wordpress.com/series/exception-handling-in-c/ is a very useful resource // NB. https://monoinfinito.wordpress.com/series/exception-handling-in-c/ is a very useful resource
// as are http://www.airs.com/blog/archives/460 and http://www.airs.com/blog/archives/464 // as are http://www.airs.com/blog/archives/460 and http://www.airs.com/blog/archives/464
unw_cursor_t cursor; unw_cursor_t cursor;
...@@ -515,6 +493,8 @@ static inline void unwind_loop(ExcData* exc_data) { ...@@ -515,6 +493,8 @@ static inline void unwind_loop(ExcData* exc_data) {
unw_getcontext(&uc); unw_getcontext(&uc);
unw_init_local(&cursor, &uc); unw_init_local(&cursor, &uc);
BoxedTraceback** tb_loc = reinterpret_cast<BoxedTraceback**>(&threading::getExceptionFerry()->traceback);
while (unw_step(&cursor) > 0) { while (unw_step(&cursor) > 0) {
unw_proc_info_t pip; unw_proc_info_t pip;
{ {
...@@ -528,7 +508,7 @@ static inline void unwind_loop(ExcData* exc_data) { ...@@ -528,7 +508,7 @@ static inline void unwind_loop(ExcData* exc_data) {
print_frame(&cursor, &pip); print_frame(&cursor, &pip);
} }
maybeTracebackHere(&cursor, reinterpret_cast<BoxedTraceback**>(&exc_data->exc.traceback)); maybeTracebackHere(&cursor, tb_loc);
// Skip frames without handlers // Skip frames without handlers
if (pip.handler == 0) { if (pip.handler == 0) {
...@@ -584,8 +564,8 @@ static inline void unwind_loop(ExcData* exc_data) { ...@@ -584,8 +564,8 @@ static inline void unwind_loop(ExcData* exc_data) {
// The unwinder entry-point. // The unwinder entry-point.
static void unwind(ExcData* exc) { static void unwind(ExcInfo* exc) {
exc->check(); checkExcInfo(exc);
unwind_loop(exc); unwind_loop(exc);
// unwind_loop returned, couldn't find any handler. ruh-roh. // unwind_loop returned, couldn't find any handler. ruh-roh.
panic(); panic();
...@@ -619,7 +599,7 @@ extern "C" void _Unwind_Resume(struct _Unwind_Exception* _exc) { ...@@ -619,7 +599,7 @@ extern "C" void _Unwind_Resume(struct _Unwind_Exception* _exc) {
if (VERBOSITY("cxx_unwind") >= 4) if (VERBOSITY("cxx_unwind") >= 4)
printf("***** _Unwind_Resume() *****\n"); printf("***** _Unwind_Resume() *****\n");
// we give `_exc' type `struct _Unwind_Exception*' because unwind.h demands it; it's not actually accurate // we give `_exc' type `struct _Unwind_Exception*' because unwind.h demands it; it's not actually accurate
pyston::ExcData* data = (pyston::ExcData*)_exc; pyston::ExcInfo* data = (pyston::ExcInfo*)_exc;
pyston::unwind(data); pyston::unwind(data);
} }
...@@ -653,22 +633,21 @@ extern "C" void* __cxa_allocate_exception(size_t size) noexcept { ...@@ -653,22 +633,21 @@ extern "C" void* __cxa_allocate_exception(size_t size) noexcept {
// our exception info in curexc_*, and then unset these in __cxa_end_catch, then we'll wipe our exception info // our exception info in curexc_*, and then unset these in __cxa_end_catch, then we'll wipe our exception info
// during unwinding! // during unwinding!
return (void*)&pyston::exception_ferry; return pyston::threading::getExceptionFerry();
} }
// Takes the value that resume() sent us in RAX, and returns a pointer to the exception object actually thrown. In our // Takes the value that resume() sent us in RAX, and returns a pointer to the exception object actually thrown. In our
// case, these are the same, and should always be &pyston::exception_ferry. // case, these are the same, and should always be &pyston::exception_ferry.
extern "C" void* __cxa_begin_catch(void* exc_obj_in) noexcept { extern "C" void* __cxa_begin_catch(void* exc_obj_in) noexcept {
pyston::gc::endGCUnexpectedRegion();
assert(exc_obj_in); assert(exc_obj_in);
pyston::us_unwind_resume_catch.log(pyston::per_thread_resume_catch_timer.end()); pyston::us_unwind_resume_catch.log(pyston::per_thread_resume_catch_timer.end());
if (VERBOSITY("cxx_unwind") >= 4) if (VERBOSITY("cxx_unwind") >= 4)
printf("***** __cxa_begin_catch() *****\n"); printf("***** __cxa_begin_catch() *****\n");
pyston::ExcData* e = (pyston::ExcData*)exc_obj_in; pyston::ExcInfo* e = (pyston::ExcInfo*)exc_obj_in;
e->check(); checkExcInfo(e);
return (void*)&e->exc; return e;
} }
extern "C" void __cxa_end_catch() { extern "C" void __cxa_end_catch() {
...@@ -682,7 +661,6 @@ extern "C" void __cxa_end_catch() { ...@@ -682,7 +661,6 @@ extern "C" void __cxa_end_catch() {
extern "C" std::type_info EXCINFO_TYPE_INFO; extern "C" std::type_info EXCINFO_TYPE_INFO;
extern "C" void __cxa_throw(void* exc_obj, std::type_info* tinfo, void (*dtor)(void*)) { extern "C" void __cxa_throw(void* exc_obj, std::type_info* tinfo, void (*dtor)(void*)) {
pyston::gc::startGCUnexpectedRegion();
assert(!pyston::in_cleanup_code); assert(!pyston::in_cleanup_code);
assert(exc_obj); assert(exc_obj);
RELEASE_ASSERT(tinfo == &EXCINFO_TYPE_INFO, "can't throw a non-ExcInfo value! type info: %p", tinfo); RELEASE_ASSERT(tinfo == &EXCINFO_TYPE_INFO, "can't throw a non-ExcInfo value! type info: %p", tinfo);
...@@ -690,16 +668,16 @@ extern "C" void __cxa_throw(void* exc_obj, std::type_info* tinfo, void (*dtor)(v ...@@ -690,16 +668,16 @@ extern "C" void __cxa_throw(void* exc_obj, std::type_info* tinfo, void (*dtor)(v
if (VERBOSITY("cxx_unwind") >= 4) if (VERBOSITY("cxx_unwind") >= 4)
printf("***** __cxa_throw() *****\n"); printf("***** __cxa_throw() *****\n");
pyston::ExcData* exc_data = (pyston::ExcData*)exc_obj; pyston::ExcInfo* exc_data = (pyston::ExcInfo*)exc_obj;
exc_data->check(); checkExcInfo(exc_data);
pyston::unwind(exc_data); pyston::unwind(exc_data);
} }
extern "C" void* __cxa_get_exception_ptr(void* exc_obj_in) noexcept { extern "C" void* __cxa_get_exception_ptr(void* exc_obj_in) noexcept {
assert(exc_obj_in); assert(exc_obj_in);
pyston::ExcData* e = (pyston::ExcData*)exc_obj_in; pyston::ExcInfo* e = (pyston::ExcInfo*)exc_obj_in;
e->check(); checkExcInfo(e);
return (void*)&e->exc; return e;
} }
// We deliberately don't implement rethrowing because we can't implement it correctly with our current strategy for // We deliberately don't implement rethrowing because we can't implement it correctly with our current strategy for
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment