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

Merge branch 'getframe_merge'

parents 62794865 45225091
......@@ -71,15 +71,9 @@ else:
_srcfile = __file__
_srcfile = os.path.normcase(_srcfile)
# next bit filched from 1.5.2's inspect.py
def currentframe():
"""Return the frame object for the caller's stack frame."""
try:
raise Exception
except:
return sys.exc_info()[2].tb_frame.f_back
if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)
# pyston changes: we don't support tb_frame or f_back, so always use sys._getframe
currentframe = lambda: sys._getframe(4)
start_getframe = 4
# done filching
# _srcfile is only used in conjunction with sys._getframe().
......@@ -1229,20 +1223,22 @@ class Logger(Filterer):
file name, line number and function name.
"""
# Pyston change:
return "(unknown file)", 0, "(unknown function)"
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
#IronPython isn't run with -X:Frames.
if f is not None:
f = f.f_back
#if f is not None:
# f = f.f_back
rv = "(unknown file)", 0, "(unknown function)"
while hasattr(f, "f_code"):
co = f.f_code
filename = os.path.normcase(co.co_filename)
if filename == _srcfile:
f = f.f_back
fn += 1
f = sys._getframe(fn);
#f = f.f_back
continue
rv = (co.co_filename, f.f_lineno, co.co_name)
break
......
......@@ -54,6 +54,12 @@ const int dwarf_to_gp[] = {
// 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) {
assert(dwarf_regnum >= 0);
......@@ -856,6 +862,20 @@ void Assembler::je(JumpDestination dest) {
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) {
......
......@@ -140,6 +140,7 @@ public:
void jmp_cond(JumpDestination dest, ConditionCode condition);
void jmp(JumpDestination dest);
void jmpq(Register dest);
void je(JumpDestination dest);
void jne(JumpDestination dest);
......
......@@ -45,6 +45,8 @@ struct Register {
bool operator!=(const Register& rhs) const { return !(*this == rhs); }
void dump() const;
static Register fromDwarf(int dwarf_regnum);
};
const Register RAX(0);
......
......@@ -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);
return r;
} catch (ExcInfo e) {
fatalOrError(PyExc_NotImplementedError, "unimplemented");
return nullptr;
setCAPIException(e);
return NULL;
}
}
......
......@@ -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);
}
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() {
/*
There is a matrix of possibilities here.
......@@ -162,6 +172,11 @@ llvm::Value* IRGenState::getFrameInfoVar() {
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;
}
}
......
......@@ -27,6 +27,7 @@
#include "llvm/IR/DebugInfo.h"
#include "llvm/Object/ObjectFile.h"
#include "asm_writing/types.h"
#include "analysis/scoping_analysis.h"
#include "codegen/ast_interpreter.h"
#include "codegen/codegen.h"
......@@ -255,30 +256,28 @@ struct PythonFrameId {
INTERPRETED,
} type;
union {
uint64_t ip; // if type == COMPILED
uint64_t bp; // if type == INTERPRETED
};
uint64_t ip;
uint64_t bp;
bool operator==(const PythonFrameId& rhs) const { return (this->type == rhs.type) && (this->ip == rhs.ip); }
};
class PythonFrameIterator {
private:
class PythonFrameIteratorImpl {
public:
PythonFrameId id;
unw_context_t ctx;
unw_cursor_t cursor;
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'
PythonFrameIterator(const PythonFrameIterator&) = delete;
void operator=(const PythonFrameIterator&) = delete;
PythonFrameIterator(const PythonFrameIterator&&) = delete;
void operator=(const PythonFrameIterator&&) = delete;
PythonFrameIteratorImpl() : regs_valid(0) {}
public:
CompiledFunction* getCF() const {
assert(cf);
return cf;
......@@ -362,162 +361,138 @@ public:
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) {
assert(0 <= dwarf_num && dwarf_num < 16);
// for x86_64, at least, libunwind seems to use the dwarf numbering
unw_word_t rtn;
int code = unw_get_reg(&cursor, dwarf_num, &rtn);
assert(code == 0);
return rtn;
}
assert(0 <= dwarf_num && dwarf_num < 16);
assert(regs_valid & (1 << dwarf_num));
assert(id.type == PythonFrameId::COMPILED);
unw_word_t getFunctionEnd(unw_word_t ip) {
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;
return regs[dwarf_num];
}
bool 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);
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;
}
bool pointsToTheSameAs(const PythonFrameIteratorImpl& rhs) const {
return this->id.type == rhs.id.type && this->id.bp == rhs.id.bp;
}
};
if ((unw_word_t)interpreter_instr_addr <= ip && ip < interpreter_instr_end) {
unw_word_t bp;
unw_get_reg(&this->cursor, UNW_TDEP_BP, &bp);
static unw_word_t getFunctionEnd(unw_word_t ip) {
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;
}
this->id.type = PythonFrameId::INTERPRETED;
this->id.bp = bp;
cf = getCFForInterpretedFrame((void*)bp);
// While I'm not a huge fan of the callback-passing style, libunwind cursors are only valid for
// the stack frame that they were created in, so we need to use this approach (as opposed to
// 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;
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();
unw_context_t ctx;
unw_cursor_t cursor;
unw_getcontext(&ctx);
unw_init_local(&cursor, &ctx);
// 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;
}
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);
bool stop = func(std::move(info));
if (stop)
break;
}
// keep unwinding
was_osr = (bool)cf->entry_descriptor;
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++() {
assert(it.get());
bool found = it->incr();
if (!found)
it.release();
return *this;
}
PythonFrameIterator& operator*() const {
assert(it.get());
return *it.get();
if ((unw_word_t)interpreter_instr_addr <= ip && ip < interpreter_instr_end) {
std::unique_ptr<PythonFrameIteratorImpl> info(new PythonFrameIteratorImpl());
info->id.type = PythonFrameId::INTERPRETED;
info->id.ip = ip;
info->id.bp = bp;
cf = info->cf = getCFForInterpretedFrame((void*)bp);
assert(cf);
if (!was_osr) {
bool stop = func(std::move(info));
if (stop)
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() {
return PythonFrameIterator::Manager();
// keep unwinding
}
}
static std::unique_ptr<PythonFrameIterator> getTopPythonFrame() {
std::unique_ptr<PythonFrameIterator> fr = PythonFrameIterator::begin();
if (fr == PythonFrameIterator::end())
return std::unique_ptr<PythonFrameIterator>();
return fr;
static std::unique_ptr<PythonFrameIteratorImpl> getTopPythonFrame() {
std::unique_ptr<PythonFrameIteratorImpl> rtn(nullptr);
unwindPythonStack([&](std::unique_ptr<PythonFrameIteratorImpl>&& iter) {
rtn = std::move(iter);
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();
auto* cf = frame_it.getCF();
assert(cf);
......@@ -550,11 +525,12 @@ BoxedTraceback* getTraceback() {
Timer _t("getTraceback", 1000);
std::vector<const LineInfo*> entries;
for (auto& frame_iter : unwindPythonFrames()) {
const LineInfo* line_info = lineInfoForFrame(frame_iter);
unwindPythonStack([&](std::unique_ptr<PythonFrameIteratorImpl>&& frame_iter) {
const LineInfo* line_info = lineInfoForFrame(*frame_iter.get());
if (line_info)
entries.push_back(line_info);
}
return false;
});
std::reverse(entries.begin(), entries.end());
......@@ -568,8 +544,9 @@ ExcInfo* getFrameExcInfo() {
std::vector<ExcInfo*> to_update;
ExcInfo* copy_from_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;
if (!cur_exc)
......@@ -577,11 +554,11 @@ ExcInfo* getFrameExcInfo() {
if (!copy_from_exc->type) {
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
......@@ -630,19 +607,49 @@ BoxedModule* getCurrentModule() {
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
// because they are pretty ugly but have a pretty repetitive pattern.
FrameStackState getFrameStackState() {
for (PythonFrameIterator& frame_iter : unwindPythonFrames()) {
FrameStackState rtn(NULL, NULL);
bool found = false;
unwindPythonStack([&](std::unique_ptr<PythonFrameIteratorImpl>&& frame_iter) {
BoxedDict* d;
BoxedClosure* closure;
CompiledFunction* cf;
if (frame_iter.getId().type == PythonFrameId::COMPILED) {
if (frame_iter->getId().type == PythonFrameId::COMPILED) {
d = new BoxedDict();
cf = frame_iter.getCF();
uint64_t ip = frame_iter.getId().ip;
cf = frame_iter->getCF();
uint64_t ip = frame_iter->getId().ip;
assert(ip > cf->code_start);
unsigned offset = ip - cf->code_start;
......@@ -663,7 +670,7 @@ FrameStackState getFrameStackState() {
const auto& locs = e.locations;
assert(locs.size() == 1);
uint64_t v = frame_iter.readLocation(locs[0]);
uint64_t v = frame_iter->readLocation(locs[0]);
if ((v & 1) == 0)
is_undefined.insert(p.first.substr(12));
......@@ -687,7 +694,7 @@ FrameStackState getFrameStackState() {
// printf("%s: %s\n", p.first.c_str(), e.type->debugName().c_str());
for (auto& loc : locs) {
vals.push_back(frame_iter.readLocation(loc));
vals.push_back(frame_iter->readLocation(loc));
}
Box* v = e.type->deserializeFromFrame(vals);
......@@ -701,156 +708,168 @@ FrameStackState getFrameStackState() {
abort();
}
return FrameStackState(d, frame_iter.getFrameInfo());
}
RELEASE_ASSERT(0, "Internal error: unable to find any python frames");
rtn = FrameStackState(d, frame_iter->getFrameInfo());
found = true;
return true;
});
RELEASE_ASSERT(found, "Internal error: unable to find any python frames");
return rtn;
}
Box* fastLocalsToBoxedLocals() {
for (PythonFrameIterator& frame_iter : unwindPythonFrames()) {
BoxedDict* d;
BoxedClosure* closure;
FrameInfo* frame_info;
return getPythonFrame(0).fastLocalsToBoxedLocals();
}
CompiledFunction* cf = frame_iter.getCF();
ScopeInfo* scope_info = cf->clfunc->source->getScopeInfo();
Box* PythonFrameIterator::fastLocalsToBoxedLocals() {
assert(impl.get());
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
return getGlobalsDict();
}
BoxedDict* d;
BoxedClosure* closure;
FrameInfo* frame_info;
if (frame_iter.getId().type == PythonFrameId::COMPILED) {
d = new BoxedDict();
CompiledFunction* cf = impl->getCF();
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);
unsigned offset = ip - cf->code_start;
if (impl->getId().type == PythonFrameId::COMPILED) {
d = new BoxedDict();
assert(cf->location_map);
uint64_t ip = impl->getId().ip;
// We have to detect + ignore any entries for variables that
// could have been defined (so they have entries) but aren't (so the
// entries point to uninitialized memory).
std::unordered_set<std::string> is_undefined;
assert(ip > cf->code_start);
unsigned offset = ip - cf->code_start;
for (const auto& p : cf->location_map->names) {
if (!startswith(p.first, "!is_defined_"))
continue;
assert(cf->location_map);
for (const LocationMap::LocationTable::LocationEntry& e : p.second.locations) {
if (e.offset < offset && offset <= e.offset + e.length) {
const auto& locs = e.locations;
// We have to detect + ignore any entries for variables that
// could have been defined (so they have entries) but aren't (so the
// entries point to uninitialized memory).
std::unordered_set<std::string> is_undefined;
assert(locs.size() == 1);
uint64_t v = frame_iter.readLocation(locs[0]);
if ((v & 1) == 0)
is_undefined.insert(p.first.substr(12));
for (const auto& p : cf->location_map->names) {
if (!startswith(p.first, "!is_defined_"))
continue;
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) {
if (p.first[0] == '!')
continue;
if (p.first[0] == '#')
continue;
for (const auto& p : cf->location_map->names) {
if (p.first[0] == '!')
continue;
if (is_undefined.count(p.first))
continue;
if (p.first[0] == '#')
continue;
for (const LocationMap::LocationTable::LocationEntry& e : p.second.locations) {
if (e.offset < offset && offset <= e.offset + e.length) {
const auto& locs = e.locations;
if (is_undefined.count(p.first))
continue;
llvm::SmallVector<uint64_t, 1> vals;
// printf("%s: %s\n", p.first.c_str(), e.type->debugName().c_str());
for (const LocationMap::LocationTable::LocationEntry& e : p.second.locations) {
if (e.offset < offset && offset <= e.offset + e.length) {
const auto& locs = e.locations;
for (auto& loc : locs) {
vals.push_back(frame_iter.readLocation(loc));
}
llvm::SmallVector<uint64_t, 1> vals;
// 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);
// 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;
for (auto& loc : locs) {
auto v = impl->readLocation(loc);
vals.push_back(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;
if (cf->location_map->names.count(PASSED_CLOSURE_NAME) > 0) {
for (const LocationMap::LocationTable::LocationEntry& e :
cf->location_map->names[PASSED_CLOSURE_NAME].locations) {
if (e.offset < offset && offset <= e.offset + e.length) {
const auto& locs = e.locations;
llvm::SmallVector<uint64_t, 1> vals;
closure = NULL;
if (cf->location_map->names.count(PASSED_CLOSURE_NAME) > 0) {
for (const LocationMap::LocationTable::LocationEntry& e :
cf->location_map->names[PASSED_CLOSURE_NAME].locations) {
if (e.offset < offset && offset <= e.offset + e.length) {
const auto& locs = e.locations;
for (auto& loc : locs) {
vals.push_back(frame_iter.readLocation(loc));
}
llvm::SmallVector<uint64_t, 1> vals;
Box* v = e.type->deserializeFromFrame(vals);
assert(gc::isValidGCObject(v));
closure = static_cast<BoxedClosure*>(v);
for (auto& loc : locs) {
vals.push_back(impl->readLocation(loc));
}
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);
if (frame_info->boxedLocals == NULL) {
frame_info->boxedLocals = new BoxedDict();
}
assert(gc::isValidGCObject(frame_info->boxedLocals));
// Add the locals from the closure
// 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);
}
}
frame_info = impl->getFrameInfo();
} else if (impl->getId().type == PythonFrameId::INTERPRETED) {
d = localsForInterpretedFrame((void*)impl->getId().bp, true);
closure = passedClosureForInterpretedFrame((void*)impl->getId().bp);
frame_info = getFrameInfoForInterpretedFrame((void*)impl->getId().bp);
} else {
abort();
}
// 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);
assert(frame_info);
if (frame_info->boxedLocals == NULL) {
frame_info->boxedLocals = new BoxedDict();
}
assert(gc::isValidGCObject(frame_info->boxedLocals));
// Add the locals from the closure
// 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() {
......@@ -860,6 +879,34 @@ ExecutionPoint getExecutionPoint() {
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() {
return new TracebacksEventListener();
}
......
......@@ -33,19 +33,47 @@ Box* getGlobalsDict(); // always returns a dict-like object
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.
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,
// calculating it if necessary (from previous frames).
ExcInfo* getFrameExcInfo();
struct ExecutionPoint {
CompiledFunction* cf;
AST_stmt* current_stmt;
};
ExecutionPoint getExecutionPoint();
struct FrameStackState {
// This includes all # variables (but not the ! ones).
// Therefore, it's not the same as the BoxedLocals.
......
......@@ -561,16 +561,29 @@ struct ExcInfo {
void printExcAndTraceback() const;
};
class BoxedFrame;
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*
// 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.
// - 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;
// This field is always initialized:
Box* boxedLocals;
FrameInfo(ExcInfo exc) : exc(exc), boxedLocals(NULL) {}
BoxedFrame* frame_obj;
FrameInfo(ExcInfo exc) : exc(exc), boxedLocals(NULL), frame_obj(0) {}
};
struct CallattrFlags {
......
......@@ -99,6 +99,22 @@ Box* getSysStdout() {
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() {
return boxStrConstant(PyUnicode_GetDefaultEncoding());
}
......@@ -281,6 +297,8 @@ void setupSys() {
main_fn = llvm::sys::fs::getMainExecutable(NULL, NULL);
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(
"getdefaultencoding",
new BoxedBuiltinFunctionOrMethod(boxRTFunction((void*)sysGetDefaultEncoding, STR, 0), "getdefaultencoding"));
......
......@@ -39,6 +39,16 @@ public:
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*) {
RELEASE_ASSERT(b->cls == code_cls, "");
......@@ -80,12 +90,18 @@ Box* codeForFunction(BoxedFunction* f) {
return new BoxedCode(f->f);
}
Box* codeForCLFunction(CLFunction* f) {
return new BoxedCode(f);
}
void setupCode() {
code_cls
= 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("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_varnames", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::varnames, 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
*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 you change this, you *must* update the value in _eh_frame_template
// (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) {
// TODO: ideally would be more intelligent about allocation strategies.
// The code sections should be together and the eh sections together
eh_frame_addr = malloc(EH_FRAME_SIZE);
writeTrivialEhFrame(eh_frame_addr, addr, total_size);
registerEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
eh_frame.writeAndRegister(addr, total_size);
} else {
addr = func_addr;
}
......@@ -268,9 +279,7 @@ RuntimeIC::RuntimeIC(void* func_addr, int num_slots, int slot_size) {
RuntimeIC::~RuntimeIC() {
if (ENABLE_RUNTIME_ICS) {
deregisterCompiledPatchpoint(icinfo.get());
deregisterEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
free(addr);
free(eh_frame_addr);
} else {
}
}
......
......@@ -22,10 +22,20 @@ namespace pyston {
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 {
private:
void* addr;
void* eh_frame_addr;
EHFrameManager eh_frame;
std::unique_ptr<ICInfo> icinfo;
......
......@@ -2146,6 +2146,7 @@ void setupRuntime() {
setupDescr();
setupTraceback();
setupCode();
setupFrame();
function_cls->giveAttr("__dict__", dict_descr);
function_cls->giveAttr("__name__", new (pyston_getset_cls) BoxedGetsetDescriptor(funcName, funcSetName, NULL));
......
......@@ -68,6 +68,7 @@ void setupGenerator();
void setupDescr();
void teardownDescr();
void setupCode();
void setupFrame();
void setupSys();
void setupBuiltins();
......@@ -815,6 +816,9 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
extern Box* dict_descr;
Box* codeForFunction(BoxedFunction*);
Box* codeForCLFunction(CLFunction*);
Box* getFrame(int depth);
}
#endif
......@@ -15,7 +15,7 @@ d[2].append(3)
print sorted(d.items())
NT = collections.namedtuple("NT", ["field1", "field2"])
print NT.__name__
print NT.__name__, NT
n = NT(1, "hi")
print n.field1, n.field2, len(n), list(n), n[0], n[-1]
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