Commit 79ad8be8 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge branch 'getframe_merge'

parents 62794865 45225091
...@@ -71,15 +71,9 @@ else: ...@@ -71,15 +71,9 @@ else:
_srcfile = __file__ _srcfile = __file__
_srcfile = os.path.normcase(_srcfile) _srcfile = os.path.normcase(_srcfile)
# next bit filched from 1.5.2's inspect.py # pyston changes: we don't support tb_frame or f_back, so always use sys._getframe
def currentframe(): currentframe = lambda: sys._getframe(4)
"""Return the frame object for the caller's stack frame.""" start_getframe = 4
try:
raise Exception
except:
return sys.exc_info()[2].tb_frame.f_back
if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)
# done filching # done filching
# _srcfile is only used in conjunction with sys._getframe(). # _srcfile is only used in conjunction with sys._getframe().
...@@ -1229,20 +1223,22 @@ class Logger(Filterer): ...@@ -1229,20 +1223,22 @@ class Logger(Filterer):
file name, line number and function name. file name, line number and function name.
""" """
# Pyston change:
return "(unknown file)", 0, "(unknown function)"
f = currentframe() f = currentframe()
# pyston change: we can't use f_back to walk back up the stack, so increment a counter of
# frames to skip and repeatedly call sys._getframe
fn = start_getframe
#On some versions of IronPython, currentframe() returns None if #On some versions of IronPython, currentframe() returns None if
#IronPython isn't run with -X:Frames. #IronPython isn't run with -X:Frames.
if f is not None: #if f is not None:
f = f.f_back # f = f.f_back
rv = "(unknown file)", 0, "(unknown function)" rv = "(unknown file)", 0, "(unknown function)"
while hasattr(f, "f_code"): while hasattr(f, "f_code"):
co = f.f_code co = f.f_code
filename = os.path.normcase(co.co_filename) filename = os.path.normcase(co.co_filename)
if filename == _srcfile: if filename == _srcfile:
f = f.f_back fn += 1
f = sys._getframe(fn);
#f = f.f_back
continue continue
rv = (co.co_filename, f.f_lineno, co.co_name) rv = (co.co_filename, f.f_lineno, co.co_name)
break break
......
...@@ -54,6 +54,12 @@ const int dwarf_to_gp[] = { ...@@ -54,6 +54,12 @@ const int dwarf_to_gp[] = {
// 17-32: xmm0-xmm15 // 17-32: xmm0-xmm15
}; };
Register Register::fromDwarf(int dwarf_regnum) {
assert(dwarf_regnum >= 0 && dwarf_regnum <= 16);
return Register(dwarf_to_gp[dwarf_regnum]);
}
GenericRegister GenericRegister::fromDwarf(int dwarf_regnum) { GenericRegister GenericRegister::fromDwarf(int dwarf_regnum) {
assert(dwarf_regnum >= 0); assert(dwarf_regnum >= 0);
...@@ -856,6 +862,20 @@ void Assembler::je(JumpDestination dest) { ...@@ -856,6 +862,20 @@ void Assembler::je(JumpDestination dest) {
jmp_cond(dest, COND_EQUAL); jmp_cond(dest, COND_EQUAL);
} }
void Assembler::jmpq(Register dest) {
int reg_idx = dest.regnum;
if (reg_idx >= 8) {
emitRex(REX_B);
reg_idx -= 8;
}
assert(0 <= reg_idx && reg_idx < 8);
emitByte(0xff);
emitModRM(0b11, 0b100, reg_idx);
}
void Assembler::set_cond(Register reg, ConditionCode condition) { void Assembler::set_cond(Register reg, ConditionCode condition) {
......
...@@ -140,6 +140,7 @@ public: ...@@ -140,6 +140,7 @@ public:
void jmp_cond(JumpDestination dest, ConditionCode condition); void jmp_cond(JumpDestination dest, ConditionCode condition);
void jmp(JumpDestination dest); void jmp(JumpDestination dest);
void jmpq(Register dest);
void je(JumpDestination dest); void je(JumpDestination dest);
void jne(JumpDestination dest); void jne(JumpDestination dest);
......
...@@ -45,6 +45,8 @@ struct Register { ...@@ -45,6 +45,8 @@ struct Register {
bool operator!=(const Register& rhs) const { return !(*this == rhs); } bool operator!=(const Register& rhs) const { return !(*this == rhs); }
void dump() const; void dump() const;
static Register fromDwarf(int dwarf_regnum);
}; };
const Register RAX(0); const Register RAX(0);
......
...@@ -469,8 +469,8 @@ extern "C" PyObject* PyObject_CallObject(PyObject* obj, PyObject* args) noexcept ...@@ -469,8 +469,8 @@ extern "C" PyObject* PyObject_CallObject(PyObject* obj, PyObject* args) noexcept
r = runtimeCall(obj, ArgPassSpec(0, 0, false, false), NULL, NULL, NULL, NULL, NULL); r = runtimeCall(obj, ArgPassSpec(0, 0, false, false), NULL, NULL, NULL, NULL, NULL);
return r; return r;
} catch (ExcInfo e) { } catch (ExcInfo e) {
fatalOrError(PyExc_NotImplementedError, "unimplemented"); setCAPIException(e);
return nullptr; return NULL;
} }
} }
......
...@@ -100,6 +100,16 @@ static llvm::Value* getExcinfoGep(llvm::IRBuilder<true>& builder, llvm::Value* v ...@@ -100,6 +100,16 @@ static llvm::Value* getExcinfoGep(llvm::IRBuilder<true>& builder, llvm::Value* v
return builder.CreateConstInBoundsGEP2_32(builder.CreateConstInBoundsGEP2_32(v, 0, 0), 0, 0); return builder.CreateConstInBoundsGEP2_32(builder.CreateConstInBoundsGEP2_32(v, 0, 0), 0, 0);
} }
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(Box*) == 8, "");
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)
}
llvm::Value* IRGenState::getFrameInfoVar() { llvm::Value* IRGenState::getFrameInfoVar() {
/* /*
There is a matrix of possibilities here. There is a matrix of possibilities here.
...@@ -162,6 +172,11 @@ llvm::Value* IRGenState::getFrameInfoVar() { ...@@ -162,6 +172,11 @@ llvm::Value* IRGenState::getFrameInfoVar() {
builder.CreateStore(this->boxed_locals, boxed_locals_gep); builder.CreateStore(this->boxed_locals, boxed_locals_gep);
} }
// frame_info.frame_obj = NULL
static llvm::Type* llvm_frame_obj_type_ptr
= llvm::cast<llvm::StructType>(g.llvm_frame_info_type)->getElementType(2);
builder.CreateStore(embedConstantPtr(NULL, llvm_frame_obj_type_ptr), getFrameObjGep(builder, al));
this->frame_info = al; this->frame_info = al;
} }
} }
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugInfo.h"
#include "llvm/Object/ObjectFile.h" #include "llvm/Object/ObjectFile.h"
#include "asm_writing/types.h"
#include "analysis/scoping_analysis.h" #include "analysis/scoping_analysis.h"
#include "codegen/ast_interpreter.h" #include "codegen/ast_interpreter.h"
#include "codegen/codegen.h" #include "codegen/codegen.h"
...@@ -255,30 +256,28 @@ struct PythonFrameId { ...@@ -255,30 +256,28 @@ struct PythonFrameId {
INTERPRETED, INTERPRETED,
} type; } type;
union { uint64_t ip;
uint64_t ip; // if type == COMPILED uint64_t bp;
uint64_t bp; // if type == INTERPRETED
}; bool operator==(const PythonFrameId& rhs) const { return (this->type == rhs.type) && (this->ip == rhs.ip); }
}; };
class PythonFrameIterator { class PythonFrameIteratorImpl {
private: public:
PythonFrameId id; PythonFrameId id;
unw_context_t ctx;
unw_cursor_t cursor;
CompiledFunction* cf; CompiledFunction* cf;
bool cur_is_osr; // We have to save a copy of the regs since it's very difficult to keep the unw_context_t
// structure valid.
intptr_t regs[16];
uint16_t regs_valid;
PythonFrameIterator() : cf(NULL), cur_is_osr(false) {} PythonFrameIteratorImpl(const PythonFrameIteratorImpl&) = delete;
void operator=(const PythonFrameIteratorImpl&) = delete;
PythonFrameIteratorImpl(const PythonFrameIteratorImpl&&) = delete;
void operator=(const PythonFrameIteratorImpl&&) = delete;
// not copyable or movable, since 'cursor' holds an internal pointer to 'ctx' PythonFrameIteratorImpl() : regs_valid(0) {}
PythonFrameIterator(const PythonFrameIterator&) = delete;
void operator=(const PythonFrameIterator&) = delete;
PythonFrameIterator(const PythonFrameIterator&&) = delete;
void operator=(const PythonFrameIterator&&) = delete;
public:
CompiledFunction* getCF() const { CompiledFunction* getCF() const {
assert(cf); assert(cf);
return cf; return cf;
...@@ -362,162 +361,138 @@ public: ...@@ -362,162 +361,138 @@ public:
const PythonFrameId& getId() const { return id; } const PythonFrameId& getId() const { return id; }
static std::unique_ptr<PythonFrameIterator> end() { return std::unique_ptr<PythonFrameIterator>(nullptr); }
static std::unique_ptr<PythonFrameIterator> begin() {
std::unique_ptr<PythonFrameIterator> rtn(new PythonFrameIterator());
unw_getcontext(&rtn->ctx);
unw_init_local(&rtn->cursor, &rtn->ctx);
bool found = rtn->incr();
if (!found)
return NULL;
return rtn;
}
uint64_t getReg(int dwarf_num) { uint64_t getReg(int dwarf_num) {
assert(0 <= dwarf_num && dwarf_num < 16);
// for x86_64, at least, libunwind seems to use the dwarf numbering // for x86_64, at least, libunwind seems to use the dwarf numbering
unw_word_t rtn; assert(0 <= dwarf_num && dwarf_num < 16);
int code = unw_get_reg(&cursor, dwarf_num, &rtn); assert(regs_valid & (1 << dwarf_num));
assert(code == 0); assert(id.type == PythonFrameId::COMPILED);
return rtn;
}
unw_word_t getFunctionEnd(unw_word_t ip) { return regs[dwarf_num];
unw_proc_info_t pip;
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;
} }
bool incr() { bool pointsToTheSameAs(const PythonFrameIteratorImpl& rhs) const {
static unw_word_t interpreter_instr_end = getFunctionEnd((unw_word_t)interpreter_instr_addr); return this->id.type == rhs.id.type && this->id.bp == rhs.id.bp;
static unw_word_t generator_entry_end = getFunctionEnd((unw_word_t)generatorEntry); }
};
bool was_osr = cur_is_osr;
while (true) {
int r = unw_step(&this->cursor);
if (r <= 0) {
return false;
}
unw_word_t ip;
unw_get_reg(&this->cursor, UNW_REG_IP, &ip);
cf = getCFForAddress(ip);
if (cf) {
this->id.type = PythonFrameId::COMPILED;
this->id.ip = ip;
unw_word_t bp;
unw_get_reg(&this->cursor, UNW_TDEP_BP, &bp);
cur_is_osr = (bool)cf->entry_descriptor;
if (was_osr) {
// Skip the frame we just found if the previous one was its OSR
// TODO this will break if we start collapsing the OSR frames
return incr();
}
return true;
}
if ((unw_word_t)interpreter_instr_addr <= ip && ip < interpreter_instr_end) { static unw_word_t getFunctionEnd(unw_word_t ip) {
unw_word_t bp; unw_proc_info_t pip;
unw_get_reg(&this->cursor, UNW_TDEP_BP, &bp); 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;
}
this->id.type = PythonFrameId::INTERPRETED; // While I'm not a huge fan of the callback-passing style, libunwind cursors are only valid for
this->id.bp = bp; // the stack frame that they were created in, so we need to use this approach (as opposed to
cf = getCFForInterpretedFrame((void*)bp); // C++11 range loops, for example).
// Return true from the handler to stop iteration at that frame.
void unwindPythonStack(std::function<bool(std::unique_ptr<PythonFrameIteratorImpl>&&)> func) {
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);
cur_is_osr = (bool)cf->entry_descriptor; unw_context_t ctx;
if (was_osr) { unw_cursor_t cursor;
// Skip the frame we just found if the previous one was its OSR unw_getcontext(&ctx);
// TODO this will break if we start collapsing the OSR frames unw_init_local(&cursor, &ctx);
return incr();
// If the previous (lower, ie newer) frame was a frame that got OSR'd into,
// we skip the previous frame, its OSR parent.
bool was_osr = false;
while (true) {
int r = unw_step(&cursor);
assert(r >= 0);
if (r == 0)
break;
unw_word_t ip;
unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_word_t bp;
unw_get_reg(&cursor, UNW_TDEP_BP, &bp);
CompiledFunction* cf = getCFForAddress(ip);
if (cf) {
std::unique_ptr<PythonFrameIteratorImpl> info(new PythonFrameIteratorImpl());
info->id.type = PythonFrameId::COMPILED;
info->id.ip = ip;
info->id.bp = bp;
info->cf = cf;
if (!was_osr) {
// 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
// stack frame, which can show up as a -UNW_EBADREG return code.
for (int i = 0; i < 16; i++) {
if (!assembler::Register::fromDwarf(i).isCalleeSave())
continue;
unw_word_t r;
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);
}
} }
return true; bool stop = func(std::move(info));
} if (stop)
break;
if ((unw_word_t)generatorEntry <= ip && ip < generator_entry_end) {
// for generators continue unwinding in the context in which the generator got called
unw_word_t bp;
unw_get_reg(&this->cursor, UNW_TDEP_BP, &bp);
Context* remote_ctx = getReturnContextForGeneratorFrame((void*)bp);
// setup unw_context_t struct from the infos we have, seems like this is enough to make unwinding work.
memset(&ctx, 0, sizeof(ctx));
ctx.uc_mcontext.gregs[REG_R12] = remote_ctx->r12;
ctx.uc_mcontext.gregs[REG_R13] = remote_ctx->r13;
ctx.uc_mcontext.gregs[REG_R14] = remote_ctx->r14;
ctx.uc_mcontext.gregs[REG_R15] = remote_ctx->r15;
ctx.uc_mcontext.gregs[REG_RBX] = remote_ctx->rbx;
ctx.uc_mcontext.gregs[REG_RBP] = remote_ctx->rbp;
ctx.uc_mcontext.gregs[REG_RIP] = remote_ctx->rip;
ctx.uc_mcontext.gregs[REG_RSP] = (greg_t)remote_ctx;
unw_init_local(&cursor, &ctx);
} }
was_osr = (bool)cf->entry_descriptor;
// keep unwinding continue;
} }
}
// Adapter classes to be able to use this in a C++11 range for loop.
// (Needed because we do more memory management than typical.)
// TODO: maybe the memory management should be handled by the Manager?
class Manager {
public:
class Holder {
public:
std::unique_ptr<PythonFrameIterator> it;
Holder(std::unique_ptr<PythonFrameIterator> it) : it(std::move(it)) {}
bool operator!=(const Holder& rhs) const {
assert(rhs.it.get() == NULL); // this is the only intended use case, for comparing to end()
return it != rhs.it;
}
Holder& operator++() { if ((unw_word_t)interpreter_instr_addr <= ip && ip < interpreter_instr_end) {
assert(it.get()); std::unique_ptr<PythonFrameIteratorImpl> info(new PythonFrameIteratorImpl());
info->id.type = PythonFrameId::INTERPRETED;
bool found = it->incr(); info->id.ip = ip;
if (!found) info->id.bp = bp;
it.release(); cf = info->cf = getCFForInterpretedFrame((void*)bp);
return *this; assert(cf);
}
if (!was_osr) {
PythonFrameIterator& operator*() const { bool stop = func(std::move(info));
assert(it.get()); if (stop)
return *it.get(); break;
} }
};
Holder begin() { return PythonFrameIterator::begin(); } was_osr = (bool)cf->entry_descriptor;
continue;
}
Holder end() { return PythonFrameIterator::end(); } if ((unw_word_t)generatorEntry <= ip && ip < generator_entry_end) {
}; // for generators continue unwinding in the context in which the generator got called
}; Context* remote_ctx = getReturnContextForGeneratorFrame((void*)bp);
// setup unw_context_t struct from the infos we have, seems like this is enough to make unwinding work.
memset(&ctx, 0, sizeof(ctx));
ctx.uc_mcontext.gregs[REG_R12] = remote_ctx->r12;
ctx.uc_mcontext.gregs[REG_R13] = remote_ctx->r13;
ctx.uc_mcontext.gregs[REG_R14] = remote_ctx->r14;
ctx.uc_mcontext.gregs[REG_R15] = remote_ctx->r15;
ctx.uc_mcontext.gregs[REG_RBX] = remote_ctx->rbx;
ctx.uc_mcontext.gregs[REG_RBP] = remote_ctx->rbp;
ctx.uc_mcontext.gregs[REG_RIP] = remote_ctx->rip;
ctx.uc_mcontext.gregs[REG_RSP] = (greg_t)remote_ctx;
unw_init_local(&cursor, &ctx);
}
PythonFrameIterator::Manager unwindPythonFrames() { // keep unwinding
return PythonFrameIterator::Manager(); }
} }
static std::unique_ptr<PythonFrameIterator> getTopPythonFrame() { static std::unique_ptr<PythonFrameIteratorImpl> getTopPythonFrame() {
std::unique_ptr<PythonFrameIterator> fr = PythonFrameIterator::begin(); std::unique_ptr<PythonFrameIteratorImpl> rtn(nullptr);
if (fr == PythonFrameIterator::end()) unwindPythonStack([&](std::unique_ptr<PythonFrameIteratorImpl>&& iter) {
return std::unique_ptr<PythonFrameIterator>(); rtn = std::move(iter);
return fr; return true;
});
return rtn;
} }
static const LineInfo* lineInfoForFrame(PythonFrameIterator& frame_it) { static const LineInfo* lineInfoForFrame(PythonFrameIteratorImpl& frame_it) {
AST_stmt* current_stmt = frame_it.getCurrentStatement(); AST_stmt* current_stmt = frame_it.getCurrentStatement();
auto* cf = frame_it.getCF(); auto* cf = frame_it.getCF();
assert(cf); assert(cf);
...@@ -550,11 +525,12 @@ BoxedTraceback* getTraceback() { ...@@ -550,11 +525,12 @@ BoxedTraceback* getTraceback() {
Timer _t("getTraceback", 1000); Timer _t("getTraceback", 1000);
std::vector<const LineInfo*> entries; std::vector<const LineInfo*> entries;
for (auto& frame_iter : unwindPythonFrames()) { unwindPythonStack([&](std::unique_ptr<PythonFrameIteratorImpl>&& frame_iter) {
const LineInfo* line_info = lineInfoForFrame(frame_iter); const LineInfo* line_info = lineInfoForFrame(*frame_iter.get());
if (line_info) if (line_info)
entries.push_back(line_info); entries.push_back(line_info);
} return false;
});
std::reverse(entries.begin(), entries.end()); std::reverse(entries.begin(), entries.end());
...@@ -568,8 +544,9 @@ ExcInfo* getFrameExcInfo() { ...@@ -568,8 +544,9 @@ ExcInfo* getFrameExcInfo() {
std::vector<ExcInfo*> to_update; std::vector<ExcInfo*> to_update;
ExcInfo* copy_from_exc = NULL; ExcInfo* copy_from_exc = NULL;
ExcInfo* cur_exc = NULL; ExcInfo* cur_exc = NULL;
for (PythonFrameIterator& frame_iter : unwindPythonFrames()) {
FrameInfo* frame_info = frame_iter.getFrameInfo(); unwindPythonStack([&](std::unique_ptr<PythonFrameIteratorImpl>&& frame_iter) {
FrameInfo* frame_info = frame_iter->getFrameInfo();
copy_from_exc = &frame_info->exc; copy_from_exc = &frame_info->exc;
if (!cur_exc) if (!cur_exc)
...@@ -577,11 +554,11 @@ ExcInfo* getFrameExcInfo() { ...@@ -577,11 +554,11 @@ ExcInfo* getFrameExcInfo() {
if (!copy_from_exc->type) { if (!copy_from_exc->type) {
to_update.push_back(copy_from_exc); to_update.push_back(copy_from_exc);
continue; return false;
} }
break; return true;
} });
assert(copy_from_exc); // Only way this could still be NULL is if there weren't any python frames assert(copy_from_exc); // Only way this could still be NULL is if there weren't any python frames
...@@ -630,19 +607,49 @@ BoxedModule* getCurrentModule() { ...@@ -630,19 +607,49 @@ BoxedModule* getCurrentModule() {
return compiledFunction->clfunc->source->parent_module; return compiledFunction->clfunc->source->parent_module;
} }
PythonFrameIterator getPythonFrame(int depth) {
std::unique_ptr<PythonFrameIteratorImpl> rtn(nullptr);
unwindPythonStack([&](std::unique_ptr<PythonFrameIteratorImpl>&& frame_iter) {
if (depth == 0) {
rtn = std::move(frame_iter);
return true;
}
depth--;
return false;
});
return PythonFrameIterator(std::move(rtn));
}
PythonFrameIterator::~PythonFrameIterator() {
}
PythonFrameIterator::PythonFrameIterator(PythonFrameIterator&& rhs) {
std::swap(this->impl, rhs.impl);
}
void PythonFrameIterator::operator=(PythonFrameIterator&& rhs) {
std::swap(this->impl, rhs.impl);
}
PythonFrameIterator::PythonFrameIterator(std::unique_ptr<PythonFrameIteratorImpl>&& impl) {
std::swap(this->impl, impl);
}
// TODO factor getStackLoclasIncludingUserHidden and fastLocalsToBoxedLocals // TODO factor getStackLoclasIncludingUserHidden and fastLocalsToBoxedLocals
// because they are pretty ugly but have a pretty repetitive pattern. // because they are pretty ugly but have a pretty repetitive pattern.
FrameStackState getFrameStackState() { FrameStackState getFrameStackState() {
for (PythonFrameIterator& frame_iter : unwindPythonFrames()) { FrameStackState rtn(NULL, NULL);
bool found = false;
unwindPythonStack([&](std::unique_ptr<PythonFrameIteratorImpl>&& frame_iter) {
BoxedDict* d; BoxedDict* d;
BoxedClosure* closure; BoxedClosure* closure;
CompiledFunction* cf; CompiledFunction* cf;
if (frame_iter.getId().type == PythonFrameId::COMPILED) { if (frame_iter->getId().type == PythonFrameId::COMPILED) {
d = new BoxedDict(); d = new BoxedDict();
cf = frame_iter.getCF(); cf = frame_iter->getCF();
uint64_t ip = frame_iter.getId().ip; uint64_t ip = frame_iter->getId().ip;
assert(ip > cf->code_start); assert(ip > cf->code_start);
unsigned offset = ip - cf->code_start; unsigned offset = ip - cf->code_start;
...@@ -663,7 +670,7 @@ FrameStackState getFrameStackState() { ...@@ -663,7 +670,7 @@ FrameStackState getFrameStackState() {
const auto& locs = e.locations; const auto& locs = e.locations;
assert(locs.size() == 1); assert(locs.size() == 1);
uint64_t v = frame_iter.readLocation(locs[0]); uint64_t v = frame_iter->readLocation(locs[0]);
if ((v & 1) == 0) if ((v & 1) == 0)
is_undefined.insert(p.first.substr(12)); is_undefined.insert(p.first.substr(12));
...@@ -687,7 +694,7 @@ FrameStackState getFrameStackState() { ...@@ -687,7 +694,7 @@ FrameStackState getFrameStackState() {
// printf("%s: %s\n", p.first.c_str(), e.type->debugName().c_str()); // printf("%s: %s\n", p.first.c_str(), e.type->debugName().c_str());
for (auto& loc : locs) { for (auto& loc : locs) {
vals.push_back(frame_iter.readLocation(loc)); vals.push_back(frame_iter->readLocation(loc));
} }
Box* v = e.type->deserializeFromFrame(vals); Box* v = e.type->deserializeFromFrame(vals);
...@@ -701,156 +708,168 @@ FrameStackState getFrameStackState() { ...@@ -701,156 +708,168 @@ FrameStackState getFrameStackState() {
abort(); abort();
} }
return FrameStackState(d, frame_iter.getFrameInfo()); rtn = FrameStackState(d, frame_iter->getFrameInfo());
} found = true;
RELEASE_ASSERT(0, "Internal error: unable to find any python frames"); return true;
});
RELEASE_ASSERT(found, "Internal error: unable to find any python frames");
return rtn;
} }
Box* fastLocalsToBoxedLocals() { Box* fastLocalsToBoxedLocals() {
for (PythonFrameIterator& frame_iter : unwindPythonFrames()) { return getPythonFrame(0).fastLocalsToBoxedLocals();
BoxedDict* d; }
BoxedClosure* closure;
FrameInfo* frame_info;
CompiledFunction* cf = frame_iter.getCF(); Box* PythonFrameIterator::fastLocalsToBoxedLocals() {
ScopeInfo* scope_info = cf->clfunc->source->getScopeInfo(); assert(impl.get());
if (scope_info->areLocalsFromModule()) { BoxedDict* d;
// TODO we should cache this in frame_info->locals or something so that locals() BoxedClosure* closure;
// (and globals() too) will always return the same dict FrameInfo* frame_info;
return getGlobalsDict();
}
if (frame_iter.getId().type == PythonFrameId::COMPILED) { CompiledFunction* cf = impl->getCF();
d = new BoxedDict(); ScopeInfo* scope_info = cf->clfunc->source->getScopeInfo();
uint64_t ip = frame_iter.getId().ip; if (scope_info->areLocalsFromModule()) {
// TODO we should cache this in frame_info->locals or something so that locals()
// (and globals() too) will always return the same dict
RELEASE_ASSERT(cf->clfunc->source->scoping->areGlobalsFromModule(), "");
return makeAttrWrapper(cf->clfunc->source->parent_module);
}
assert(ip > cf->code_start); if (impl->getId().type == PythonFrameId::COMPILED) {
unsigned offset = ip - cf->code_start; d = new BoxedDict();
assert(cf->location_map); uint64_t ip = impl->getId().ip;
// We have to detect + ignore any entries for variables that assert(ip > cf->code_start);
// could have been defined (so they have entries) but aren't (so the unsigned offset = ip - cf->code_start;
// entries point to uninitialized memory).
std::unordered_set<std::string> is_undefined;
for (const auto& p : cf->location_map->names) { assert(cf->location_map);
if (!startswith(p.first, "!is_defined_"))
continue;
for (const LocationMap::LocationTable::LocationEntry& e : p.second.locations) { // We have to detect + ignore any entries for variables that
if (e.offset < offset && offset <= e.offset + e.length) { // could have been defined (so they have entries) but aren't (so the
const auto& locs = e.locations; // entries point to uninitialized memory).
std::unordered_set<std::string> is_undefined;
assert(locs.size() == 1); for (const auto& p : cf->location_map->names) {
uint64_t v = frame_iter.readLocation(locs[0]); if (!startswith(p.first, "!is_defined_"))
if ((v & 1) == 0) continue;
is_undefined.insert(p.first.substr(12));
break; for (const LocationMap::LocationTable::LocationEntry& e : p.second.locations) {
} if (e.offset < offset && offset <= e.offset + e.length) {
const auto& locs = e.locations;
assert(locs.size() == 1);
uint64_t v = impl->readLocation(locs[0]);
if ((v & 1) == 0)
is_undefined.insert(p.first.substr(12));
break;
} }
} }
}
for (const auto& p : cf->location_map->names) { for (const auto& p : cf->location_map->names) {
if (p.first[0] == '!') if (p.first[0] == '!')
continue; continue;
if (p.first[0] == '#')
continue;
if (is_undefined.count(p.first)) if (p.first[0] == '#')
continue; continue;
for (const LocationMap::LocationTable::LocationEntry& e : p.second.locations) { if (is_undefined.count(p.first))
if (e.offset < offset && offset <= e.offset + e.length) { continue;
const auto& locs = e.locations;
llvm::SmallVector<uint64_t, 1> vals; for (const LocationMap::LocationTable::LocationEntry& e : p.second.locations) {
// printf("%s: %s\n", p.first.c_str(), e.type->debugName().c_str()); if (e.offset < offset && offset <= e.offset + e.length) {
const auto& locs = e.locations;
for (auto& loc : locs) { llvm::SmallVector<uint64_t, 1> vals;
vals.push_back(frame_iter.readLocation(loc)); // printf("%s: %s\n", p.first.c_str(), e.type->debugName().c_str());
} // printf("%ld locs\n", locs.size());
Box* v = e.type->deserializeFromFrame(vals); for (auto& loc : locs) {
// printf("%s: (pp id %ld) %p\n", p.first.c_str(), e._debug_pp_id, v); auto v = impl->readLocation(loc);
assert(gc::isValidGCObject(v)); vals.push_back(v);
d->d[boxString(p.first)] = v; // printf("%d %d %d: 0x%lx\n", loc.type, loc.regnum, loc.offset, v);
// dump((void*)v);
} }
Box* v = e.type->deserializeFromFrame(vals);
// printf("%s: (pp id %ld) %p\n", p.first.c_str(), e._debug_pp_id, v);
assert(gc::isValidGCObject(v));
d->d[boxString(p.first)] = v;
} }
} }
}
closure = NULL; closure = NULL;
if (cf->location_map->names.count(PASSED_CLOSURE_NAME) > 0) { if (cf->location_map->names.count(PASSED_CLOSURE_NAME) > 0) {
for (const LocationMap::LocationTable::LocationEntry& e : for (const LocationMap::LocationTable::LocationEntry& e :
cf->location_map->names[PASSED_CLOSURE_NAME].locations) { cf->location_map->names[PASSED_CLOSURE_NAME].locations) {
if (e.offset < offset && offset <= e.offset + e.length) { if (e.offset < offset && offset <= e.offset + e.length) {
const auto& locs = e.locations; const auto& locs = e.locations;
llvm::SmallVector<uint64_t, 1> vals;
for (auto& loc : locs) { llvm::SmallVector<uint64_t, 1> vals;
vals.push_back(frame_iter.readLocation(loc));
}
Box* v = e.type->deserializeFromFrame(vals); for (auto& loc : locs) {
assert(gc::isValidGCObject(v)); vals.push_back(impl->readLocation(loc));
closure = static_cast<BoxedClosure*>(v);
} }
Box* v = e.type->deserializeFromFrame(vals);
assert(gc::isValidGCObject(v));
closure = static_cast<BoxedClosure*>(v);
} }
} }
frame_info = frame_iter.getFrameInfo();
} else if (frame_iter.getId().type == PythonFrameId::INTERPRETED) {
d = localsForInterpretedFrame((void*)frame_iter.getId().bp, true);
closure = passedClosureForInterpretedFrame((void*)frame_iter.getId().bp);
frame_info = getFrameInfoForInterpretedFrame((void*)frame_iter.getId().bp);
} else {
abort();
} }
assert(frame_info); frame_info = impl->getFrameInfo();
if (frame_info->boxedLocals == NULL) { } else if (impl->getId().type == PythonFrameId::INTERPRETED) {
frame_info->boxedLocals = new BoxedDict(); d = localsForInterpretedFrame((void*)impl->getId().bp, true);
} closure = passedClosureForInterpretedFrame((void*)impl->getId().bp);
assert(gc::isValidGCObject(frame_info->boxedLocals)); frame_info = getFrameInfoForInterpretedFrame((void*)impl->getId().bp);
} else {
// Add the locals from the closure abort();
// TODO in a ClassDef scope, we aren't supposed to add these }
size_t depth = 0;
for (auto& p : scope_info->getAllDerefVarsAndInfo()) {
InternedString name = p.first;
DerefInfo derefInfo = p.second;
while (depth < derefInfo.num_parents_from_passed_closure) {
depth++;
closure = closure->parent;
}
assert(closure != NULL);
Box* val = closure->elts[derefInfo.offset];
Box* boxedName = boxString(name.str());
if (val != NULL) {
d->d[boxedName] = val;
} else {
d->d.erase(boxedName);
}
}
// Loop through all the values found above. assert(frame_info);
// TODO Right now d just has all the python variables that are *initialized* if (frame_info->boxedLocals == NULL) {
// But we also need to loop through all the uninitialized variables that we have frame_info->boxedLocals = new BoxedDict();
// access to and delete them from the locals dict }
for (const auto& p : d->d) { assert(gc::isValidGCObject(frame_info->boxedLocals));
Box* varname = p.first;
Box* value = p.second; // Add the locals from the closure
setitem(frame_info->boxedLocals, varname, value); // TODO in a ClassDef scope, we aren't supposed to add these
size_t depth = 0;
for (auto& p : scope_info->getAllDerefVarsAndInfo()) {
InternedString name = p.first;
DerefInfo derefInfo = p.second;
while (depth < derefInfo.num_parents_from_passed_closure) {
depth++;
closure = closure->parent;
}
assert(closure != NULL);
Box* val = closure->elts[derefInfo.offset];
Box* boxedName = boxString(name.str());
if (val != NULL) {
d->d[boxedName] = val;
} else {
d->d.erase(boxedName);
} }
}
return frame_info->boxedLocals; // Loop through all the values found above.
// TODO Right now d just has all the python variables that are *initialized*
// But we also need to loop through all the uninitialized variables that we have
// access to and delete them from the locals dict
for (const auto& p : d->d) {
Box* varname = p.first;
Box* value = p.second;
setitem(frame_info->boxedLocals, varname, value);
} }
RELEASE_ASSERT(0, "Internal error: unable to find any python frames");
return frame_info->boxedLocals;
} }
ExecutionPoint getExecutionPoint() { ExecutionPoint getExecutionPoint() {
...@@ -860,6 +879,34 @@ ExecutionPoint getExecutionPoint() { ...@@ -860,6 +879,34 @@ ExecutionPoint getExecutionPoint() {
return ExecutionPoint({.cf = cf, .current_stmt = current_stmt }); return ExecutionPoint({.cf = cf, .current_stmt = current_stmt });
} }
std::unique_ptr<ExecutionPoint> PythonFrameIterator::getExecutionPoint() {
assert(impl.get());
auto cf = impl->getCF();
auto stmt = impl->getCurrentStatement();
return std::unique_ptr<ExecutionPoint>(new ExecutionPoint({.cf = cf, .current_stmt = stmt }));
}
CompiledFunction* PythonFrameIterator::getCF() {
return impl->getCF();
}
FrameInfo* PythonFrameIterator::getFrameInfo() {
return impl->getFrameInfo();
}
PythonFrameIterator PythonFrameIterator::getCurrentVersion() {
std::unique_ptr<PythonFrameIteratorImpl> rtn(nullptr);
auto& impl = this->impl;
unwindPythonStack([&](std::unique_ptr<PythonFrameIteratorImpl>&& frame_iter) {
if (frame_iter->pointsToTheSameAs(*impl.get())) {
rtn = std::move(frame_iter);
return true;
}
return false;
});
return PythonFrameIterator(std::move(rtn));
}
llvm::JITEventListener* makeTracebacksListener() { llvm::JITEventListener* makeTracebacksListener() {
return new TracebacksEventListener(); return new TracebacksEventListener();
} }
......
...@@ -33,19 +33,47 @@ Box* getGlobalsDict(); // always returns a dict-like object ...@@ -33,19 +33,47 @@ Box* getGlobalsDict(); // always returns a dict-like object
BoxedTraceback* getTraceback(); BoxedTraceback* getTraceback();
struct ExecutionPoint {
CompiledFunction* cf;
AST_stmt* current_stmt;
};
ExecutionPoint getExecutionPoint();
// Adds stack locals and closure locals into the locals dict, and returns it. // Adds stack locals and closure locals into the locals dict, and returns it.
Box* fastLocalsToBoxedLocals(); Box* fastLocalsToBoxedLocals();
class PythonFrameIteratorImpl;
class PythonFrameIterator {
private:
std::unique_ptr<PythonFrameIteratorImpl> impl;
public:
CompiledFunction* getCF();
FrameInfo* getFrameInfo();
bool exists() { return impl.get() != NULL; }
std::unique_ptr<ExecutionPoint> getExecutionPoint();
Box* fastLocalsToBoxedLocals();
// Gets the "current version" of this frame: if the frame has executed since
// the iterator was obtained, the methods may return old values. This returns
// an updated copy that returns the updated values.
// The "current version" will live at the same stack location, but any other
// similarities need to be verified by the caller.
// This function can only be called from the thread that created this object.
PythonFrameIterator getCurrentVersion();
PythonFrameIterator(PythonFrameIterator&& rhs);
void operator=(PythonFrameIterator&& rhs);
PythonFrameIterator(std::unique_ptr<PythonFrameIteratorImpl>&& impl);
~PythonFrameIterator();
};
PythonFrameIterator getPythonFrame(int depth);
// Fetches a writeable pointer to the frame-local excinfo object, // Fetches a writeable pointer to the frame-local excinfo object,
// calculating it if necessary (from previous frames). // calculating it if necessary (from previous frames).
ExcInfo* getFrameExcInfo(); ExcInfo* getFrameExcInfo();
struct ExecutionPoint {
CompiledFunction* cf;
AST_stmt* current_stmt;
};
ExecutionPoint getExecutionPoint();
struct FrameStackState { struct FrameStackState {
// This includes all # variables (but not the ! ones). // This includes all # variables (but not the ! ones).
// Therefore, it's not the same as the BoxedLocals. // Therefore, it's not the same as the BoxedLocals.
......
...@@ -561,16 +561,29 @@ struct ExcInfo { ...@@ -561,16 +561,29 @@ struct ExcInfo {
void printExcAndTraceback() const; void printExcAndTraceback() const;
}; };
class BoxedFrame;
struct FrameInfo { struct FrameInfo {
// Note(kmod): we have a number of fields here that all have independent
// initialization rules. We could potentially save time on every function-entry
// by having an "initialized" variable (or condition) that guards all of them.
// *Not the same semantics as CPython's frame->f_exc* // *Not the same semantics as CPython's frame->f_exc*
// In CPython, f_exc is the saved exc_info from the previous frame. // In CPython, f_exc is the saved exc_info from the previous frame.
// In Pyston, exc is the frame-local value of sys.exc_info. // In Pyston, exc is the frame-local value of sys.exc_info.
// - This makes frame entering+leaving faster at the expense of slower exceptions. // - This makes frame entering+leaving faster at the expense of slower exceptions.
//
// exc.type is initialized to NULL at function entry, and exc.value and exc.tb are left
// uninitialized. When one wants to access any of the values, you need to check if exc.type
// is NULL, and if so crawl up the stack looking for the first frame with a non-null exc.type
// and copy that.
ExcInfo exc; ExcInfo exc;
// This field is always initialized:
Box* boxedLocals; Box* boxedLocals;
FrameInfo(ExcInfo exc) : exc(exc), boxedLocals(NULL) {} BoxedFrame* frame_obj;
FrameInfo(ExcInfo exc) : exc(exc), boxedLocals(NULL), frame_obj(0) {}
}; };
struct CallattrFlags { struct CallattrFlags {
......
...@@ -99,6 +99,22 @@ Box* getSysStdout() { ...@@ -99,6 +99,22 @@ Box* getSysStdout() {
return sys_stdout; return sys_stdout;
} }
Box* sysGetFrame(Box* val) {
int depth = 0;
if (val) {
if (!isSubclass(val->cls, int_cls)) {
raiseExcHelper(TypeError, "TypeError: an integer is required");
}
depth = static_cast<BoxedInt*>(val)->n;
}
Box* frame = getFrame(depth);
if (!frame) {
raiseExcHelper(ValueError, "call stack is not deep enough");
}
return frame;
}
Box* sysGetDefaultEncoding() { Box* sysGetDefaultEncoding() {
return boxStrConstant(PyUnicode_GetDefaultEncoding()); return boxStrConstant(PyUnicode_GetDefaultEncoding());
} }
...@@ -281,6 +297,8 @@ void setupSys() { ...@@ -281,6 +297,8 @@ void setupSys() {
main_fn = llvm::sys::fs::getMainExecutable(NULL, NULL); main_fn = llvm::sys::fs::getMainExecutable(NULL, NULL);
sys_module->giveAttr("executable", boxString(main_fn.str())); sys_module->giveAttr("executable", boxString(main_fn.str()));
sys_module->giveAttr("_getframe",
new BoxedFunction(boxRTFunction((void*)sysGetFrame, UNKNOWN, 1, 1, false, false), { NULL }));
sys_module->giveAttr( sys_module->giveAttr(
"getdefaultencoding", "getdefaultencoding",
new BoxedBuiltinFunctionOrMethod(boxRTFunction((void*)sysGetDefaultEncoding, STR, 0), "getdefaultencoding")); new BoxedBuiltinFunctionOrMethod(boxRTFunction((void*)sysGetDefaultEncoding, STR, 0), "getdefaultencoding"));
......
...@@ -39,6 +39,16 @@ public: ...@@ -39,6 +39,16 @@ public:
boxGCHandler(v, b); boxGCHandler(v, b);
} }
static Box* name(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, "");
return boxString(static_cast<BoxedCode*>(b)->f->source->getName());
}
static Box* filename(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, "");
return boxString(static_cast<BoxedCode*>(b)->f->source->parent_module->fn);
}
static Box* argcount(Box* b, void*) { static Box* argcount(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, ""); RELEASE_ASSERT(b->cls == code_cls, "");
...@@ -80,12 +90,18 @@ Box* codeForFunction(BoxedFunction* f) { ...@@ -80,12 +90,18 @@ Box* codeForFunction(BoxedFunction* f) {
return new BoxedCode(f->f); return new BoxedCode(f->f);
} }
Box* codeForCLFunction(CLFunction* f) {
return new BoxedCode(f);
}
void setupCode() { void setupCode() {
code_cls code_cls
= BoxedHeapClass::create(type_cls, object_cls, &BoxedCode::gcHandler, 0, 0, sizeof(BoxedCode), false, "code"); = BoxedHeapClass::create(type_cls, object_cls, &BoxedCode::gcHandler, 0, 0, sizeof(BoxedCode), false, "code");
code_cls->giveAttr("__new__", None); // Hacky way of preventing users from instantiating this code_cls->giveAttr("__new__", None); // Hacky way of preventing users from instantiating this
code_cls->giveAttr("co_name", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::name, NULL, NULL));
code_cls->giveAttr("co_filename", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::filename, NULL, NULL));
code_cls->giveAttr("co_argcount", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::argcount, NULL, NULL)); code_cls->giveAttr("co_argcount", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::argcount, NULL, NULL));
code_cls->giveAttr("co_varnames", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::varnames, NULL, NULL)); code_cls->giveAttr("co_varnames", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::varnames, NULL, NULL));
code_cls->giveAttr("co_flags", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::flags, NULL, NULL)); code_cls->giveAttr("co_flags", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::flags, NULL, NULL));
......
// Copyright (c) 2014-2015 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Python.h"
#include "pythread.h"
#include "codegen/unwinding.h"
#include "runtime/types.h"
namespace pyston {
BoxedClass* frame_cls;
// Issues:
// - breaks gdb backtraces
// - breaks c++ exceptions
// - we never free the trampolines
class BoxedFrame : public Box {
public:
BoxedFrame(PythonFrameIterator&& it) __attribute__((visibility("default")))
: it(std::move(it)), thread_id(PyThread_get_thread_ident()) {}
PythonFrameIterator it;
long thread_id;
Box* _globals;
Box* _code;
void update() {
// This makes sense as an exception, but who knows how the user program would react
// (it might swallow it and do something different)
RELEASE_ASSERT(thread_id == PyThread_get_thread_ident(),
"frame objects can only be accessed from the same thread");
PythonFrameIterator new_it = it.getCurrentVersion();
RELEASE_ASSERT(new_it.exists() && new_it.getFrameInfo()->frame_obj == this, "frame has exited");
it = std::move(new_it);
}
// cpython frame objects have the following attributes
// read-only attributes
//
// f_back[*] : previous stack frame (toward caller)
// f_code : code object being executed in this frame
// f_locals : dictionary used to look up local variables in this frame
// f_globals : dictionary used to look up global variables in this frame
// f_builtins[*] : dictionary to look up built-in (intrinsic) names
// f_restricted[*] : whether this function is executing in restricted execution mode
// f_lasti[*] : precise instruction (this is an index into the bytecode string of the code object)
// writable attributes
//
// f_trace[*] : if not None, is a function called at the start of each source code line (used by debugger)
// f_exc_type[*], : represent the last exception raised in the parent frame provided another exception was
// f_exc_value[*], : ever raised in the current frame (in all other cases they are None).
// f_exc_traceback[*] :
// f_lineno[**] : the current line number of the frame -- writing to this from within a trace function jumps
// to
// : the given line (only for the bottom-most frame). A debugger can implement a Jump command
// (aka
// : Set Next Statement) by writing to f_lineno
//
// * = unsupported in Pyston
// ** = getter supported, but setter unsupported
static void gchandler(GCVisitor* v, Box* b) {
boxGCHandler(v, b);
auto f = static_cast<BoxedFrame*>(b);
v->visit(f->_code);
v->visit(f->_globals);
}
static void simpleDestructor(Box* b) {
auto f = static_cast<BoxedFrame*>(b);
f->it.~PythonFrameIterator();
}
static Box* code(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj);
return f->_code;
}
static Box* locals(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj);
f->update();
return f->it.fastLocalsToBoxedLocals();
}
static Box* globals(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj);
return f->_globals;
}
static Box* lineno(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj);
f->update();
std::unique_ptr<ExecutionPoint> fr = f->it.getExecutionPoint();
return boxInt(fr->current_stmt->lineno);
}
DEFAULT_CLASS(frame_cls);
};
Box* getFrame(int depth) {
auto it = getPythonFrame(depth);
if (!it.exists())
return NULL;
FrameInfo* fi = it.getFrameInfo();
if (fi->frame_obj == NULL) {
auto cf = it.getCF();
BoxedFrame* f = fi->frame_obj = new BoxedFrame(std::move(it));
assert(cf->clfunc->source->scoping->areGlobalsFromModule());
f->_globals = makeAttrWrapper(cf->clfunc->source->parent_module);
f->_code = codeForCLFunction(cf->clfunc);
}
return fi->frame_obj;
}
void setupFrame() {
frame_cls = BoxedHeapClass::create(type_cls, object_cls, &BoxedFrame::gchandler, 0, 0, sizeof(BoxedFrame), false,
"frame");
frame_cls->simple_destructor = BoxedFrame::simpleDestructor;
frame_cls->giveAttr("f_code", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::code, NULL, NULL));
frame_cls->giveAttr("f_locals", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::locals, NULL, NULL));
frame_cls->giveAttr("f_lineno", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::lineno, NULL, NULL));
frame_cls->giveAttr("f_globals", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::globals, NULL, NULL));
frame_cls->freeze();
}
}
...@@ -158,6 +158,20 @@ static void writeTrivialEhFrame(void* eh_frame_addr, void* func_addr, uint64_t f ...@@ -158,6 +158,20 @@ static void writeTrivialEhFrame(void* eh_frame_addr, void* func_addr, uint64_t f
*size_ptr = func_size; *size_ptr = func_size;
} }
void EHFrameManager::writeAndRegister(void* func_addr, uint64_t func_size) {
assert(eh_frame_addr == NULL);
eh_frame_addr = malloc(EH_FRAME_SIZE);
writeTrivialEhFrame(eh_frame_addr, func_addr, func_size);
registerEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
}
EHFrameManager::~EHFrameManager() {
if (eh_frame_addr) {
deregisterEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
free(eh_frame_addr);
}
}
#if RUNTIMEICS_OMIT_FRAME_PTR #if RUNTIMEICS_OMIT_FRAME_PTR
// If you change this, you *must* update the value in _eh_frame_template // If you change this, you *must* update the value in _eh_frame_template
// (set the -9'th byte to this value plus 8) // (set the -9'th byte to this value plus 8)
...@@ -256,10 +270,7 @@ RuntimeIC::RuntimeIC(void* func_addr, int num_slots, int slot_size) { ...@@ -256,10 +270,7 @@ RuntimeIC::RuntimeIC(void* func_addr, int num_slots, int slot_size) {
// TODO: ideally would be more intelligent about allocation strategies. // TODO: ideally would be more intelligent about allocation strategies.
// The code sections should be together and the eh sections together // The code sections should be together and the eh sections together
eh_frame_addr = malloc(EH_FRAME_SIZE); eh_frame.writeAndRegister(addr, total_size);
writeTrivialEhFrame(eh_frame_addr, addr, total_size);
registerEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
} else { } else {
addr = func_addr; addr = func_addr;
} }
...@@ -268,9 +279,7 @@ RuntimeIC::RuntimeIC(void* func_addr, int num_slots, int slot_size) { ...@@ -268,9 +279,7 @@ RuntimeIC::RuntimeIC(void* func_addr, int num_slots, int slot_size) {
RuntimeIC::~RuntimeIC() { RuntimeIC::~RuntimeIC() {
if (ENABLE_RUNTIME_ICS) { if (ENABLE_RUNTIME_ICS) {
deregisterCompiledPatchpoint(icinfo.get()); deregisterCompiledPatchpoint(icinfo.get());
deregisterEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
free(addr); free(addr);
free(eh_frame_addr);
} else { } else {
} }
} }
......
...@@ -22,10 +22,20 @@ namespace pyston { ...@@ -22,10 +22,20 @@ namespace pyston {
class ICInfo; class ICInfo;
class EHFrameManager {
private:
void* eh_frame_addr;
public:
EHFrameManager() : eh_frame_addr(NULL) {}
~EHFrameManager();
void writeAndRegister(void* func_addr, uint64_t func_size);
};
class RuntimeIC { class RuntimeIC {
private: private:
void* addr; void* addr;
void* eh_frame_addr; EHFrameManager eh_frame;
std::unique_ptr<ICInfo> icinfo; std::unique_ptr<ICInfo> icinfo;
......
...@@ -2146,6 +2146,7 @@ void setupRuntime() { ...@@ -2146,6 +2146,7 @@ void setupRuntime() {
setupDescr(); setupDescr();
setupTraceback(); setupTraceback();
setupCode(); setupCode();
setupFrame();
function_cls->giveAttr("__dict__", dict_descr); function_cls->giveAttr("__dict__", dict_descr);
function_cls->giveAttr("__name__", new (pyston_getset_cls) BoxedGetsetDescriptor(funcName, funcSetName, NULL)); function_cls->giveAttr("__name__", new (pyston_getset_cls) BoxedGetsetDescriptor(funcName, funcSetName, NULL));
......
...@@ -68,6 +68,7 @@ void setupGenerator(); ...@@ -68,6 +68,7 @@ void setupGenerator();
void setupDescr(); void setupDescr();
void teardownDescr(); void teardownDescr();
void setupCode(); void setupCode();
void setupFrame();
void setupSys(); void setupSys();
void setupBuiltins(); void setupBuiltins();
...@@ -815,6 +816,9 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) ...@@ -815,6 +816,9 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
extern Box* dict_descr; extern Box* dict_descr;
Box* codeForFunction(BoxedFunction*); Box* codeForFunction(BoxedFunction*);
Box* codeForCLFunction(CLFunction*);
Box* getFrame(int depth);
} }
#endif #endif
...@@ -15,7 +15,7 @@ d[2].append(3) ...@@ -15,7 +15,7 @@ d[2].append(3)
print sorted(d.items()) print sorted(d.items())
NT = collections.namedtuple("NT", ["field1", "field2"]) NT = collections.namedtuple("NT", ["field1", "field2"])
print NT.__name__ print NT.__name__, NT
n = NT(1, "hi") n = NT(1, "hi")
print n.field1, n.field2, len(n), list(n), n[0], n[-1] print n.field1, n.field2, len(n), list(n), n[0], n[-1]
print n print n
import logging
import warnings
# output something using the root logger
def foo():
logging.warning("Houston, we have a %s", "bit of a problem")
foo()
import warnings
def fxn():
warnings.warn("deprecated", DeprecationWarning)
with warnings.catch_warnings(record=True) as w:
# Cause all warnings to always be triggered.
warnings.simplefilter("always")
# Trigger a warning.
fxn()
# Verify some things
assert len(w) == 1
assert issubclass(w[-1].category, DeprecationWarning)
assert "deprecated" in str(w[-1].message)
print "warnings module works"
# expected: fail
# - needs sys._getframe
import collections
NT = collections.namedtuple("NT", ["field1", "field2"])
print NT
"""
Frame Hack Recipe #1: Ruby-style string interpolation (version 1)
"""
# from http://farmdev.com/src/secrets/framehack/interpolate/solutions/interpolate1.py
import sys
from string import Template
def interpolate(templateStr):
frame = sys._getframe(1)
framedict = frame.f_locals
t = Template(templateStr)
return t.substitute(**framedict)
name = 'Feihong'
place = 'Chicago'
print interpolate("My name is ${name}. I work in ${place}.")
import sys
def sysframetest():
return sys._getframe(0)
def sysframetestwrapper():
return sysframetest()
fr = sysframetest()
print sysframetest.__name__
print fr.f_code.co_name
print fr.f_code.co_filename
fr = sysframetestwrapper()
print sysframetestwrapper.__name__
print fr.f_code.co_name
print fr.f_code.co_filename
import sys
def f():
fr = sys._getframe(0)
print fr.f_lineno
print fr.f_lineno
print sorted(fr.f_locals.keys())
a = 1
print sorted(fr.f_locals.keys())
f()
assert sys._getframe(0) is sys._getframe(0)
def f2():
f1 = sys._getframe(0)
# trigger osr:
for i in xrange(20000):
pass
assert f1 is sys._getframe(0)
f2()
# Make sure we can throw exceptions through frame we called _getframe
import sys
def g():
sys._getframe(1)
1/0
def f():
g()
try:
f()
except Exception as e:
print e
# expected: fail
# - we don't (yet?) support looking at frame objects after
# their frame has exited
import sys
def f():
return sys._getframe(0)
fr = f()
print fr.f_locals
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