Commit 4b7c277f authored by Kevin Modzelewski's avatar Kevin Modzelewski

Move a bunch of stuff out of UnwindSession

It was pretty unwieldy and handled some disparate parts of the
exception-raising process.  I think things are a bit cleaner now:
- cxx_unwind.cpp handles C++ unwinding semantics
- unwinding.cpp converts C stacks to Python stacks
- exceptions.cpp takes Python stack frames and handles them appropriately

So for throwing a C++ exception, it starts out in cxx_unwind.cpp, which
then hands off the C frames to unwinding.cpp, which then hands off the
Python frames to exceptions.cpp.  When we get exceptions not via uncaught
C++ exceptions (ie explicitly handled C++ exceptions or CAPI exceptions),
those go directly into exceptions.cpp.  There are also non-exception cases
that we want to get the Python stack trace (ex sys._getframe), and those
are handled by unwinding.cpp
parent 10ac9f32
......@@ -131,8 +131,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) == 32, "");
static_assert(offsetof(FrameInfo, boxedLocals) == 32, "");
static_assert(sizeof(ExcInfo) == 24, "");
static_assert(offsetof(FrameInfo, boxedLocals) == 24, "");
return builder.CreateConstInBoundsGEP2_32(v, 0, 1);
}
......@@ -143,9 +143,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) == 32, "");
static_assert(sizeof(ExcInfo) == 24, "");
static_assert(sizeof(Box*) == 8, "");
static_assert(offsetof(FrameInfo, frame_obj) == 40, "");
static_assert(offsetof(FrameInfo, frame_obj) == 32, "");
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)
......
......@@ -493,6 +493,11 @@ static const LineInfo lineInfoForFrame(PythonFrameIteratorImpl* frame_it) {
return LineInfo(current_stmt->lineno, current_stmt->col_offset, source->fn, source->getName());
}
// A class that converts a C stack trace to a Python stack trace.
// It allows for different ways of driving the C stack trace; it just needs
// to have handleCFrame called once per frame.
// If you want to do a normal (non-destructive) stack walk, use unwindPythonStack
// which will use this internally.
class PythonStackExtractor {
private:
bool skip_next_pythonlike_frame = false;
......@@ -522,74 +527,42 @@ public:
class PythonUnwindSession : public Box {
ExcInfo exc_info;
PythonStackExtractor pystack_extractor;
bool is_active;
Timer t;
public:
DEFAULT_CLASS_SIMPLE(unwind_session_cls);
PythonUnwindSession() : exc_info(NULL, NULL, NULL), is_active(false), t(/*min_usec=*/10000) {}
PythonUnwindSession() : exc_info(NULL, NULL, NULL), t(/*min_usec=*/10000) {}
ExcInfo* getExcInfoStorage() {
RELEASE_ASSERT(is_active, "");
return &exc_info;
}
bool isActive() const { return is_active; }
ExcInfo* getExcInfoStorage() { return &exc_info; }
void begin() {
RELEASE_ASSERT(!is_active, "");
exc_info = ExcInfo(NULL, NULL, NULL);
is_active = true;
t.restart();
static StatCounter stat("unwind_sessions");
stat.log();
}
void end() {
RELEASE_ASSERT(is_active, "");
is_active = false;
static StatCounter stat("us_unwind_session");
stat.log(t.end());
}
void addTraceback(PythonFrameIteratorImpl& frame_iter) {
RELEASE_ASSERT(is_active, "");
if (exc_info.reraise) {
exc_info.reraise = false;
return;
}
// TODO: shouldn't fetch this multiple times?
frame_iter.getCurrentStatement()->cxx_exception_count++;
auto line_info = lineInfoForFrame(&frame_iter);
BoxedTraceback::here(line_info, &exc_info.traceback);
}
void logException() {
#if STAT_EXCEPTIONS
static StatCounter num_exceptions("num_exceptions");
num_exceptions.log();
std::string stat_name;
if (PyType_Check(exc_info.type))
stat_name = "num_exceptions_" + std::string(static_cast<BoxedClass*>(exc_info.type)->tp_name);
else
stat_name = "num_exceptions_" + std::string(exc_info.value->cls->tp_name);
Stats::log(Stats::getStatCounter(stat_name));
#if STAT_EXCEPTIONS_LOCATION
logByCurrentPythonLine(stat_name);
#endif
#endif
}
void handleCFrame(unw_cursor_t* cursor) {
unw_word_t ip = get_cursor_ip(cursor);
unw_word_t bp = get_cursor_bp(cursor);
PythonFrameIteratorImpl frame_iter;
bool found_frame = pystack_extractor.handleCFrame(cursor, &frame_iter);
if (found_frame)
addTraceback(frame_iter);
if (found_frame) {
if (exceptionAtLineCheck()) {
// TODO: shouldn't fetch this multiple times?
frame_iter.getCurrentStatement()->cxx_exception_count++;
auto line_info = lineInfoForFrame(&frame_iter);
exceptionAtLine(line_info, &exc_info.traceback);
}
}
}
static void gcHandler(GCVisitor* v, Box* _o) {
......@@ -597,14 +570,6 @@ public:
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)
return;
v->visitIf(o->exc_info.type);
v->visitIf(o->exc_info.value);
v->visitIf(o->exc_info.traceback);
......@@ -612,17 +577,22 @@ public:
};
static __thread PythonUnwindSession* cur_unwind;
PythonUnwindSession* beginPythonUnwindSession() {
static PythonUnwindSession* getUnwindSession() {
if (!cur_unwind) {
cur_unwind = new PythonUnwindSession();
pyston::gc::registerPermanentRoot(cur_unwind);
}
return cur_unwind;
}
PythonUnwindSession* beginPythonUnwindSession() {
getUnwindSession();
cur_unwind->begin();
return cur_unwind;
}
PythonUnwindSession* getActivePythonUnwindSession() {
RELEASE_ASSERT(cur_unwind && cur_unwind->isActive(), "");
ASSERT(cur_unwind, "");
return cur_unwind;
}
......@@ -630,58 +600,13 @@ void endPythonUnwindSession(PythonUnwindSession* unwind) {
RELEASE_ASSERT(unwind && unwind == cur_unwind, "");
unwind->end();
}
void* getPythonUnwindSessionExceptionStorage(PythonUnwindSession* unwind) {
RELEASE_ASSERT(unwind && unwind == cur_unwind, "");
PythonUnwindSession* state = static_cast<PythonUnwindSession*>(unwind);
return state->getExcInfoStorage();
}
void throwingException(PythonUnwindSession* unwind) {
RELEASE_ASSERT(unwind && unwind == cur_unwind, "");
unwind->logException();
}
extern "C" void capiExcCaughtInJit(AST_stmt* stmt, void* _source_info) {
SourceInfo* source = static_cast<SourceInfo*>(_source_info);
// TODO: handle reraise (currently on the ExcInfo object)
PyThreadState* tstate = PyThreadState_GET();
BoxedTraceback::here(LineInfo(stmt->lineno, stmt->col_offset, source->fn, source->getName()),
&tstate->curexc_traceback);
}
extern "C" void reraiseJitCapiExc() {
ensureCAPIExceptionSet();
// TODO: we are normalizing to many times?
ExcInfo e = excInfoForRaise(cur_thread_state.curexc_type, cur_thread_state.curexc_value,
cur_thread_state.curexc_traceback);
PyErr_Clear();
e.reraise = true;
throw e;
}
void exceptionCaughtInInterpreter(LineInfo line_info, ExcInfo* exc_info) {
static StatCounter frames_unwound("num_frames_unwound_python");
frames_unwound.log();
// basically the same as PythonUnwindSession::addTraceback, but needs to
// be callable after an PythonUnwindSession has ended. The interpreter
// will call this from catch blocks if it needs to ensure that a
// line is added. Right now this only happens in
// ASTInterpreter::visit_invoke.
// It's basically the same except for one thing: we don't have to
// worry about the 'skip' (osr) state that PythonUnwindSession handles
// here, because the only way we could have gotten into the ast
// interpreter is if the exception wasn't caught, and if there was
// the osr frame for the one the interpreter is running, it would
// have already caught it.
if (exc_info->reraise) {
exc_info->reraise = false;
return;
}
BoxedTraceback::here(line_info, &exc_info->traceback);
}
void unwindingThroughFrame(PythonUnwindSession* unwind_session, unw_cursor_t* cursor) {
unwind_session->handleCFrame(cursor);
}
......
......@@ -44,13 +44,18 @@ Box* getTraceback();
class PythonUnwindSession;
PythonUnwindSession* beginPythonUnwindSession();
PythonUnwindSession* getActivePythonUnwindSession();
void throwingException(PythonUnwindSession* unwind_session);
void endPythonUnwindSession(PythonUnwindSession* unwind_session);
void* getPythonUnwindSessionExceptionStorage(PythonUnwindSession* unwind_session);
void unwindingThroughFrame(PythonUnwindSession* unwind_session, unw_cursor_t* cursor);
// TODO move these to exceptions.h
void logException(ExcInfo* exc_info);
void startReraise();
bool exceptionAtLineCheck();
void exceptionAtLine(LineInfo line_info, Box** traceback);
void exceptionCaughtInInterpreter(LineInfo line_info, ExcInfo* exc_info);
CLFunction* getTopPythonFunction();
// debugging/stat helper, returns python filename:linenumber, or "unknown:-1" if it fails
......
......@@ -777,13 +777,8 @@ 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), reraise(false) {}
#endif
constexpr ExcInfo(Box* type, Box* value, Box* traceback) : type(type), value(value), traceback(traceback) {}
bool matches(BoxedClass* cls) const;
void printExcAndTraceback() const;
};
......
......@@ -688,7 +688,7 @@ extern "C" void __cxa_throw(void* exc_obj, std::type_info* tinfo, void (*dtor)(v
pyston::StatTimer::overrideCounter(unwinding_stattimer);
#endif
// let unwinding.cpp know we've started unwinding
pyston::throwingException(pyston::getActivePythonUnwindSession());
pyston::logException(exc_data);
pyston::unwind(exc_data);
}
......
......@@ -76,17 +76,11 @@ extern "C" void raise0() {
if (exc_info->type == None)
raiseExcHelper(TypeError, "exceptions must be old-style classes or derived from BaseException, not NoneType");
exc_info->reraise = true;
startReraise();
assert(!PyErr_Occurred());
throw * exc_info;
}
#ifndef NDEBUG
ExcInfo::ExcInfo(Box* type, Box* value, Box* traceback)
: type(type), value(value), traceback(traceback), reraise(false) {
}
#endif
void ExcInfo::printExcAndTraceback() const {
PyErr_Display(type, value, traceback);
}
......@@ -158,7 +152,9 @@ extern "C" void raise3(Box* arg0, Box* arg1, Box* arg2) {
bool reraise = arg2 != NULL && arg2 != None;
auto exc_info = excInfoForRaise(arg0, arg1, arg2);
exc_info.reraise = reraise;
if (reraise)
startReraise();
assert(!PyErr_Occurred());
throw exc_info;
}
......@@ -169,12 +165,11 @@ extern "C" void raise3_capi(Box* arg0, Box* arg1, Box* arg2) noexcept {
ExcInfo exc_info(NULL, NULL, NULL);
try {
exc_info = excInfoForRaise(arg0, arg1, arg2);
exc_info.reraise = reraise;
} catch (ExcInfo e) {
exc_info = e;
}
assert(!exc_info.reraise); // would get thrown away
assert(!reraise); // would get thrown away
PyErr_Restore(exc_info.type, exc_info.value, exc_info.traceback);
}
......@@ -206,4 +201,71 @@ void raiseExcHelper(BoxedClass* cls, const char* msg, ...) {
raiseExc(exc_obj);
}
}
void logException(ExcInfo* exc_info) {
#if STAT_EXCEPTIONS
static StatCounter num_exceptions("num_exceptions");
num_exceptions.log();
std::string stat_name;
if (PyType_Check(exc_info->type))
stat_name = "num_exceptions_" + std::string(static_cast<BoxedClass*>(exc_info->type)->tp_name);
else
stat_name = "num_exceptions_" + std::string(exc_info->value->cls->tp_name);
Stats::log(Stats::getStatCounter(stat_name));
#if STAT_EXCEPTIONS_LOCATION
logByCurrentPythonLine(stat_name);
#endif
#endif
}
extern "C" void capiExcCaughtInJit(AST_stmt* stmt, void* _source_info) {
SourceInfo* source = static_cast<SourceInfo*>(_source_info);
PyThreadState* tstate = PyThreadState_GET();
exceptionAtLine(LineInfo(stmt->lineno, stmt->col_offset, source->fn, source->getName()), &tstate->curexc_traceback);
}
extern "C" void reraiseJitCapiExc() {
ensureCAPIExceptionSet();
// TODO: we are normalizing to many times?
ExcInfo e = excInfoForRaise(cur_thread_state.curexc_type, cur_thread_state.curexc_value,
cur_thread_state.curexc_traceback);
PyErr_Clear();
startReraise();
throw e;
}
void exceptionCaughtInInterpreter(LineInfo line_info, ExcInfo* exc_info) {
static StatCounter frames_unwound("num_frames_unwound_python");
frames_unwound.log();
exceptionAtLine(line_info, &exc_info->traceback);
}
struct ExcState {
bool is_reraise;
constexpr ExcState() : is_reraise(false) {}
} static __thread exc_state;
bool exceptionAtLineCheck() {
if (exc_state.is_reraise) {
exc_state.is_reraise = false;
return false;
}
return true;
}
void exceptionAtLine(LineInfo line_info, Box** traceback) {
if (exceptionAtLineCheck())
BoxedTraceback::here(line_info, traceback);
}
void startReraise() {
assert(!exc_state.is_reraise);
exc_state.is_reraise = true;
}
}
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