Skip to content

Commit

Permalink
print frames more nicely
Browse files Browse the repository at this point in the history
  • Loading branch information
rntz committed Apr 24, 2015
1 parent b441f83 commit 1dc501b
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 21 deletions.
1 change: 1 addition & 0 deletions src/codegen/unwinding.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct FrameInfo;
BoxedModule* getCurrentModule();
Box* getGlobals(); // returns either the module or a globals dict
Box* getGlobalsDict(); // always returns a dict-like object
CompiledFunction* getCFForAddress(uint64_t addr);

BoxedTraceback* getTraceback();

Expand Down
114 changes: 93 additions & 21 deletions src/runtime/cxx_unwind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
#include <stdint.h>
#include <stddef.h>
#include <unwind.h>
#include <dlfcn.h> // dladdr

#include "llvm/Support/LEB128.h" // for {U,S}LEB128 decoding

#include "core/types.h" // for ExcInfo
#include "core/types.h" // for ExcInfo
#include "codegen/ast_interpreter.h" // interpreter_instr_addr
#include "runtime/generator.h" // generatorEntry
#include "codegen/unwinding.h" // getCFForAddress

#define UNW_LOCAL_ONLY
#include <libunwind.h>
Expand Down Expand Up @@ -280,7 +284,7 @@ bool find_call_site_entry(const lsda_info_t* info, const uint8_t *ip, call_site_
while (p < info->action_table) {
p = parse_call_site_entry(p, info, entry);

if (VERBOSITY("cxx_unwind") >= 2) {
if (VERBOSITY("cxx_unwind") >= 3) {
printf(" start %p end %p landingpad %p action %lx\n",
entry->instrs_start, entry->instrs_start + entry->instrs_len_bytes,
entry->landing_pad, entry->action_offset_plus_one);
Expand Down Expand Up @@ -341,8 +345,13 @@ void pyston_lsda_dispatch(unw_cursor_t* cursor, const lsda_info_t* info, const E
pyston_resume_cleanup(cursor, &entry, exc_info);
}

// Read a chain of actions. When we see a cleanup action, we *don't* immediately take it. Rather, we remember that
// we should clean up if none of the other actions matched.
// Read a chain of actions.
if (VERBOSITY("cxx_unwind") >= 3) {
printf(" reading action chain\n");
}

// When we see a cleanup action, we *don't* immediately take it. Rather, we remember that we should clean up if none
// of the other actions matched.
bool saw_cleanup = false;
uint64_t action_offset = entry.action_offset_plus_one - 1;
p = info->action_table + action_offset;
Expand All @@ -352,7 +361,7 @@ void pyston_lsda_dispatch(unw_cursor_t* cursor, const lsda_info_t* info, const E
p += leb_size;
// The next entry offset is relative to the position of the next entry offset field itself.
int64_t offset_to_next_entry = llvm::decodeSLEB128(p);
if (VERBOSITY("cxx_unwind") >= 2) {
if (VERBOSITY("cxx_unwind") >= 3) {
printf(" %ld: filter %ld next relative offset %ld\n", p - info->action_table - leb_size, type_filter,
offset_to_next_entry);
}
Expand Down Expand Up @@ -387,6 +396,77 @@ void pyston_lsda_dispatch(unw_cursor_t* cursor, const lsda_info_t* info, const E
RELEASE_ASSERT(0, "action chain exhausted and no cleanup indicated");
}

// FIXME: duplicated from unwinding.cpp
static
unw_word_t getFunctionEnd(unw_word_t ip) {
unw_proc_info_t pip;
// where is the documentation for unw_get_proc_info_by_ip, anyway?
int ret = unw_get_proc_info_by_ip(unw_local_addr_space, ip, &pip, NULL);
RELEASE_ASSERT(ret == 0 && pip.end_ip, "");
return pip.end_ip;
}

static
void print_frame(unw_cursor_t* cursor, const unw_proc_info_t* pip) {
// FIXME: code duplication with PythonFrameIter::incr
static unw_word_t interpreter_instr_end = getFunctionEnd((unw_word_t)interpreter_instr_addr);
static unw_word_t generator_entry_end = getFunctionEnd((unw_word_t)generatorEntry);

unw_word_t ip, bp;
check(unw_get_reg(cursor, UNW_REG_IP, &ip));
check(unw_get_reg(cursor, UNW_TDEP_BP, &bp));

{
Dl_info dl_info;
if (dladdr((void*)ip, &dl_info)) { // returns non-zero on success, wtf?
if (!dl_info.dli_sname || strlen(dl_info.dli_sname) < 50)
printf(" %-50s", dl_info.dli_sname ? dl_info.dli_sname : "<nil>");
else
printf(" %s\n", dl_info.dli_sname);
} else {
printf(" <unknown>\n");
}
}

CompiledFunction *cf = getCFForAddress(ip);
AST_stmt *cur_stmt = nullptr;
enum { COMPILED, INTERPRETED, GENERATOR, OTHER } frame_type;
if (cf) {
// compiled frame
frame_type = COMPILED;
printf(" ip %lx bp %lx jitted\n", ip, bp);
// TODO: get current statement
} else if ((unw_word_t) interpreter_instr_addr <= ip && ip < interpreter_instr_end) {
// interpreted frame
frame_type = INTERPRETED;
printf(" ip %lx bp %lx interpreted\n", ip, bp);
// sometimes this assert()s!
// cf = getCFForInterpretedFrame((void*)bp);
// cur_stmt = getCurrentStatementForInterpretedFrame((void*) bp);
} else if ((unw_word_t) generatorEntry <= ip && ip < generator_entry_end) {
// generator return frame
frame_type = GENERATOR;
printf(" ip %lx bp %lx generator\n", ip, bp);
} else {
// generic frame, probably C/C++
frame_type = OTHER;
printf(" ip %lx bp %lx\n", ip, bp);
}

if (frame_type == INTERPRETED && cf && cur_stmt) {
auto source = cf->clfunc->source;
// FIXME: dup'ed from lineInfoForFrame
const std::string* fn = &source->parent_module->fn;
if (source->ast->type == AST_TYPE::Suite /* exec */ || source->ast->type == AST_TYPE::Expression /* eval */) {
static const std::string string_str("<string>");
fn = &string_str;
}

LineInfo line(cur_stmt->lineno, cur_stmt->col_offset, *fn, source->getName());
printf(" File \"%s\", line %d, in %s\n", line.file.c_str(), line.line, line.func.c_str());
}
}


// The stack-unwinding loop.
// TODO: integrate incremental traceback generation into this function
Expand All @@ -403,29 +483,21 @@ void unwind_loop(const ExcInfo *exc_info) {

// TODO?: need to handle unwinding through generator frames?
while (unw_step(&cursor) > 0) {
unw_word_t ip, sp;
check(unw_get_reg(&cursor, UNW_REG_IP, &ip));
check(unw_get_reg(&cursor, UNW_REG_SP, &sp));
if (VERBOSITY("cxx_unwind") >= 2) {
printf(" FRAME: ip %lx sp %lx", (long)ip, (long)sp);
}

// TODO: get line info for this frame!
// TODO: should I use PythonFrameIter for this purpose?
// no, it'll skip C++ frames that might need unwinding.

unw_proc_info_t pip;
check(unw_get_proc_info(&cursor, &pip));
assert((pip.lsda == 0) == (pip.handler == 0));
assert(pip.flags == 0);

// TODO: get line info for this frame!
// TODO: should I use PythonFrameIter for this purpose?
// no, it'll skip C++ frames that might need unwinding.
if (VERBOSITY("cxx_unwind") >= 2) {
print_frame(&cursor, &pip);
}

// Skip frames without handlers
if (pip.handler == 0) {
if (VERBOSITY("cxx_unwind") >= 2) {
printf(" no handler, skipped\n");
}
continue;
} else if (VERBOSITY("cxx_unwind") >= 2) {
printf("\n");
}

RELEASE_ASSERT(pip.handler == (uintptr_t)__gxx_personality_v0,
Expand Down

0 comments on commit 1dc501b

Please sign in to comment.