Commit 20d98b25 authored by Chris Toshok's avatar Chris Toshok

create an UnwindSession type (gc allocated) per thread to contain all the python unwinding state.

parent 8e88774f
......@@ -632,19 +632,8 @@ Value ASTInterpreter::visit_invoke(AST_Invoke* node) {
next_block = node->normal_dest;
} catch (ExcInfo e) {
if (threading::ThreadStateInternal::getUnwindState() == UNWIND_STATE_NORMAL) {
// when generating the traceback incrementally we only
// include an interpreter frame if we unwind through
// ASTInterpreter::execute_inner. this will keep a toplevel
// invoke from showing up, since we catch the exception
// here.
auto source = getCF()->clfunc->source.get();
BoxedTraceback::Here(LineInfo(node->lineno, node->col_offset, source->fn, source->getName()),
reinterpret_cast<BoxedTraceback**>(&e.traceback));
}
threading::ThreadStateInternal::setUnwindState(UNWIND_STATE_NORMAL);
auto source = getCF()->clfunc->source.get();
exceptionCaughtInInterpreter(LineInfo(node->lineno, node->col_offset, source->fn, source->getName()), &e);
next_block = node->exc_dest;
last_exception = e;
......
......@@ -110,8 +110,8 @@ static llvm::Value* getClosureElementGep(IREmitter& emitter, llvm::Value* closur
static llvm::Value* getBoxedLocalsGep(llvm::IRBuilder<true>& builder, llvm::Value* v) {
static_assert(offsetof(FrameInfo, exc) == 0, "");
static_assert(sizeof(ExcInfo) == 24, "");
static_assert(offsetof(FrameInfo, boxedLocals) == 24, "");
static_assert(sizeof(ExcInfo) == 32, "");
static_assert(offsetof(FrameInfo, boxedLocals) == 32, "");
return builder.CreateConstInBoundsGEP2_32(v, 0, 1);
}
......@@ -122,9 +122,9 @@ static llvm::Value* getExcinfoGep(llvm::IRBuilder<true>& builder, llvm::Value* v
static llvm::Value* getFrameObjGep(llvm::IRBuilder<true>& builder, llvm::Value* v) {
static_assert(offsetof(FrameInfo, exc) == 0, "");
static_assert(sizeof(ExcInfo) == 24, "");
static_assert(sizeof(ExcInfo) == 32, "");
static_assert(sizeof(Box*) == 8, "");
static_assert(offsetof(FrameInfo, frame_obj) == 32, "");
static_assert(offsetof(FrameInfo, frame_obj) == 40, "");
return builder.CreateConstInBoundsGEP2_32(v, 0, 2);
// TODO: this could be made more resilient by doing something like
// gep->accumulateConstantOffset(g.tm->getDataLayout(), ap_offset)
......
......@@ -58,6 +58,10 @@ struct uw_table_entry {
namespace pyston {
namespace {
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.
// Currently only supports .eh_frame sections with exactly one fde.
// See http://www.airs.com/blog/archives/460 for some useful info.
......@@ -441,8 +445,7 @@ static inline unw_word_t get_cursor_bp(unw_cursor_t* cursor) {
return get_cursor_reg(cursor, UNW_TDEP_BP);
}
template <typename FrameFunc>
bool unwindProcessFrame(unw_word_t ip, unw_word_t bp, unw_cursor_t* cursor, FrameFunc func) {
bool unwindProcessFrame(unw_word_t ip, unw_word_t bp, unw_cursor_t* cursor, PythonFrameIteratorImpl* info) {
CompiledFunction* cf = getCFForAddress(ip);
bool jitted = cf != NULL;
if (!cf) {
......@@ -455,7 +458,7 @@ bool unwindProcessFrame(unw_word_t ip, unw_word_t bp, unw_cursor_t* cursor, Fram
if (!cf)
return false;
PythonFrameIteratorImpl info(jitted ? PythonFrameId::COMPILED : PythonFrameId::INTERPRETED, ip, bp, cf);
*info = PythonFrameIteratorImpl(jitted ? PythonFrameId::COMPILED : PythonFrameId::INTERPRETED, ip, bp, cf);
if (jitted) {
// Try getting all the callee-save registers, and save the ones we were able to get.
// Some of them may be inaccessible, I think because they weren't defined by that
......@@ -467,22 +470,56 @@ bool unwindProcessFrame(unw_word_t ip, unw_word_t bp, unw_cursor_t* cursor, Fram
int code = unw_get_reg(cursor, i, &r);
ASSERT(code == 0 || code == -UNW_EBADREG, "%d %d", code, i);
if (code == 0) {
info.regs[i] = r;
info.regs_valid |= (1 << i);
info->regs[i] = r;
info->regs_valid |= (1 << i);
}
}
}
return func(&info);
return true;
}
class UnwindSession : public Box {
ExcInfo exc_info;
bool skip;
public:
DEFAULT_CLASS_SIMPLE(unwind_session_cls);
UnwindSession() : exc_info(NULL, NULL, NULL), skip(false) {}
ExcInfo* getExcInfoStorage() { return &exc_info; }
bool shouldSkipFrame() const { return skip; }
void setShouldSkipNextFrame(bool skip) { this->skip = skip; }
void clear() {
exc_info = ExcInfo(NULL, NULL, NULL);
skip = false;
}
void addTraceback(const LineInfo& line_info) {
if (exc_info.reraise) {
exc_info.reraise = false;
return;
}
BoxedTraceback::Here(line_info, reinterpret_cast<BoxedTraceback**>(&exc_info.traceback));
}
static void gcHandler(GCVisitor* v, Box* _o) {
// assert(_o->cls == unwind_session_cls)
UnwindSession* o = static_cast<UnwindSession*>(_o);
v->visitIf(o->exc_info.type);
v->visitIf(o->exc_info.value);
v->visitIf(o->exc_info.traceback);
}
};
// While I'm not a huge fan of the callback-passing style, libunwind cursors are only valid for
// the stack frame that they were created in, so we need to use this approach (as opposed to
// C++11 range loops, for example).
// Return true from the handler to stop iteration at that frame.
template <typename Func> void unwindPythonStack(Func func) {
threading::ThreadStateInternal::setUnwindState(
UNWIND_STATE_NORMAL); // ensure we won't be skipping any python frames at the start
UnwindSession* unwind_state = (UnwindSession*)beginUnwind();
unw_context_t ctx;
unw_cursor_t cursor;
unw_getcontext(&ctx);
......@@ -498,20 +535,20 @@ template <typename Func> void unwindPythonStack(Func func) {
unw_word_t ip = get_cursor_ip(&cursor);
unw_word_t bp = get_cursor_bp(&cursor);
bool stop_unwinding = unwindProcessFrame(ip, bp, &cursor, [&](PythonFrameIteratorImpl* frame_iter) {
bool rtn = false;
if (threading::ThreadStateInternal::getUnwindState() == UNWIND_STATE_NORMAL)
rtn = func(frame_iter);
bool stop_unwinding = false;
threading::ThreadStateInternal::setUnwindState(
(bool)frame_iter->cf->entry_descriptor ? UNWIND_STATE_SKIPNEXT : UNWIND_STATE_NORMAL);
return rtn;
});
PythonFrameIteratorImpl frame_iter;
if (unwindProcessFrame(ip, bp, &cursor, &frame_iter)) {
if (!unwind_state->shouldSkipFrame())
stop_unwinding = func(&frame_iter);
if (stop_unwinding) {
break;
// frame_iter->cf->entry_descriptor will be non-null for OSR frames.
unwind_state->setShouldSkipNextFrame((bool)frame_iter.cf->entry_descriptor);
}
if (stop_unwinding)
break;
if (inGeneratorEntry(ip)) {
// for generators continue unwinding in the context in which the generator got called
Context* remote_ctx = getReturnContextForGeneratorFrame((void*)bp);
......@@ -530,6 +567,8 @@ template <typename Func> void unwindPythonStack(Func func) {
// keep unwinding
}
endUnwind(unwind_state);
}
static std::unique_ptr<PythonFrameIteratorImpl> getTopPythonFrame() {
......@@ -552,23 +591,58 @@ static const LineInfo lineInfoForFrame(PythonFrameIteratorImpl* frame_it) {
return LineInfo(current_stmt->lineno, current_stmt->col_offset, source->fn, source->getName());
}
void maybeTracebackHere(void* unw_cursor) {
void exceptionCaughtInInterpreter(LineInfo line_info, ExcInfo* exc_info) {
if (exc_info->reraise) {
exc_info->reraise = false;
return;
}
BoxedTraceback::Here(line_info, reinterpret_cast<BoxedTraceback**>(&exc_info->traceback));
}
static __thread UnwindSession* cur_unwind;
void* beginUnwind() {
if (!cur_unwind) {
cur_unwind = new UnwindSession();
pyston::gc::registerPermanentRoot(cur_unwind);
}
// if we can figure out a way to make endUnwind in cxx_uwind.cpp work, we can remove this.
cur_unwind->clear();
return cur_unwind;
}
void* getUnwind() {
RELEASE_ASSERT(cur_unwind, "");
return cur_unwind;
}
void endUnwind(void* unwind) {
RELEASE_ASSERT(unwind && unwind == cur_unwind, "");
cur_unwind->clear();
}
void* getExceptionFerry(void* unwind) {
RELEASE_ASSERT(unwind && unwind == cur_unwind, "");
UnwindSession* state = static_cast<UnwindSession*>(unwind);
return state->getExcInfoStorage();
}
void maybeTracebackHere(void* unw_cursor, void* unwind_token) {
unw_cursor_t* cursor = (unw_cursor_t*)unw_cursor;
UnwindSession* state = (UnwindSession*)unwind_token;
unw_word_t ip = get_cursor_ip(cursor);
unw_word_t bp = get_cursor_bp(cursor);
BoxedTraceback** tb_loc
= reinterpret_cast<BoxedTraceback**>(&threading::ThreadStateInternal::getExceptionFerry()->traceback);
unwindProcessFrame(ip, bp, cursor, [&](PythonFrameIteratorImpl* frame_iter) {
if (threading::ThreadStateInternal::getUnwindState() == UNWIND_STATE_NORMAL)
BoxedTraceback::Here(lineInfoForFrame(frame_iter), tb_loc);
PythonFrameIteratorImpl frame_iter;
if (unwindProcessFrame(ip, bp, cursor, &frame_iter)) {
if (!state->shouldSkipFrame()) {
state->addTraceback(lineInfoForFrame(&frame_iter));
}
threading::ThreadStateInternal::setUnwindState((bool)frame_iter->cf->entry_descriptor ? UNWIND_STATE_SKIPNEXT
: UNWIND_STATE_NORMAL);
return false;
});
// frame_iter->cf->entry_descriptor will be non-null for OSR frames.
state->setShouldSkipNextFrame((bool)frame_iter.cf->entry_descriptor);
}
}
// To produce a traceback, we:
......@@ -1053,4 +1127,10 @@ void logByCurrentPythonLine(const std::string& stat_name) {
llvm::JITEventListener* makeTracebacksListener() {
return new TracebacksEventListener();
}
void setupUnwinding() {
unwind_session_cls = BoxedHeapClass::create(type_cls, object_cls, UnwindSession::gcHandler, 0, 0,
sizeof(UnwindSession), false, "unwind_session");
unwind_session_cls->freeze();
}
}
......@@ -29,6 +29,7 @@ struct FrameInfo;
void registerDynamicEhFrame(uint64_t code_addr, size_t code_size, uint64_t eh_frame_addr, size_t eh_frame_size);
void setupUnwinding();
BoxedModule* getCurrentModule();
Box* getGlobals(); // returns either the module or a globals dict
Box* getGlobalsDict(); // always returns a dict-like object
......@@ -36,7 +37,13 @@ CompiledFunction* getCFForAddress(uint64_t addr);
BoxedTraceback* getTraceback();
void maybeTracebackHere(void* unw_cursor);
void* beginUnwind();
void* getUnwind();
void endUnwind(void* unwind_token);
void* getExceptionFerry(void* unwind_token);
void exceptionCaughtInInterpreter(LineInfo line_info, ExcInfo* exc_info);
void maybeTracebackHere(void* unw_cursor, void* unwind_token);
struct ExecutionPoint {
CompiledFunction* cf;
......
......@@ -52,12 +52,7 @@ PthreadFastMutex threading_lock;
int num_starting_threads(0);
ThreadStateInternal::ThreadStateInternal(void* stack_start, pthread_t pthread_id, PyThreadState* public_thread_state)
: saved(false),
stack_start(stack_start),
pthread_id(pthread_id),
unwind_state(UNWIND_STATE_NORMAL),
exc_info(NULL, NULL, NULL),
public_thread_state(public_thread_state) {
: saved(false), stack_start(stack_start), pthread_id(pthread_id), public_thread_state(public_thread_state) {
}
void ThreadStateInternal::accept(gc::GCVisitor* v) {
......@@ -67,10 +62,6 @@ void ThreadStateInternal::accept(gc::GCVisitor* v) {
v->visitIf(pub_state->curexc_traceback);
v->visitIf(pub_state->dict);
v->visitIf(exc_info.type);
v->visitIf(exc_info.value);
v->visitIf(exc_info.traceback);
for (auto& stack_info : previous_stacks) {
v->visit(stack_info.next_generator);
#if STACK_GROWS_DOWN
......
......@@ -32,13 +32,6 @@ namespace gc {
class GCVisitor;
}
// somewhat similar to CPython's WHY_* enum
// UNWIND_STATE_NORMAL : == WHY_EXCEPTION. we call it "NORMAL" since we often unwind due to things other than
// exceptions (getGlobals, getLocals, etc)
// UNWIND_STATE_SKIPNEXT: skip this frame (do not include it in tracebacks). this happens when re-raising an exception
// and also when dealing with osr replacements (we skip the frame we OSR).
enum UnwindState { UNWIND_STATE_NORMAL = 0, UNWIND_STATE_SKIPNEXT };
namespace threading {
class ThreadStateInternal {
......@@ -82,9 +75,6 @@ public:
std::vector<StackInfo> previous_stacks;
pthread_t pthread_id;
UnwindState unwind_state;
ExcInfo exc_info;
PyThreadState* public_thread_state;
ThreadStateInternal(void* stack_start, pthread_t pthread_id, PyThreadState* public_thread_state);
......@@ -117,21 +107,6 @@ public:
assert(ThreadStateInternal::current);
current->_popGenerator();
}
inline static void setUnwindState(UnwindState state) {
assert(ThreadStateInternal::current);
current->unwind_state = state;
}
inline static UnwindState getUnwindState() {
assert(ThreadStateInternal::current);
return current->unwind_state;
}
static ExcInfo* getExceptionFerry() {
assert(ThreadStateInternal::current);
return &current->exc_info;
}
};
// Whether or not a second thread was ever started:
......
......@@ -679,11 +679,12 @@ public:
struct ExcInfo {
Box* type, *value, *traceback;
bool reraise;
#ifndef NDEBUG
ExcInfo(Box* type, Box* value, Box* traceback);
#else
ExcInfo(Box* type, Box* value, Box* traceback) : type(type), value(value), traceback(traceback) {}
ExcInfo(Box* type, Box* value, Box* traceback) : type(type), value(value), traceback(traceback), reraise(false) {}
#endif
bool matches(BoxedClass* cls) const;
void printExcAndTraceback() const;
......
......@@ -493,12 +493,14 @@ static inline void unwind_loop(ExcInfo* exc_data) {
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
void* unwind_token = getUnwind();
while (unw_step(&cursor) > 0) {
unw_proc_info_t pip;
{
// NB. unw_get_proc_info is slow; a significant chunk of all time spent unwinding is spent here.
check(unw_get_proc_info(&cursor, &pip));
}
// NB. unw_get_proc_info is slow; a significant chunk of all time spent unwinding is spent here.
check(unw_get_proc_info(&cursor, &pip));
assert((pip.lsda == 0) == (pip.handler == 0));
assert(pip.flags == 0);
......@@ -506,7 +508,7 @@ static inline void unwind_loop(ExcInfo* exc_data) {
print_frame(&cursor, &pip);
}
maybeTracebackHere(&cursor);
maybeTracebackHere(&cursor, unwind_token);
// Skip frames without handlers
if (pip.handler == 0) {
......@@ -554,6 +556,10 @@ static inline void unwind_loop(ExcInfo* exc_data) {
}
int64_t switch_value = determine_action(&info, &entry);
if (switch_value != CLEANUP_ACTION) {
// printf ("cleanup action == %ld, should end unwind session\n", switch_value);
// endUnwind(unwind_token);
}
resume(&cursor, entry.landing_pad, switch_value, exc_data);
}
......@@ -631,7 +637,7 @@ 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
// during unwinding!
return pyston::threading::ThreadStateInternal::getExceptionFerry();
return pyston::getExceptionFerry(pyston::getUnwind());
}
// Takes the value that resume() sent us in RAX, and returns a pointer to the exception object actually thrown. In our
......
......@@ -72,11 +72,12 @@ void raiseRaw(const ExcInfo& e) {
#endif
#endif
// printf ("beginning unwind\n");
beginUnwind();
throw e;
}
void raiseExc(Box* exc_obj) {
threading::ThreadStateInternal::setUnwindState(UNWIND_STATE_NORMAL);
raiseRaw(ExcInfo(exc_obj->cls, exc_obj, new BoxedTraceback()));
}
......@@ -205,13 +206,13 @@ extern "C" void raise0() {
if (exc_info->type == None)
raiseExcHelper(TypeError, "exceptions must be old-style classes or derived from BaseException, not NoneType");
threading::ThreadStateInternal::setUnwindState(UNWIND_STATE_SKIPNEXT);
exc_info->reraise = true;
raiseRaw(*exc_info);
}
#ifndef NDEBUG
ExcInfo::ExcInfo(Box* type, Box* value, Box* traceback) : type(type), value(value), traceback(traceback) {
ExcInfo::ExcInfo(Box* type, Box* value, Box* traceback)
: type(type), value(value), traceback(traceback), reraise(false) {
}
#endif
......@@ -286,7 +287,7 @@ extern "C" void raise3(Box* arg0, Box* arg1, Box* arg2) {
bool reraise = arg2 != NULL && arg2 != None;
auto exc_info = excInfoForRaise(arg0, arg1, arg2);
threading::ThreadStateInternal::setUnwindState(reraise ? UNWIND_STATE_SKIPNEXT : UNWIND_STATE_NORMAL);
exc_info.reraise = reraise;
raiseRaw(exc_info);
}
......
......@@ -2703,6 +2703,7 @@ void setupRuntime() {
closure_cls->freeze();
setupUnwinding();
setupInterpreter();
setupCAPI();
......
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