Compare commits

...

2 Commits

Author SHA1 Message Date
blueloveTH
483e6fcb57 implement tracefunc 2025-03-06 00:48:16 +08:00
blueloveTH
64a4ae4676 add TraceInfo 2025-03-06 00:05:10 +08:00
9 changed files with 94 additions and 73 deletions

View File

@ -40,6 +40,11 @@ typedef struct Frame {
UnwindTarget* uw_list; UnwindTarget* uw_list;
} Frame; } Frame;
typedef struct SourceLocation {
SourceData_ src;
int lineno;
} SourceLocation;
Frame* Frame__new(const CodeObject* co, Frame* Frame__new(const CodeObject* co,
py_StackRef p0, py_StackRef p0,
py_GlobalRef module, py_GlobalRef module,
@ -63,4 +68,5 @@ int Frame__prepare_jump_exception_handler(Frame* self, ValueStack*);
UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock); UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock);
void Frame__set_unwind_target(Frame* self, py_TValue* sp); void Frame__set_unwind_target(Frame* self, py_TValue* sp);
void Frame__gc_mark(Frame* self); void Frame__gc_mark(Frame* self);
SourceLocation Frame__source_location(Frame* self);

View File

@ -18,6 +18,11 @@
// 5. stack effect of each opcode // 5. stack effect of each opcode
// 6. py_TypeInfo // 6. py_TypeInfo
typedef struct TraceInfo {
SourceLocation prev_loc;
py_TraceFunc tracefunc;
} TraceInfo;
typedef struct VM { typedef struct VM {
Frame* top_frame; Frame* top_frame;
@ -38,9 +43,10 @@ typedef struct VM {
py_TValue reg[8]; // users' registers py_TValue reg[8]; // users' registers
void* ctx; // user-defined context void* ctx; // user-defined context
py_StackRef __curr_class; py_StackRef curr_class;
py_StackRef __curr_function; py_StackRef curr_function;
py_TValue __vectorcall_buffer[PK_MAX_CO_VARNAMES]; TraceInfo trace_info;
py_TValue vectorcall_buffer[PK_MAX_CO_VARNAMES];
InternedNames names; InternedNames names;
FixedMemoryPool pool_frame; FixedMemoryPool pool_frame;

View File

@ -34,45 +34,6 @@ typedef struct c11_sv {
int size; int size;
} c11_sv; } c11_sv;
// An enum for tracing events.
enum py_TraceEvent {
TraceEvent_Line,
TraceEvent_Call,
TraceEvent_Return,
TraceEvent_Exception,
};
/// A struct contains the arguments of the tracing event.
union py_TraceEventArg {
struct {
int _;
} line;
struct {
int _;
} call;
struct {
int _;
} return_;
struct {
int _;
} exception;
};
/// A struct contains the callbacks of the VM.
typedef struct py_Callbacks {
/// Used by `__import__` to load source code of a module.
char* (*importfile)(const char*);
/// Used by `print` to output a string.
void (*print)(const char*);
/// Used by `input` to get a character.
int (*getchar)();
/// C-style `sys.settrace` function.
void (*tracefunc)(enum py_TraceEvent, union py_TraceEventArg);
} py_Callbacks;
#define PY_RAISE #define PY_RAISE
#define PY_RETURN #define PY_RETURN
@ -89,6 +50,28 @@ typedef py_TValue* py_ItemRef;
/// An output reference for returning a value. /// An output reference for returning a value.
typedef py_TValue* py_OutRef; typedef py_TValue* py_OutRef;
typedef struct py_Frame py_Frame;
// An enum for tracing events.
enum py_TraceEvent {
TRACE_EVENT_LINE,
TRACE_EVENT_CALL,
TRACE_EVENT_RETURN,
TRACE_EVENT_EXCEPTION,
};
typedef void (*py_TraceFunc)(py_Frame* frame, enum py_TraceEvent);
/// A struct contains the callbacks of the VM.
typedef struct py_Callbacks {
/// Used by `__import__` to load source code of a module.
char* (*importfile)(const char*);
/// Used by `print` to output a string.
void (*print)(const char*);
/// Used by `input` to get a character.
int (*getchar)();
} py_Callbacks;
/// Native function signature. /// Native function signature.
/// @param argc number of arguments. /// @param argc number of arguments.
/// @param argv array of arguments. Use `py_arg(i)` macro to get the i-th argument. /// @param argv array of arguments. Use `py_arg(i)` macro to get the i-th argument.
@ -120,6 +103,8 @@ PK_API void* py_getvmctx();
PK_API void py_setvmctx(void* ctx); PK_API void py_setvmctx(void* ctx);
/// Set `sys.argv`. Used for storing command-line arguments. /// Set `sys.argv`. Used for storing command-line arguments.
PK_API void py_sys_setargv(int argc, char** argv); PK_API void py_sys_setargv(int argc, char** argv);
/// Set the trace function for the current VM.
PK_API void py_sys_settrace(py_TraceFunc func);
/// Setup the callbacks for the current VM. /// Setup the callbacks for the current VM.
PK_API py_Callbacks* py_callbacks(); PK_API py_Callbacks* py_callbacks();

View File

@ -1046,7 +1046,7 @@ void CallExpr__emit_(Expr* self_, Ctx* ctx) {
Opcode opcode = OP_CALL; Opcode opcode = OP_CALL;
if(vargs || vkwargs) { if(vargs || vkwargs) {
// in this case, there is at least one *args or **kwargs as StarredExpr // in this case, there is at least one *args or **kwargs as StarredExpr
// OP_CALL_VARGS needs to unpack them via __vectorcall_buffer // OP_CALL_VARGS needs to unpack them via vectorcall_buffer
opcode = OP_CALL_VARGS; opcode = OP_CALL_VARGS;
} }

View File

@ -97,12 +97,19 @@ FrameResult VM__run_top_frame(VM* self) {
frame->ip++; frame->ip++;
__NEXT_STEP: __NEXT_STEP:
if(self->callbacks.tracefunc) {
// TODO: implement tracing mechanism
}
byte = codes[frame->ip]; byte = codes[frame->ip];
if(self->trace_info.tracefunc) {
SourceLocation loc = Frame__source_location(frame);
SourceLocation prev_loc = self->trace_info.prev_loc;
if(loc.lineno != prev_loc.lineno || loc.src != prev_loc.src) {
if(prev_loc.src) PK_DECREF(prev_loc.src);
PK_INCREF(loc.src);
self->trace_info.prev_loc = loc;
self->trace_info.tracefunc((py_Frame*)frame, TRACE_EVENT_LINE);
}
}
#ifndef NDEBUG #ifndef NDEBUG
pk_print_stack(self, frame, byte); pk_print_stack(self, frame, byte);
#endif #endif
@ -298,9 +305,9 @@ FrameResult VM__run_top_frame(VM* self) {
DISPATCH(); DISPATCH();
} }
case OP_LOAD_CLASS_GLOBAL: { case OP_LOAD_CLASS_GLOBAL: {
assert(self->__curr_class); assert(self->curr_class);
py_Name name = byte.arg; py_Name name = byte.arg;
py_Ref tmp = py_getdict(self->__curr_class, name); py_Ref tmp = py_getdict(self->curr_class, name);
if(tmp) { if(tmp) {
PUSH(tmp); PUSH(tmp);
DISPATCH(); DISPATCH();
@ -691,7 +698,7 @@ FrameResult VM__run_top_frame(VM* self) {
py_TValue* sp = SP(); py_TValue* sp = SP();
py_TValue* p1 = sp - kwargc * 2; py_TValue* p1 = sp - kwargc * 2;
py_TValue* base = p1 - argc; py_TValue* base = p1 - argc;
py_TValue* buf = self->__vectorcall_buffer; py_TValue* buf = self->vectorcall_buffer;
for(py_TValue* curr = base; curr != p1; curr++) { for(py_TValue* curr = base; curr != p1; curr++) {
if(curr->type != tp_star_wrapper) { if(curr->type != tp_star_wrapper) {
@ -1008,7 +1015,7 @@ FrameResult VM__run_top_frame(VM* self) {
base_ti->is_python, base_ti->is_python,
false); false);
PUSH(py_tpobject(type)); PUSH(py_tpobject(type));
self->__curr_class = TOP(); self->curr_class = TOP();
DISPATCH(); DISPATCH();
} }
case OP_END_CLASS: { case OP_END_CLASS: {
@ -1033,11 +1040,11 @@ FrameResult VM__run_top_frame(VM* self) {
// class with decorator is unsafe currently // class with decorator is unsafe currently
// it skips the above check // it skips the above check
POP(); POP();
self->__curr_class = NULL; self->curr_class = NULL;
DISPATCH(); DISPATCH();
} }
case OP_STORE_CLASS_ATTR: { case OP_STORE_CLASS_ATTR: {
assert(self->__curr_class); assert(self->curr_class);
py_Name name = byte.arg; py_Name name = byte.arg;
// TOP() can be a function, classmethod or custom decorator // TOP() can be a function, classmethod or custom decorator
py_Ref actual_func = TOP(); py_Ref actual_func = TOP();
@ -1046,16 +1053,16 @@ FrameResult VM__run_top_frame(VM* self) {
} }
if(actual_func->type == tp_function) { if(actual_func->type == tp_function) {
Function* ud = py_touserdata(actual_func); Function* ud = py_touserdata(actual_func);
ud->clazz = self->__curr_class->_obj; ud->clazz = self->curr_class->_obj;
} }
py_setdict(self->__curr_class, name, TOP()); py_setdict(self->curr_class, name, TOP());
POP(); POP();
DISPATCH(); DISPATCH();
} }
case OP_ADD_CLASS_ANNOTATION: { case OP_ADD_CLASS_ANNOTATION: {
assert(self->__curr_class); assert(self->curr_class);
// [type_hint string] // [type_hint string]
py_Type type = py_totype(self->__curr_class); py_Type type = py_totype(self->curr_class);
py_TypeInfo* ti = TypeList__get(&self->types, type); py_TypeInfo* ti = TypeList__get(&self->types, type);
if(py_isnil(&ti->annotations)) py_newdict(&ti->annotations); if(py_isnil(&ti->annotations)) py_newdict(&ti->annotations);
bool ok = py_dict_setitem_by_str(&ti->annotations, py_name2str(byte.arg), TOP()); bool ok = py_dict_setitem_by_str(&ti->annotations, py_name2str(byte.arg), TOP());
@ -1439,4 +1446,14 @@ static bool stack_format_object(VM* self, c11_sv spec) {
#undef POPX #undef POPX
#undef SP #undef SP
#undef INSERT_THIRD #undef INSERT_THIRD
#undef vectorcall_opcall #undef vectorcall_opcall
void py_sys_settrace(py_TraceFunc func) {
TraceInfo* info = &pk_current_vm->trace_info;
info->tracefunc = func;
if(info->prev_loc.src) {
PK_DECREF(info->prev_loc.src);
info->prev_loc.src = NULL;
}
info->prev_loc.lineno = -1;
}

View File

@ -173,4 +173,11 @@ py_Ref Frame__getclosure(Frame* self, py_Name name) {
Function* ud = py_touserdata(self->p0); Function* ud = py_touserdata(self->p0);
if(ud->closure == NULL) return NULL; if(ud->closure == NULL) return NULL;
return NameDict__try_get(ud->closure, name); return NameDict__try_get(ud->closure, name);
}
SourceLocation Frame__source_location(Frame* self) {
SourceLocation loc;
loc.lineno = Frame__lineno(self);
loc.src = self->co->src;
return loc;
} }

View File

@ -68,15 +68,15 @@ void VM__ctor(VM* self) {
self->callbacks.importfile = pk_default_importfile; self->callbacks.importfile = pk_default_importfile;
self->callbacks.print = pk_default_print; self->callbacks.print = pk_default_print;
self->callbacks.getchar = getchar; self->callbacks.getchar = getchar;
self->callbacks.tracefunc = NULL;
self->last_retval = *py_NIL(); self->last_retval = *py_NIL();
self->curr_exception = *py_NIL(); self->curr_exception = *py_NIL();
self->is_curr_exc_handled = false; self->is_curr_exc_handled = false;
self->ctx = NULL; self->ctx = NULL;
self->__curr_class = NULL; self->curr_class = NULL;
self->__curr_function = NULL; self->curr_function = NULL;
memset(&self->trace_info, 0, sizeof(TraceInfo));
FixedMemoryPool__ctor(&self->pool_frame, sizeof(Frame), 32); FixedMemoryPool__ctor(&self->pool_frame, sizeof(Frame), 32);
@ -479,11 +479,11 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
switch(fn->decl->type) { switch(fn->decl->type) {
case FuncType_NORMAL: { case FuncType_NORMAL: {
bool ok = prepare_py_call(self->__vectorcall_buffer, argv, p1, kwargc, fn->decl); bool ok = prepare_py_call(self->vectorcall_buffer, argv, p1, kwargc, fn->decl);
if(!ok) return RES_ERROR; if(!ok) return RES_ERROR;
// copy buffer back to stack // copy buffer back to stack
self->stack.sp = argv + co->nlocals; self->stack.sp = argv + co->nlocals;
memcpy(argv, self->__vectorcall_buffer, co->nlocals * sizeof(py_TValue)); memcpy(argv, self->vectorcall_buffer, co->nlocals * sizeof(py_TValue));
// submit the call // submit the call
if(!fn->cfunc) { if(!fn->cfunc) {
// python function // python function
@ -491,10 +491,10 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
return opcall ? RES_CALL : VM__run_top_frame(self); return opcall ? RES_CALL : VM__run_top_frame(self);
} else { } else {
// decl-based binding // decl-based binding
self->__curr_function = p0; self->curr_function = p0;
bool ok = py_callcfunc(fn->cfunc, co->nlocals, argv); bool ok = py_callcfunc(fn->cfunc, co->nlocals, argv);
self->stack.sp = p0; self->stack.sp = p0;
self->__curr_function = NULL; self->curr_function = NULL;
return ok ? RES_RETURN : RES_ERROR; return ok ? RES_RETURN : RES_ERROR;
} }
} }
@ -520,18 +520,18 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
return opcall ? RES_CALL : VM__run_top_frame(self); return opcall ? RES_CALL : VM__run_top_frame(self);
} else { } else {
// decl-based binding // decl-based binding
self->__curr_function = p0; self->curr_function = p0;
bool ok = py_callcfunc(fn->cfunc, co->nlocals, argv); bool ok = py_callcfunc(fn->cfunc, co->nlocals, argv);
self->stack.sp = p0; self->stack.sp = p0;
self->__curr_function = NULL; self->curr_function = NULL;
return ok ? RES_RETURN : RES_ERROR; return ok ? RES_RETURN : RES_ERROR;
} }
case FuncType_GENERATOR: { case FuncType_GENERATOR: {
bool ok = prepare_py_call(self->__vectorcall_buffer, argv, p1, kwargc, fn->decl); bool ok = prepare_py_call(self->vectorcall_buffer, argv, p1, kwargc, fn->decl);
if(!ok) return RES_ERROR; if(!ok) return RES_ERROR;
// copy buffer back to stack // copy buffer back to stack
self->stack.sp = argv + co->nlocals; self->stack.sp = argv + co->nlocals;
memcpy(argv, self->__vectorcall_buffer, co->nlocals * sizeof(py_TValue)); memcpy(argv, self->vectorcall_buffer, co->nlocals * sizeof(py_TValue));
Frame* frame = Frame__new(co, p0, fn->module, fn->globals, argv, false); Frame* frame = Frame__new(co, p0, fn->module, fn->globals, argv, false);
pk_newgenerator(py_retval(), frame, p0, self->stack.sp); pk_newgenerator(py_retval(), frame, p0, self->stack.sp);
self->stack.sp = p0; // reset the stack self->stack.sp = p0; // reset the stack

View File

@ -154,8 +154,8 @@ void py_clearexc(py_StackRef p0) {
vm->curr_exception = *py_NIL(); vm->curr_exception = *py_NIL();
vm->is_curr_exc_handled = false; vm->is_curr_exc_handled = false;
/* Don't clear this, because StopIteration() may corrupt the class definition */ /* Don't clear this, because StopIteration() may corrupt the class definition */
// vm->__curr_class = NULL; // vm->curr_class = NULL;
vm->__curr_function = NULL; vm->curr_function = NULL;
if(p0) vm->stack.sp = p0; if(p0) vm->stack.sp = p0;
} }

View File

@ -69,7 +69,7 @@ void py_setslot(py_Ref self, int i, py_Ref val) {
} }
py_StackRef py_inspect_currentfunction(){ py_StackRef py_inspect_currentfunction(){
return pk_current_vm->__curr_function; return pk_current_vm->curr_function;
} }
py_GlobalRef py_inspect_currentmodule(){ py_GlobalRef py_inspect_currentmodule(){