Commit 526e3969 authored by Chris Toshok's avatar Chris Toshok

rename UnwindSession to PythonUnwindSession, and add a few comments

parent 612c89f0
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
#include "runtime/long.h" #include "runtime/long.h"
#include "runtime/objmodel.h" #include "runtime/objmodel.h"
#include "runtime/set.h" #include "runtime/set.h"
#include "runtime/traceback.h"
#include "runtime/types.h" #include "runtime/types.h"
#ifndef NDEBUG #ifndef NDEBUG
......
...@@ -58,9 +58,7 @@ struct uw_table_entry { ...@@ -58,9 +58,7 @@ struct uw_table_entry {
namespace pyston { namespace pyston {
namespace {
static BoxedClass* unwind_session_cls; static BoxedClass* unwind_session_cls;
}
// Parse an .eh_frame section, and construct a "binary search table" such as you would find in a .eh_frame_hdr section. // Parse an .eh_frame section, and construct a "binary search table" such as you would find in a .eh_frame_hdr section.
// Currently only supports .eh_frame sections with exactly one fde. // Currently only supports .eh_frame sections with exactly one fde.
...@@ -440,6 +438,9 @@ static inline unw_word_t get_cursor_bp(unw_cursor_t* cursor) { ...@@ -440,6 +438,9 @@ static inline unw_word_t get_cursor_bp(unw_cursor_t* cursor) {
return get_cursor_reg(cursor, UNW_TDEP_BP); return get_cursor_reg(cursor, UNW_TDEP_BP);
} }
// if the given ip/bp correspond to a jitted frame or
// ASTInterpreter::execute_inner frame, return true and return the
// frame information through the PythonFrameIteratorImpl* info arg.
bool frameIsPythonFrame(unw_word_t ip, unw_word_t bp, unw_cursor_t* cursor, PythonFrameIteratorImpl* info) { bool frameIsPythonFrame(unw_word_t ip, unw_word_t bp, unw_cursor_t* cursor, PythonFrameIteratorImpl* info) {
CompiledFunction* cf = getCFForAddress(ip); CompiledFunction* cf = getCFForAddress(ip);
bool jitted = cf != NULL; bool jitted = cf != NULL;
...@@ -474,7 +475,7 @@ bool frameIsPythonFrame(unw_word_t ip, unw_word_t bp, unw_cursor_t* cursor, Pyth ...@@ -474,7 +475,7 @@ bool frameIsPythonFrame(unw_word_t ip, unw_word_t bp, unw_cursor_t* cursor, Pyth
return true; return true;
} }
class UnwindSession : public Box { class PythonUnwindSession : public Box {
ExcInfo exc_info; ExcInfo exc_info;
bool skip; bool skip;
bool is_active; bool is_active;
...@@ -482,9 +483,12 @@ class UnwindSession : public Box { ...@@ -482,9 +483,12 @@ class UnwindSession : public Box {
public: public:
DEFAULT_CLASS_SIMPLE(unwind_session_cls); DEFAULT_CLASS_SIMPLE(unwind_session_cls);
UnwindSession() : exc_info(NULL, NULL, NULL), skip(false), is_active(false) {} PythonUnwindSession() : exc_info(NULL, NULL, NULL), skip(false), is_active(false) {}
ExcInfo* getExcInfoStorage() { return &exc_info; } ExcInfo* getExcInfoStorage() {
RELEASE_ASSERT(is_active, "");
return &exc_info;
}
bool shouldSkipFrame() const { return skip; } bool shouldSkipFrame() const { return skip; }
void setShouldSkipNextFrame(bool skip) { this->skip = skip; } void setShouldSkipNextFrame(bool skip) { this->skip = skip; }
bool isActive() const { return is_active; } bool isActive() const { return is_active; }
...@@ -512,8 +516,13 @@ public: ...@@ -512,8 +516,13 @@ public:
static void gcHandler(GCVisitor* v, Box* _o) { static void gcHandler(GCVisitor* v, Box* _o) {
assert(_o->cls == unwind_session_cls); assert(_o->cls == unwind_session_cls);
UnwindSession* o = static_cast<UnwindSession*>(_o); PythonUnwindSession* o = static_cast<PythonUnwindSession*>(_o);
// this is our hack for eventually collecting
// exceptions/tracebacks after the exception has been caught.
// If a collection happens and a given thread's
// PythonUnwindSession isn't active, its exception info can be
// collected.
if (!o->is_active) if (!o->is_active)
return; return;
...@@ -522,29 +531,29 @@ public: ...@@ -522,29 +531,29 @@ public:
v->visitIf(o->exc_info.traceback); v->visitIf(o->exc_info.traceback);
} }
}; };
static __thread UnwindSession* cur_unwind; static __thread PythonUnwindSession* cur_unwind;
UnwindSession* beginUnwind() { PythonUnwindSession* beginPythonUnwindSession() {
if (!cur_unwind) { if (!cur_unwind) {
cur_unwind = new UnwindSession(); cur_unwind = new PythonUnwindSession();
pyston::gc::registerPermanentRoot(cur_unwind); pyston::gc::registerPermanentRoot(cur_unwind);
} }
cur_unwind->begin(); cur_unwind->begin();
return cur_unwind; return cur_unwind;
} }
UnwindSession* getUnwind() { PythonUnwindSession* getActivePythonUnwindSession() {
RELEASE_ASSERT(cur_unwind, ""); RELEASE_ASSERT(cur_unwind && cur_unwind->isActive(), "");
return cur_unwind; return cur_unwind;
} }
void endUnwind(UnwindSession* unwind) { void endPythonUnwindSession(PythonUnwindSession* unwind) {
RELEASE_ASSERT(unwind && unwind == cur_unwind, ""); RELEASE_ASSERT(unwind && unwind == cur_unwind, "");
unwind->end(); unwind->end();
} }
void* getExceptionStorage(UnwindSession* unwind) { void* getPythonUnwindSessionExceptionStorage(PythonUnwindSession* unwind) {
RELEASE_ASSERT(unwind && unwind == cur_unwind, ""); RELEASE_ASSERT(unwind && unwind == cur_unwind, "");
UnwindSession* state = static_cast<UnwindSession*>(unwind); PythonUnwindSession* state = static_cast<PythonUnwindSession*>(unwind);
return state->getExcInfoStorage(); return state->getExcInfoStorage();
} }
...@@ -559,14 +568,14 @@ static const LineInfo lineInfoForFrame(PythonFrameIteratorImpl* frame_it) { ...@@ -559,14 +568,14 @@ static const LineInfo lineInfoForFrame(PythonFrameIteratorImpl* frame_it) {
} }
void exceptionCaughtInInterpreter(LineInfo line_info, ExcInfo* exc_info) { void exceptionCaughtInInterpreter(LineInfo line_info, ExcInfo* exc_info) {
// basically the same as UnwindSession::addTraceback, but needs to // basically the same as PythonUnwindSession::addTraceback, but needs to
// be callable after an UnwindSession has ended. The interpreter // be callable after an PythonUnwindSession has ended. The interpreter
// will call this from catch blocks if it needs to ensure that a // will call this from catch blocks if it needs to ensure that a
// line is added. Right now this only happens in // line is added. Right now this only happens in
// ASTInterpreter::visit_invoke. // ASTInterpreter::visit_invoke.
// It's basically the same except for one thing: we don't have to // It's basically the same except for one thing: we don't have to
// worry about the 'skip' (osr) state that UnwindSession handles // worry about the 'skip' (osr) state that PythonUnwindSession handles
// here, because the only way we could have gotten into the ast // here, because the only way we could have gotten into the ast
// interpreter is if the exception wasn't caught, and if there was // interpreter is if the exception wasn't caught, and if there was
// the osr frame for the one the interpreter is running, it would // the osr frame for the one the interpreter is running, it would
...@@ -578,7 +587,7 @@ void exceptionCaughtInInterpreter(LineInfo line_info, ExcInfo* exc_info) { ...@@ -578,7 +587,7 @@ void exceptionCaughtInInterpreter(LineInfo line_info, ExcInfo* exc_info) {
BoxedTraceback::Here(line_info, reinterpret_cast<BoxedTraceback**>(&exc_info->traceback)); BoxedTraceback::Here(line_info, reinterpret_cast<BoxedTraceback**>(&exc_info->traceback));
} }
void unwindingThroughFrame(UnwindSession* unwind_session, unw_cursor_t* cursor) { void unwindingThroughFrame(PythonUnwindSession* unwind_session, unw_cursor_t* cursor) {
unw_word_t ip = get_cursor_ip(cursor); unw_word_t ip = get_cursor_ip(cursor);
unw_word_t bp = get_cursor_bp(cursor); unw_word_t bp = get_cursor_bp(cursor);
...@@ -597,7 +606,7 @@ void unwindingThroughFrame(UnwindSession* unwind_session, unw_cursor_t* cursor) ...@@ -597,7 +606,7 @@ void unwindingThroughFrame(UnwindSession* unwind_session, unw_cursor_t* cursor)
// C++11 range loops, for example). // C++11 range loops, for example).
// Return true from the handler to stop iteration at that frame. // Return true from the handler to stop iteration at that frame.
template <typename Func> void unwindPythonStack(Func func) { template <typename Func> void unwindPythonStack(Func func) {
UnwindSession* unwind_session = new UnwindSession(); PythonUnwindSession* unwind_session = new PythonUnwindSession();
unwind_session->begin(); unwind_session->begin();
...@@ -1146,8 +1155,8 @@ llvm::JITEventListener* makeTracebacksListener() { ...@@ -1146,8 +1155,8 @@ llvm::JITEventListener* makeTracebacksListener() {
} }
void setupUnwinding() { void setupUnwinding() {
unwind_session_cls = BoxedHeapClass::create(type_cls, object_cls, UnwindSession::gcHandler, 0, 0, unwind_session_cls = BoxedHeapClass::create(type_cls, object_cls, PythonUnwindSession::gcHandler, 0, 0,
sizeof(UnwindSession), false, "unwind_session"); sizeof(PythonUnwindSession), false, "unwind_session");
unwind_session_cls->freeze(); unwind_session_cls->freeze();
} }
} }
...@@ -41,12 +41,12 @@ CompiledFunction* getCFForAddress(uint64_t addr); ...@@ -41,12 +41,12 @@ CompiledFunction* getCFForAddress(uint64_t addr);
BoxedTraceback* getTraceback(); BoxedTraceback* getTraceback();
class UnwindSession; class PythonUnwindSession;
UnwindSession* beginUnwind(); PythonUnwindSession* beginPythonUnwindSession();
UnwindSession* getUnwind(); PythonUnwindSession* getActivePythonUnwindSession();
void endUnwind(UnwindSession* unwind_session); void endPythonUnwindSession(PythonUnwindSession* unwind_session);
void* getExceptionStorage(UnwindSession* unwind_session); void* getPythonUnwindSessionExceptionStorage(PythonUnwindSession* unwind_session);
void unwindingThroughFrame(UnwindSession* unwind_session, unw_cursor_t* cursor); void unwindingThroughFrame(PythonUnwindSession* unwind_session, unw_cursor_t* cursor);
void exceptionCaughtInInterpreter(LineInfo line_info, ExcInfo* exc_info); void exceptionCaughtInInterpreter(LineInfo line_info, ExcInfo* exc_info);
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include "llvm/Support/LEB128.h" // for {U,S}LEB128 decoding #include "llvm/Support/LEB128.h" // for {U,S}LEB128 decoding
#include "asm_writing/assembler.h" // assembler
#include "codegen/ast_interpreter.h" // interpreter_instr_addr #include "codegen/ast_interpreter.h" // interpreter_instr_addr
#include "codegen/unwinding.h" // getCFForAddress #include "codegen/unwinding.h" // getCFForAddress
#include "core/ast.h" #include "core/ast.h"
...@@ -492,7 +491,7 @@ static inline void unwind_loop(ExcInfo* exc_data) { ...@@ -492,7 +491,7 @@ static inline void unwind_loop(ExcInfo* exc_data) {
unw_getcontext(&uc); unw_getcontext(&uc);
unw_init_local(&cursor, &uc); unw_init_local(&cursor, &uc);
auto unwind_token = getUnwind(); auto unwind_session = getActivePythonUnwindSession();
while (unw_step(&cursor) > 0) { while (unw_step(&cursor) > 0) {
unw_proc_info_t pip; unw_proc_info_t pip;
...@@ -507,7 +506,10 @@ static inline void unwind_loop(ExcInfo* exc_data) { ...@@ -507,7 +506,10 @@ static inline void unwind_loop(ExcInfo* exc_data) {
print_frame(&cursor, &pip); print_frame(&cursor, &pip);
} }
unwindingThroughFrame(unwind_token, &cursor); // let the PythonUnwindSession know that we're in a new frame,
// giving it a chance to possibly add a traceback entry for
// it.
unwindingThroughFrame(unwind_session, &cursor);
// Skip frames without handlers // Skip frames without handlers
if (pip.handler == 0) { if (pip.handler == 0) {
...@@ -556,8 +558,23 @@ static inline void unwind_loop(ExcInfo* exc_data) { ...@@ -556,8 +558,23 @@ static inline void unwind_loop(ExcInfo* exc_data) {
int64_t switch_value = determine_action(&info, &entry); int64_t switch_value = determine_action(&info, &entry);
if (switch_value != CLEANUP_ACTION) { if (switch_value != CLEANUP_ACTION) {
endUnwind(unwind_token); // we're transfering control to a non-cleanup landing pad.
// i.e. a catch block. thus ends our unwind session.
endPythonUnwindSession(unwind_session);
} }
// there is a python unwinding implementation detail leaked
// here - that the unwind session can be ended but its
// exception storage is still around.
//
// this manifests itself as this short window here where we've
// (possibly) ended the unwind session above but we still need
// to pass exc_data (which is the exceptionStorage for this
// unwind session) to resume().
//
// the only way this could bite us is if we somehow clobber
// the PythonUnwindSession's storage, or cause a GC to occur, before
// transfering control to the landing pad in resume().
//
resume(&cursor, entry.landing_pad, switch_value, exc_data); resume(&cursor, entry.landing_pad, switch_value, exc_data);
} }
...@@ -612,30 +629,10 @@ extern "C" void* __cxa_allocate_exception(size_t size) noexcept { ...@@ -612,30 +629,10 @@ extern "C" void* __cxa_allocate_exception(size_t size) noexcept {
// we should only ever be throwing ExcInfos // we should only ever be throwing ExcInfos
RELEASE_ASSERT(size == sizeof(pyston::ExcInfo), "allocating exception whose size doesn't match ExcInfo"); RELEASE_ASSERT(size == sizeof(pyston::ExcInfo), "allocating exception whose size doesn't match ExcInfo");
// Instead of allocating memory for this exception, we return a pointer to a pre-allocated thread-local variable. // we begin the unwind session here rather than in __cxa_throw
// // because we need to return the session's exception storage
// This variable, pyston::exception_ferry, is used only while we are unwinding, and should not be used outside of // from this method.
// the unwinder. Since it's a thread-local variable, we *cannot* throw any exceptions while it is live, otherwise we return pyston::getPythonUnwindSessionExceptionStorage(pyston::beginPythonUnwindSession());
// would clobber it and forget our old exception.
//
// Q: Why can't we just use cur_thread_state.curexc_{type,value,traceback}?
//
// A: Because that conflates the space used to store exceptions during C++ unwinding with the space used to store
// them during C-API return-code based unwinding! This actually comes up in practice - the original version *did*
// use curexc_{type,value,traceback}, and it had a bug.
//
// In particular, we need to unset the C API exception at an appropriate point so as not to make C-API functions
// *think* an exception is being thrown when one isn't. The natural place is __cxa_begin_catch, BUT we need some way
// to communicate the exception info to the inside of the catch block - and all we get is the return value of
// __cxa_begin_catch, which is a single pointer, when we need three!
//
// You might think we could get away with only unsetting the C-API information in __cxa_end_catch, but you'd be
// wrong! Firstly, this would prohibit calling C-API functions inside a catch-block. Secondly, __cxa_end_catch is
// always called when leaving a catch block, even if we're leaving it by re-raising the exception. So if we store
// our exception info in curexc_*, and then unset these in __cxa_end_catch, then we'll wipe our exception info
// during unwinding!
return pyston::getExceptionStorage(pyston::beginUnwind());
} }
// 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
......
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