From e288cb35c3008a27ac5366501da6ca2d3a15a221 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 22 Jun 2022 10:36:41 +1000 Subject: [PATCH 1/5] py/objfun: Add function.__code__ attribute. Only if MICROPY_PY_SYS_SETTRACE is enabled. This is used to provide introspection of attributes such as function name or source file & line. Signed-off-by: Andrew Leech --- py/objfun.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/py/objfun.c b/py/objfun.c index a742c5267254c..f71f7c9ee32bb 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -34,7 +34,8 @@ #include "py/objfun.h" #include "py/runtime.h" #include "py/bc.h" -#include "py/cstack.h" +#include "py/stackctrl.h" +#include "py/profile.h" #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) @@ -366,17 +367,13 @@ void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); dest[0] = MP_OBJ_FROM_PTR(self->context->module.globals); } - #if MICROPY_PY_FUNCTION_ATTRS_CODE + + #if MICROPY_PY_SYS_SETTRACE if (attr == MP_QSTR___code__) { - const mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); - if ((self->base.type == &mp_type_fun_bc - || self->base.type == &mp_type_gen_wrap) - && self->child_table == NULL) { - #if MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_BASIC - dest[0] = mp_obj_new_code(self->context->constants, self->bytecode); - #else - dest[0] = mp_obj_new_code(self->context, self->rc, true); - #endif + mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t code_obj = mp_obj_new_code(self->context, self->rc, false); + if (code_obj != MP_OBJ_NULL) { + dest[0] = code_obj; } } #endif From a05fa05826df24fc41e1a3da6547344ef4d0bfb5 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Mon, 21 Aug 2023 16:09:39 +0930 Subject: [PATCH 2/5] unix/standard: Enable sys.settrace() by default. Signed-off-by: Andrew Leech --- ports/unix/variants/standard/mpconfigvariant.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/unix/variants/standard/mpconfigvariant.h b/ports/unix/variants/standard/mpconfigvariant.h index 75201e9abc8d6..447832a7656b6 100644 --- a/ports/unix/variants/standard/mpconfigvariant.h +++ b/ports/unix/variants/standard/mpconfigvariant.h @@ -27,5 +27,7 @@ // Set base feature level. #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) +#define MICROPY_PY_SYS_SETTRACE (1) + // Enable extra Unix features. #include "../mpconfigvariant_common.h" From 8d3dce03b90ae4d342b51463da5aa94b6c7d4e8e Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 16 Jun 2022 20:33:48 +1000 Subject: [PATCH 3/5] sys/settrace: Add sys._getframe() function. Refer to https://docs.python.org/3/library/sys.html#sys._getframe Signed-off-by: Andrew Leech --- py/modsys.c | 12 +++++++++ py/profile.c | 51 ++++++++++++++++++++++++++++++++++--- py/profile.h | 3 +++ tests/misc/sys__getframe.py | 27 ++++++++++++++++++++ 4 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 tests/misc/sys__getframe.py diff --git a/py/modsys.c b/py/modsys.c index 9ab02293b9063..4b7cd0d22ff3d 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -238,6 +238,16 @@ static mp_obj_t mp_sys_settrace(mp_obj_t obj) { return mp_prof_settrace(obj); } MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_settrace_obj, mp_sys_settrace); + +// _getframe(): Return current frame object. +static mp_obj_t mp_sys__getframe(size_t n_args, const mp_obj_t *args) { + size_t depth = 0; + if (n_args == 1) { + depth = mp_obj_get_int(args[0]); + } + return mp_prof_get_frame(depth); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_getframe_obj, 0, 1, mp_sys__getframe); #endif // MICROPY_PY_SYS_SETTRACE #if MICROPY_PY_SYS_PATH && !MICROPY_PY_SYS_ATTR_DELEGATION @@ -322,6 +332,8 @@ static const mp_rom_map_elem_t mp_module_sys_globals_table[] = { #if MICROPY_PY_SYS_SETTRACE { MP_ROM_QSTR(MP_QSTR_settrace), MP_ROM_PTR(&mp_sys_settrace_obj) }, + { MP_ROM_QSTR(MP_QSTR_gettrace), MP_ROM_PTR(&mp_sys_gettrace_obj) }, + { MP_ROM_QSTR(MP_QSTR__getframe), MP_ROM_PTR(&mp_sys_getframe_obj) }, #endif #if MICROPY_PY_SYS_STDFILES diff --git a/py/profile.c b/py/profile.c index 397d0291f9fad..4b813bb0d7b06 100644 --- a/py/profile.c +++ b/py/profile.c @@ -86,17 +86,26 @@ static void frame_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t } static void frame_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { - if (dest[0] != MP_OBJ_NULL) { + mp_obj_frame_t *o = MP_OBJ_TO_PTR(self_in); + + if (dest[0] == MP_OBJ_SENTINEL) { + // store attr + switch (attr) { + case MP_QSTR_f_trace: + o->f_trace = dest[1]; + dest[0] = MP_OBJ_NULL; + break; + } + return; + } else if (dest[0] != MP_OBJ_NULL) { // not load attribute return; } - mp_obj_frame_t *o = MP_OBJ_TO_PTR(self_in); - switch (attr) { case MP_QSTR_f_back: dest[0] = mp_const_none; - if (o->code_state->prev_state) { + if (o->code_state->prev_state && o->code_state->prev_state->frame) { dest[0] = MP_OBJ_FROM_PTR(o->code_state->prev_state->frame); } break; @@ -112,6 +121,12 @@ static void frame_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { case MP_QSTR_f_lineno: dest[0] = MP_OBJ_NEW_SMALL_INT(o->lineno); break; + case MP_QSTR_f_trace: + dest[0] = o->f_trace; + break; + case MP_QSTR_f_locals: + dest[0] = mp_obj_new_dict(0); + break; } } @@ -148,6 +163,7 @@ mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state) { o->lineno = mp_prof_bytecode_lineno(rc, o->lasti); o->trace_opcodes = false; o->callback = MP_OBJ_NULL; + o->f_trace = MP_OBJ_NULL; return MP_OBJ_FROM_PTR(o); } @@ -187,6 +203,33 @@ mp_obj_t mp_prof_settrace(mp_obj_t callback) { return mp_const_none; } +mp_obj_t mp_prof_gettrace(void) { + if (prof_trace_cb == MP_OBJ_NULL) { + return mp_const_none; + } + return prof_trace_cb; +} + +mp_obj_t mp_prof_get_frame(size_t depth) { + + mp_code_state_t *code_state = MP_STATE_THREAD(current_code_state); + + for (size_t i = 0; i < depth; i++) { + code_state = code_state->prev_state; + if (code_state == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("call stack is not deep enough")); + } + } + + mp_obj_frame_t *frame = MP_OBJ_TO_PTR(mp_obj_new_frame(code_state)); + if (frame == NULL) { + // Couldn't allocate a frame object + return MP_OBJ_NULL; + } + + return MP_OBJ_FROM_PTR(frame); +} + mp_obj_t mp_prof_frame_enter(mp_code_state_t *code_state) { assert(!mp_prof_is_executing); diff --git a/py/profile.h b/py/profile.h index db72b9f076818..352deb34e23bc 100644 --- a/py/profile.h +++ b/py/profile.h @@ -43,6 +43,7 @@ typedef struct _mp_obj_frame_t { mp_uint_t lasti; mp_uint_t lineno; bool trace_opcodes; + mp_obj_t f_trace; } mp_obj_frame_t; uint mp_prof_bytecode_lineno(const mp_raw_code_t *rc, size_t bc); @@ -52,7 +53,9 @@ mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state); // This is the implementation for the sys.settrace mp_obj_t mp_prof_settrace(mp_obj_t callback); +mp_obj_t mp_prof_gettrace(void); +mp_obj_t mp_prof_get_frame(size_t depth); mp_obj_t mp_prof_frame_enter(mp_code_state_t *code_state); mp_obj_t mp_prof_frame_update(const mp_code_state_t *code_state); diff --git a/tests/misc/sys__getframe.py b/tests/misc/sys__getframe.py new file mode 100644 index 0000000000000..800921a41d493 --- /dev/null +++ b/tests/misc/sys__getframe.py @@ -0,0 +1,27 @@ +import sys + +try: + sys._getframe +except AttributeError: + print("SKIP") + raise SystemExit + +top_frame = sys._getframe() + +print(top_frame.f_code.co_name == "") + + +def new_frame(): + curr_frame = sys._getframe() + prev_frame = sys._getframe(1) + + print(curr_frame.f_code.co_name == "new_frame") + + print(prev_frame.f_code.co_name == "") + print(curr_frame.f_back.f_code.co_name == "") + + print(prev_frame.f_lineno == curr_frame.f_back.f_lineno) + print(prev_frame.f_code.co_filename == curr_frame.f_back.f_code.co_filename) + + +new_frame() From 872b5d9c5eded56550e4a5d8fbab0b18e9e27432 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 11 Jun 2025 05:55:56 +1000 Subject: [PATCH 4/5] sys/gettrace: Add gettrace function to mirror settrace. Refer to https://docs.python.org/3/library/sys.html#sys.gettrace Signed-off-by: Andrew Leech --- py/modsys.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/py/modsys.c b/py/modsys.c index 4b7cd0d22ff3d..3ce14bf5d17ad 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -239,6 +239,11 @@ static mp_obj_t mp_sys_settrace(mp_obj_t obj) { } MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_settrace_obj, mp_sys_settrace); +static mp_obj_t mp_sys_gettrace() { + return mp_prof_gettrace(); +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_sys_gettrace_obj, mp_sys_gettrace); + // _getframe(): Return current frame object. static mp_obj_t mp_sys__getframe(size_t n_args, const mp_obj_t *args) { size_t depth = 0; From a7ae5224c72c16b7853ea8d4e447e111aa75b7cf Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 11 Jun 2025 05:55:56 +1000 Subject: [PATCH 5/5] lib/micropython-lib: Update submodule to include debugpy support. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates micropython-lib submodule to include the new debugpy implementation that enables VS Code debugging support for MicroPython applications. The debugpy port provides: - Debug Adapter Protocol (DAP) compatibility with VS Code - Line breakpoints, stepping, and variable inspection - Integration with MicroPython's sys.settrace functionality - Network-based debugging via TCP socket connection This enables developers to debug MicroPython code using the familiar VS Code debugging interface, improving the development experience. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- lib/micropython-lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/micropython-lib b/lib/micropython-lib index 5b496e944ec04..f345a0db99b9d 160000 --- a/lib/micropython-lib +++ b/lib/micropython-lib @@ -1 +1 @@ -Subproject commit 5b496e944ec045177afa1620920a168410b7f60b +Subproject commit f345a0db99b9dc454ffcbfb13bfb68978467018d