Commit 9067a589 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #564 from toshok/incremental-traceback

Incremental traceback
parents cf145fb8 d034a63e
......@@ -69,13 +69,10 @@ private:
ASTInterpreter* interpreter;
public:
RegisterHelper(ASTInterpreter* interpreter, void* frame_addr);
RegisterHelper();
~RegisterHelper();
static void deregister(void* frame_addr) {
assert(s_interpreterMap.count(frame_addr));
s_interpreterMap.erase(frame_addr);
}
void doRegister(void* frame_addr, ASTInterpreter* interpreter);
static void deregister(void* frame_addr);
};
union Value {
......@@ -102,11 +99,13 @@ public:
void initArguments(int nargs, BoxedClosure* closure, BoxedGenerator* generator, Box* arg1, Box* arg2, Box* arg3,
Box** args);
static Value execute(ASTInterpreter& interpreter, CFGBlock* start_block = NULL, AST_stmt* start_at = NULL);
// This must not be inlined, because we rely on being able to detect when we're inside of it (by checking whether
// %rip is inside its instruction range) during a stack-trace in order to produce tracebacks inside interpreted
// code.
__attribute__((__no_inline__)) static Value
execute(ASTInterpreter& interpreter, CFGBlock* start_block = NULL, AST_stmt* start_at = NULL);
executeInner(ASTInterpreter& interpreter, CFGBlock* start_block, AST_stmt* start_at, RegisterHelper* reg);
private:
Box* createFunction(AST* node, AST_arguments* args, const std::vector<AST_stmt*>& body);
......@@ -318,22 +317,35 @@ void ASTInterpreter::initArguments(int nargs, BoxedClosure* _closure, BoxedGener
}
}
RegisterHelper::RegisterHelper(ASTInterpreter* interpreter, void* frame_addr)
: frame_addr(frame_addr), interpreter(interpreter) {
interpreter->frame_addr = frame_addr;
s_interpreterMap[frame_addr] = interpreter;
RegisterHelper::RegisterHelper() : frame_addr(NULL), interpreter(NULL) {
}
RegisterHelper::~RegisterHelper() {
assert(interpreter);
assert(interpreter->frame_addr == frame_addr);
interpreter->frame_addr = nullptr;
deregister(frame_addr);
}
Value ASTInterpreter::execute(ASTInterpreter& interpreter, CFGBlock* start_block, AST_stmt* start_at) {
STAT_TIMER(t0, "us_timer_astinterpreter_execute");
void RegisterHelper::doRegister(void* frame_addr, ASTInterpreter* interpreter) {
assert(!this->interpreter);
assert(!this->frame_addr);
this->frame_addr = frame_addr;
this->interpreter = interpreter;
interpreter->frame_addr = frame_addr;
s_interpreterMap[frame_addr] = interpreter;
}
void RegisterHelper::deregister(void* frame_addr) {
assert(frame_addr);
assert(s_interpreterMap.count(frame_addr));
s_interpreterMap.erase(frame_addr);
}
Value ASTInterpreter::executeInner(ASTInterpreter& interpreter, CFGBlock* start_block, AST_stmt* start_at,
RegisterHelper* reg) {
void* frame_addr = __builtin_frame_address(0);
RegisterHelper frame_registerer(&interpreter, frame_addr);
reg->doRegister(frame_addr, &interpreter);
Value v;
......@@ -373,6 +385,14 @@ Value ASTInterpreter::execute(ASTInterpreter& interpreter, CFGBlock* start_block
return v;
}
Value ASTInterpreter::execute(ASTInterpreter& interpreter, CFGBlock* start_block, AST_stmt* start_at) {
STAT_TIMER(t0, "us_timer_astinterpreter_execute");
RegisterHelper frame_registerer;
return executeInner(interpreter, start_block, start_at, &frame_registerer);
}
Value ASTInterpreter::doBinOp(Box* left, Box* right, int op, BinExpType exp_type) {
switch (exp_type) {
case BinExpType::AugBinOp:
......@@ -610,6 +630,10 @@ Value ASTInterpreter::visit_invoke(AST_Invoke* node) {
v = visit_stmt(node->stmt);
next_block = node->normal_dest;
} catch (ExcInfo e) {
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;
}
......@@ -1269,7 +1293,7 @@ Value ASTInterpreter::visit_attribute(AST_Attribute* node) {
}
}
const void* interpreter_instr_addr = (void*)&ASTInterpreter::execute;
const void* interpreter_instr_addr = (void*)&ASTInterpreter::executeInner;
Box* astInterpretFunction(CompiledFunction* cf, int nargs, Box* closure, Box* generator, Box* globals, Box* arg1,
Box* arg2, Box* arg3, Box** args) {
......
......@@ -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)
......
This diff is collapsed.
......@@ -19,6 +19,10 @@
#include "codegen/codegen.h"
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#undef UNW_LOCAL_ONLY
namespace pyston {
class Box;
......@@ -29,6 +33,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,6 +41,16 @@ CompiledFunction* getCFForAddress(uint64_t addr);
BoxedTraceback* 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);
void exceptionCaughtInInterpreter(LineInfo line_info, ExcInfo* exc_info);
struct ExecutionPoint {
CompiledFunction* cf;
AST_stmt* current_stmt;
......
......@@ -88,6 +88,10 @@ public:
GCVisitor(TraceStack* stack) : stack(stack) {}
// 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 visitRange(void* const* start, void* const* end);
void visitPotential(void* p);
......@@ -666,7 +670,7 @@ void raiseSyntaxErrorHelper(llvm::StringRef file, llvm::StringRef func, AST* nod
struct LineInfo {
public:
const int line, column;
int line, column;
std::string file, func;
LineInfo(int line, int column, llvm::StringRef file, llvm::StringRef func)
......@@ -675,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;
......
......@@ -926,7 +926,7 @@ void checkAndThrowCAPIException() {
RELEASE_ASSERT(value->cls == type, "unsupported");
if (tb != None)
raiseRaw(ExcInfo(value->cls, value, tb));
throw ExcInfo(value->cls, value, tb);
raiseExc(value);
}
}
......
......@@ -162,7 +162,7 @@ static Box* classobjGetattribute(Box* _cls, Box* _attr) {
}
}
Box* r = classLookup(cls, std::string(attr->s()));
Box* r = classLookup(cls, attr->s());
if (!r)
raiseExcHelper(AttributeError, "class %s has no attribute '%s'", cls->name->data(), attr->data());
......
This diff is collapsed.
......@@ -155,7 +155,7 @@ static void generatorSendInternal(BoxedGenerator* self, Box* v) {
freeGeneratorStack(self);
// don't raise StopIteration exceptions because those are handled specially.
if (!self->exception.matches(StopIteration))
raiseRaw(self->exception);
throw self->exception;
return;
}
......@@ -193,7 +193,7 @@ Box* generatorSend(Box* s, Box* v) {
self->exception = ExcInfo(nullptr, nullptr, nullptr);
if (old_exc.type == NULL)
raiseExcHelper(StopIteration, (const char*)nullptr);
raiseRaw(old_exc);
throw old_exc;
}
return self->returnValue;
......@@ -272,7 +272,7 @@ extern "C" Box* yield(BoxedGenerator* obj, Box* value) {
if (self->exception.type) {
ExcInfo e = self->exception;
self->exception = ExcInfo(NULL, NULL, NULL);
raiseRaw(e);
throw e;
}
return self->returnValue;
}
......
......@@ -49,7 +49,7 @@ Box* createAndRunModule(const std::string& name, const std::string& fn) {
compileAndRunModule(ast, module);
} catch (ExcInfo e) {
removeModule(name);
raiseRaw(e);
throw e;
}
Box* r = getSysModulesDict()->getOrNull(boxString(name));
......@@ -74,7 +74,7 @@ static Box* createAndRunModule(const std::string& name, const std::string& fn, c
compileAndRunModule(ast, module);
} catch (ExcInfo e) {
removeModule(name);
raiseRaw(e);
throw e;
}
Box* r = getSysModulesDict()->getOrNull(boxString(name));
......@@ -223,7 +223,7 @@ SearchResult findModule(const std::string& name, const std::string& full_name, B
BoxedString* p = static_cast<BoxedString*>(_p);
joined_path.clear();
llvm::sys::path::append(joined_path, std::string(p->s()), name);
llvm::sys::path::append(joined_path, p->s(), name);
std::string dn(joined_path.str());
llvm::sys::path::append(joined_path, "__init__.py");
......@@ -406,7 +406,7 @@ static Box* importSub(const std::string& name, const std::string& full_name, Box
RELEASE_ASSERT(0, "%d", sr.type);
} catch (ExcInfo e) {
removeModule(name);
raiseRaw(e);
throw e;
}
if (parent_module && parent_module != None)
......@@ -560,7 +560,7 @@ static void ensureFromlist(Box* module, Box* fromlist, std::string& buf, bool re
pathlist = getattrInternal(module, path_str, NULL);
} catch (ExcInfo e) {
if (!e.matches(AttributeError))
raiseRaw(e);
throw e;
}
if (pathlist == NULL) {
......
......@@ -727,7 +727,7 @@ void listSort(BoxedList* self, Box* cmp, Box* key, Box* reverse) {
std::stable_sort<Box**, PyLt>(self->elts->elts, self->elts->elts + self->size, PyLt());
} catch (ExcInfo e) {
remove_keys();
raiseRaw(e);
throw e;
}
remove_keys();
......
......@@ -36,7 +36,6 @@ ExcInfo excInfoForRaise(Box*, Box*, Box*);
extern "C" void raise0() __attribute__((__noreturn__));
extern "C" void raise3(Box*, Box*, Box*) __attribute__((__noreturn__));
void raiseExc(Box* exc_obj) __attribute__((__noreturn__));
void raiseRaw(const ExcInfo& e) __attribute__((__noreturn__));
void _printStacktrace();
extern "C" Box* deopt(AST_expr* expr, Box* value);
......
......@@ -46,37 +46,8 @@ void showBacktrace() {
}
}
void raiseRaw(const ExcInfo& e) __attribute__((__noreturn__));
void raiseRaw(const ExcInfo& e) {
STAT_TIMER(t0, "us_timer_raiseraw");
// Should set these to None rather than null before getting here:
assert(e.type);
assert(e.value);
assert(e.traceback);
assert(gc::isValidGCObject(e.type));
assert(gc::isValidGCObject(e.value));
assert(gc::isValidGCObject(e.traceback));
#if STAT_EXCEPTIONS
static StatCounter num_exceptions("num_exceptions");
num_exceptions.log();
std::string stat_name;
if (PyType_Check(e.type))
stat_name = "num_exceptions_" + std::string(static_cast<BoxedClass*>(e.type)->tp_name);
else
stat_name = "num_exceptions_" + std::string(e.value->cls->tp_name);
Stats::log(Stats::getStatCounter(stat_name));
#if STAT_EXCEPTIONS_LOCATION
logByCurrentPythonLine(stat_name);
#endif
#endif
throw e;
}
void raiseExc(Box* exc_obj) {
raiseRaw(ExcInfo(exc_obj->cls, exc_obj, getTraceback()));
throw ExcInfo(exc_obj->cls, exc_obj, new BoxedTraceback());
}
// Have a special helper function for syntax errors, since we want to include the location
......@@ -84,10 +55,8 @@ void raiseExc(Box* exc_obj) {
void raiseSyntaxError(const char* msg, int lineno, int col_offset, llvm::StringRef file, llvm::StringRef func) {
Box* exc = runtimeCall(SyntaxError, ArgPassSpec(1), boxString(msg), NULL, NULL, NULL, NULL);
auto tb = getTraceback();
std::vector<const LineInfo*> entries = tb->lines;
entries.push_back(new LineInfo(lineno, col_offset, file, func));
raiseRaw(ExcInfo(exc->cls, exc, new BoxedTraceback(std::move(entries))));
auto tb = new BoxedTraceback(LineInfo(lineno, col_offset, file, func), None);
throw ExcInfo(exc->cls, exc, tb);
}
void raiseSyntaxErrorHelper(llvm::StringRef file, llvm::StringRef func, AST* node_at, const char* msg, ...) {
......@@ -205,11 +174,13 @@ extern "C" void raise0() {
if (exc_info->type == None)
raiseExcHelper(TypeError, "exceptions must be old-style classes or derived from BaseException, not NoneType");
raiseRaw(*exc_info);
exc_info->reraise = true;
throw * 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
......@@ -229,8 +200,12 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) {
assert(type && value && tb); // use None for default behavior, not nullptr
// TODO switch this to PyErr_Normalize
if (tb == None)
tb = getTraceback();
if (tb == None) {
tb = NULL;
} else if (tb != NULL && !PyTraceBack_Check(tb)) {
raiseExcHelper(TypeError, "raise: arg 3 must be a traceback or None");
}
/* Next, repeatedly, replace a tuple exception with its first item */
while (PyTuple_Check(type) && PyTuple_Size(type) > 0) {
......@@ -242,6 +217,7 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) {
if (PyExceptionClass_Check(type)) {
PyErr_NormalizeException(&type, &value, &tb);
if (!PyExceptionInstance_Check(value)) {
raiseExcHelper(TypeError, "calling %s() should have returned an instance of "
"BaseException, not '%s'",
......@@ -268,11 +244,19 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) {
assert(PyExceptionClass_Check(type));
if (tb == NULL) {
tb = new BoxedTraceback();
}
return ExcInfo(type, value, tb);
}
extern "C" void raise3(Box* arg0, Box* arg1, Box* arg2) {
raiseRaw(excInfoForRaise(arg0, arg1, arg2));
bool reraise = arg2 != NULL && arg2 != None;
auto exc_info = excInfoForRaise(arg0, arg1, arg2);
exc_info.reraise = reraise;
throw exc_info;
}
void raiseExcHelper(BoxedClass* cls, Box* arg) {
......
......@@ -128,7 +128,7 @@ Box* superGetattribute(Box* _s, Box* _attr) {
}
}
Box* r = typeLookup(s->cls, std::string(attr->s()), NULL);
Box* r = typeLookup(s->cls, attr->s(), NULL);
// TODO implement this
RELEASE_ASSERT(r, "should call the equivalent of objectGetattr here");
return processDescriptor(r, s, s->cls);
......
......@@ -24,6 +24,7 @@
#include "core/stats.h"
#include "core/types.h"
#include "gc/collector.h"
#include "runtime/list.h"
#include "runtime/objmodel.h"
#include "runtime/types.h"
#include "runtime/util.h"
......@@ -40,6 +41,8 @@ void BoxedTraceback::gcHandler(GCVisitor* v, Box* b) {
if (self->py_lines)
v->visit(self->py_lines);
if (self->tb_next)
v->visit(self->tb_next);
boxGCHandler(v, b);
}
......@@ -53,16 +56,17 @@ void printTraceback(Box* b) {
fprintf(stderr, "Traceback (most recent call last):\n");
for (auto line : tb->lines) {
fprintf(stderr, " File \"%s\", line %d, in %s:\n", line->file.c_str(), line->line, line->func.c_str());
for (; tb && tb != None; tb = static_cast<BoxedTraceback*>(tb->tb_next)) {
auto& line = tb->line;
fprintf(stderr, " File \"%s\", line %d, in %s:\n", line.file.c_str(), line.line, line.func.c_str());
if (line->line < 0)
if (line.line < 0)
continue;
FILE* f = fopen(line->file.c_str(), "r");
FILE* f = fopen(line.file.c_str(), "r");
if (f) {
assert(line->line < 10000000 && "Refusing to try to seek that many lines forward");
for (int i = 1; i < line->line; i++) {
assert(line.line < 10000000 && "Refusing to try to seek that many lines forward");
for (int i = 1; i < line.line; i++) {
char* buf = NULL;
size_t size;
size_t r = getline(&buf, &size, f);
......@@ -97,10 +101,12 @@ Box* BoxedTraceback::getLines(Box* b) {
if (!tb->py_lines) {
BoxedList* lines = new BoxedList();
lines->ensure(tb->lines.size());
for (auto line : tb->lines) {
auto l = BoxedTuple::create({ boxString(line->file), boxString(line->func), boxInt(line->line) });
listAppendInternal(lines, l);
for (BoxedTraceback* wtb = tb; wtb && wtb != None; wtb = static_cast<BoxedTraceback*>(wtb->tb_next)) {
if (wtb->has_line) {
auto& line = wtb->line;
auto l = BoxedTuple::create({ boxString(line.file), boxString(line.func), boxInt(line.line) });
listAppendInternal(lines, l);
}
}
tb->py_lines = lines;
}
......@@ -108,6 +114,10 @@ Box* BoxedTraceback::getLines(Box* b) {
return tb->py_lines;
}
void BoxedTraceback::here(LineInfo lineInfo, Box** tb) {
*tb = new BoxedTraceback(lineInfo, *tb);
}
void setupTraceback() {
traceback_cls = BoxedHeapClass::create(type_cls, object_cls, BoxedTraceback::gcHandler, 0, 0,
sizeof(BoxedTraceback), false, "traceback");
......
......@@ -27,19 +27,22 @@ class GCVisitor;
extern "C" BoxedClass* traceback_cls;
class BoxedTraceback : public Box {
public:
std::vector<const LineInfo*> lines;
Box* tb_next;
bool has_line;
LineInfo line;
Box* py_lines;
BoxedTraceback(std::vector<const LineInfo*> lines) : lines(std::move(lines)), py_lines(NULL) {}
BoxedTraceback() : py_lines(NULL) {}
BoxedTraceback(LineInfo line, Box* tb_next) : tb_next(tb_next), has_line(true), line(line), py_lines(NULL) {}
BoxedTraceback() : tb_next(None), has_line(false), line(-1, -1, "", ""), py_lines(NULL) {}
DEFAULT_CLASS(traceback_cls);
void addLine(const LineInfo* line);
static Box* getLines(Box* b);
static void gcHandler(gc::GCVisitor* v, Box* b);
// somewhat equivalent to PyTraceBack_Here
static void here(LineInfo lineInfo, Box** tb);
};
void printTraceback(Box* b);
......
......@@ -2703,6 +2703,7 @@ void setupRuntime() {
closure_cls->freeze();
setupUnwinding();
setupInterpreter();
setupCAPI();
......
import traceback
import sys
def f():
a, b, c = sys.exc_info()
raise a, b, c
et0, ev0, tb0 = None, None, None
try:
1/0
except:
pass
for i in xrange(10):
try:
f()
except:
et0, ev0, tb0 = sys.exc_info()
print "******** 0", ''.join(traceback.format_exception(et0, ev0, tb0))
et1, ev1, tb1 = None, None, None
et2, ev2, tb2 = None, None, None
def f1():
raise
def f2():
f1()
def f21():
raise Exception()
def f3():
try:
f21()
except:
global et1, tv1, tb1
et1, tv1, tb1 = sys.exc_info()
f2()
try:
f3()
except:
et2, tv2, tb2 = sys.exc_info()
print "******** 1", ''.join(traceback.format_exception(et1, ev1, tb1))
print "******** 2", ''.join(traceback.format_exception(et2, ev2, tb2))
print et1 is et2
print ev1 is ev2
print tb1 is tb2
# expected: fail
# - we don't stop tracebacks at the catching except handler. this is hard do the way it gets added to
# (ie a bare "raise" statement will add more traceback entries to the traceback it raises)
import sys
import traceback
......
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