Commit 39ac322d authored by Kevin Modzelewski's avatar Kevin Modzelewski

Basic traceback support

If you throw an unhandled exception, you'll see a traceback
from the top-level handler.  Doesn't enable us to run any additional
programs, since this functionality isn't available to Python-level code,
but hopefully should make debugging easier.
parent 38d32bb9
......@@ -21,6 +21,7 @@
#include "core/types.h"
namespace llvm {
class DILineInfo;
class ExecutionEngine;
class JITEventListener;
class LLVMContext;
......@@ -82,6 +83,9 @@ extern GlobalState g;
// in runtime_hooks.cpp:
void initGlobalFuncs(GlobalState& g);
const llvm::DILineInfo* getLineInfoFor(uint64_t addr);
}
#endif
......@@ -105,7 +105,7 @@ PystonJITEventListener::PystonJITEventListener() {
asm_printer->Mang = new llvm::Mangler(tmachine->getDataLayout());
DisAsm = target->createMCDisassembler(*STI);
DisAsm = target->createMCDisassembler(*STI, *Ctx);
assert(DisAsm);
MIA = target->createMCInstrAnalysis(MII);
assert(MIA);
......@@ -121,7 +121,7 @@ void PystonJITEventListener::NotifyFunctionEmitted(const llvm::Function& f, void
asm_printer->MF = &MF;
for (llvm::MachineFunction::const_iterator it = MF.begin(); it != MF.end(); it++) {
// it->dump();
asm_printer->EmitBasicBlockStart(it);
asm_printer->EmitBasicBlockStart(*it);
for (llvm::MachineBasicBlock::const_instr_iterator it2 = it->instr_begin(); it2 != it->instr_end(); it2++) {
// llvm::errs() << "dump:";
// it2->print(llvm::errs());
......
......@@ -839,7 +839,7 @@ static llvm::MDNode* setupDebugInfo(SourceInfo* source, llvm::Function* f, std::
llvm::DIBuilder builder(*g.cur_module);
std::string fn = source->parent_module->fn;
std::string dir = "TODO fill this in";
std::string dir = "";
std::string producer = "pyston; git rev " STRINGIFY(GITREV);
llvm::DIFile file = builder.createFile(fn, dir);
......
......@@ -946,7 +946,10 @@ private:
}
CompilerVariable* evalExpr(AST_expr* node, ExcInfo exc_info) {
emitter.getBuilder()->SetCurrentDebugLocation(llvm::DebugLoc::get(node->lineno, 0, irstate->getFuncDbgInfo()));
//printf("%d expr: %d\n", node->type, node->lineno);
if (node->lineno) {
emitter.getBuilder()->SetCurrentDebugLocation(llvm::DebugLoc::get(node->lineno, 0, irstate->getFuncDbgInfo()));
}
CompilerVariable* rtn = NULL;
if (state != PARTIAL) {
......@@ -1759,6 +1762,11 @@ private:
}
void doStmt(AST* node, ExcInfo exc_info) {
//printf("%d stmt: %d\n", node->type, node->lineno);
if (node->lineno) {
emitter.getBuilder()->SetCurrentDebugLocation(llvm::DebugLoc::get(node->lineno, 0, irstate->getFuncDbgInfo()));
}
switch (node->type) {
case AST_TYPE::Assert:
doAssert(ast_cast<AST_Assert>(node), exc_info);
......
......@@ -71,6 +71,44 @@ void parseEhFrame(uint64_t start_addr, uint64_t size, uint64_t* out_data, uint64
*out_len = nentries;
}
class LineTableRegistry {
private:
struct LineTableRegistryEntry {
const uint64_t addr, size;
const llvm::DILineInfoTable linetable;
LineTableRegistryEntry(uint64_t addr, uint64_t size, llvm::DILineInfoTable linetable) :
addr(addr), size(size), linetable(linetable) {
}
};
std::vector<LineTableRegistryEntry> entries;
public:
void registerLineTable(uint64_t addr, uint64_t size, llvm::DILineInfoTable linetable) {
entries.push_back(LineTableRegistryEntry(addr, size, linetable));
}
const llvm::DILineInfo* getLineInfoFor(uint64_t addr) {
for (const auto& entry : entries) {
if (addr < entry.addr || addr >= entry.addr + entry.size)
continue;
const auto& linetable = entry.linetable;
for (int i = linetable.size() - 1; i >= 0; i--) {
if (linetable[i].first < addr)
return &linetable[i].second;
}
abort();
}
return NULL;
}
};
static LineTableRegistry line_table_registry;
const llvm::DILineInfo* getLineInfoFor(uint64_t addr) {
return line_table_registry.getLineInfoFor(addr);
}
class TracebacksEventListener : public llvm::JITEventListener {
public:
void NotifyObjectEmitted(const llvm::ObjectImage& Obj) {
......@@ -100,12 +138,16 @@ public:
Addr, Size, llvm::DILineInfoSpecifier(llvm::DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
llvm::DILineInfoSpecifier::FunctionNameKind::LinkageName));
#endif
for (int i = 0; i < lines.size(); i++) {
// printf("%s:%d, %s: %lx\n", lines[i].second.getFileName(), lines[i].second.getLine(),
// lines[i].second.getFunctionName(), lines[i].first);
if (VERBOSITY() >= 2) {
for (int i = 0; i < lines.size(); i++) {
printf("%s:%d, %s: %lx\n", lines[i].second.FileName.c_str(), lines[i].second.Line,
lines[i].second.FunctionName.c_str(), lines[i].first);
}
}
line_table_registry.registerLineTable(Addr, Size, lines);
}
}
delete Context;
// Currently-unused libunwind support:
llvm::error_code code;
......
......@@ -257,8 +257,6 @@ private:
AST_expr* makeNum(int n) {
AST_Num* node = new AST_Num();
node->col_offset = -1;
node->lineno = -1;
node->num_type = AST_Num::INT;
node->n_int = n;
return node;
......@@ -329,7 +327,7 @@ private:
return call;
}
AST_Name* makeName(const std::string& id, AST_TYPE::AST_TYPE ctx_type, int lineno = -1, int col_offset = -1) {
AST_Name* makeName(const std::string& id, AST_TYPE::AST_TYPE ctx_type, int lineno = 0, int col_offset = 0) {
AST_Name* name = new AST_Name();
name->id = id;
name->col_offset = col_offset;
......@@ -996,6 +994,7 @@ public:
int i = 0;
for (auto v : node->values) {
AST_Print* remapped = new AST_Print();
remapped->lineno = node->lineno;
// TODO not good to reuse 'dest' like this
remapped->dest = dest;
......@@ -1572,6 +1571,7 @@ CFG* computeCFG(AST_TYPE::AST_TYPE root_type, std::vector<AST_stmt*> body) {
// we already have to support multiple return statements in a function, but this way we can avoid
// having to support not having a return statement:
AST_Return* return_stmt = new AST_Return();
return_stmt->lineno = return_stmt->col_offset = 0;
return_stmt->value = NULL;
visitor.push_back(return_stmt);
......
......@@ -391,6 +391,7 @@ void addToSysPath(const std::string& path);
void addToSysArgv(const char* str);
std::string formatException(Box* e);
void printTraceback();
}
#endif
......@@ -146,6 +146,7 @@ int main(int argc, char** argv) {
}
catch (Box* b) {
std::string msg = formatException(b);
printTraceback();
fprintf(stderr, "%s\n", msg.c_str());
exit(1);
}
......@@ -230,6 +231,7 @@ int main(int argc, char** argv) {
}
catch (Box* b) {
std::string msg = formatException(b);
printTraceback();
fprintf(stderr, "%s\n", msg.c_str());
}
}
......
......@@ -12,6 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <cstdarg>
#include "llvm/DebugInfo/DIContext.h"
#include "codegen/codegen.h"
#include "core/options.h"
#include "runtime/objmodel.h"
#include "runtime/types.h"
#include "runtime/util.h"
#define UNW_LOCAL_ONLY
#include <libunwind.h>
......@@ -22,14 +33,6 @@
#endif
#include "runtime/objmodel.h"
#include "runtime/types.h"
#include "runtime/util.h"
#include "core/options.h"
#include <stdarg.h>
namespace pyston {
// from http://www.nongnu.org/libunwind/man/libunwind(3).html
......@@ -112,7 +115,72 @@ void raiseExc(Box* exc_obj) {
abort();
}
static std::vector<const llvm::DILineInfo*> last_tb;
void printTraceback() {
fprintf(stderr, "Traceback (most recent call last):\n");
for (auto line : last_tb) {
fprintf(stderr, " File \"%s\", line %d, in %s:\n", line->FileName.c_str(), line->Line, line->FunctionName.c_str());
FILE* f = fopen(line->FileName.c_str(), "r");
if (f) {
for (int i = 1; i < line->Line; i++) {
char* buf = NULL;
size_t size;
size_t r = getline(&buf, &size, f);
if (r != -1)
free(buf);
}
char* buf = NULL;
size_t size;
size_t r = getline(&buf, &size, f);
if (r != -1) {
while (buf[r-1] == '\n' or buf[r-1] == '\r')
r--;
char* ptr = buf;
while (*ptr == ' ' || *ptr == '\t') {
ptr++;
r--;
}
fprintf(stderr, " %.*s\n", (int)r, ptr);
free(buf);
}
}
}
}
static std::vector<const llvm::DILineInfo*> getTracebackEntries() {
std::vector<const llvm::DILineInfo*> entries;
unw_cursor_t cursor;
unw_context_t uc;
unw_word_t ip, sp;
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
int code;
unw_proc_info_t pip;
while (unw_step(&cursor) > 0) {
unw_get_reg(&cursor, UNW_REG_IP, &ip);
const llvm::DILineInfo* line = getLineInfoFor((uint64_t)ip);
if (line) {
entries.push_back(line);
}
}
std::reverse(entries.begin(), entries.end());
return entries;
}
void raiseExcHelper(BoxedClass* cls, const char* msg, ...) {
auto entries = getTracebackEntries();
last_tb = std::move(entries);
if (msg != NULL) {
va_list ap;
va_start(ap, msg);
......
......@@ -205,7 +205,8 @@ extern "C" void conservativeGCHandler(GCVisitor* v, void* p) {
extern "C" {
BoxedClass* object_cls, *type_cls, *none_cls, *bool_cls, *int_cls, *float_cls, *str_cls, *function_cls,
*instancemethod_cls, *list_cls, *slice_cls, *module_cls, *dict_cls, *tuple_cls, *file_cls, *member_cls;
*instancemethod_cls, *list_cls, *slice_cls, *module_cls, *dict_cls, *tuple_cls, *file_cls, *member_cls,
*traceback_cls;
const ObjectFlavor object_flavor(&boxGCHandler, NULL);
const ObjectFlavor type_flavor(&typeGCHandler, NULL);
......@@ -223,6 +224,7 @@ const ObjectFlavor dict_flavor(&dictGCHandler, NULL);
const ObjectFlavor tuple_flavor(&tupleGCHandler, NULL);
const ObjectFlavor file_flavor(&boxGCHandler, NULL);
const ObjectFlavor member_flavor(&boxGCHandler, NULL);
const ObjectFlavor traceback_flavor(&boxGCHandler, NULL);
const AllocationKind untracked_kind(NULL, NULL);
const AllocationKind hc_kind(&hcGCHandler, NULL);
......@@ -444,6 +446,7 @@ void setupRuntime() {
file_cls = new BoxedClass(object_cls, 0, sizeof(BoxedFile), false);
set_cls = new BoxedClass(object_cls, 0, sizeof(BoxedSet), false);
member_cls = new BoxedClass(object_cls, 0, sizeof(BoxedMemberDescriptor), false);
traceback_cls = new BoxedClass(object_cls, 0, sizeof(BoxedTraceback), false);
STR = typeFromClass(str_cls);
BOXED_INT = typeFromClass(int_cls);
......
......@@ -17,6 +17,10 @@
#include "core/types.h"
namespace llvm {
class DILineInfo;
}
namespace pyston {
extern bool IN_SHUTDOWN;
......@@ -60,12 +64,13 @@ BoxedList* getSysPath();
extern "C" {
extern BoxedClass* object_cls, *type_cls, *bool_cls, *int_cls, *float_cls, *str_cls, *function_cls, *none_cls,
*instancemethod_cls, *list_cls, *slice_cls, *module_cls, *dict_cls, *tuple_cls, *file_cls, *xrange_cls, *member_cls;
*instancemethod_cls, *list_cls, *slice_cls, *module_cls, *dict_cls, *tuple_cls, *file_cls, *xrange_cls, *member_cls,
*traceback_cls;
}
extern "C" {
extern const ObjectFlavor object_flavor, type_flavor, bool_flavor, int_flavor, float_flavor, str_flavor,
function_flavor, none_flavor, instancemethod_flavor, list_flavor, slice_flavor, module_flavor, dict_flavor,
tuple_flavor, file_flavor, xrange_flavor, member_flavor;
tuple_flavor, file_flavor, xrange_flavor, member_flavor, traceback_flavor;
}
extern "C" { extern Box* None, *NotImplemented, *True, *False; }
extern "C" {
......@@ -293,6 +298,15 @@ public:
BoxedMemberDescriptor(MemberType type, int offset) : Box(&member_flavor, member_cls), type(type), offset(offset) {}
};
class BoxedTraceback : public Box {
public:
const std::vector<const llvm::DILineInfo*> entries;
BoxedTraceback(const std::vector<const llvm::DILineInfo*> &&entries) : Box(&traceback_flavor, traceback_cls),
entries(std::move(entries)) {
}
};
extern "C" void boxGCHandler(GCVisitor* v, void* p);
Box* exceptionNew1(BoxedClass* cls);
......
......@@ -30,10 +30,32 @@ def f2():
self.foo(n)
self.foo(n)
def foo(self, n):
print "B.foo()", n
class C(B):
def foo(self, n):
print n
print "C.foo()", n
c = C()
print c.wrapper(2)
print B().wrapper(2)
print C().wrapper(2)
f2()
def f3():
class I(object):
def foo(self):
print self + 1
a = I(1)
b = I(2)
print a, type(a)
print b, type(b)
c = a + b
print c, type(c)
d = c + a
e = a + c
print d, type(d)
print e, type(e)
print a.foo()
f3()
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