diff --git a/lib/nxp_driver b/lib/nxp_driver index fa5a554c7944d..72fa6068dbfb6 160000 --- a/lib/nxp_driver +++ b/lib/nxp_driver @@ -1 +1 @@ -Subproject commit fa5a554c7944d2a196626f8d3631e44943f9abcc +Subproject commit 72fa6068dbfb6080272227f589352f6da6627fdb diff --git a/lib/protobuf-c b/lib/protobuf-c index abc67a11c6db2..4719fdd776062 160000 --- a/lib/protobuf-c +++ b/lib/protobuf-c @@ -1 +1 @@ -Subproject commit abc67a11c6db271bedbb9f58be85d6f4e2ea8389 +Subproject commit 4719fdd7760624388c2c5b9d6759eb6a47490626 diff --git a/lib/wiznet5k b/lib/wiznet5k index 0803fc519ad72..ce4a7b6d07541 160000 --- a/lib/wiznet5k +++ b/lib/wiznet5k @@ -1 +1 @@ -Subproject commit 0803fc519ad7227e841287fb3638d6c8b2f111a1 +Subproject commit ce4a7b6d07541bf0ba9f91e369276b38faa619bd diff --git a/ports/unix/variants/standard/mpconfigvariant.h b/ports/unix/variants/standard/mpconfigvariant.h index 75201e9abc8d6..024b08fec7245 100644 --- a/ports/unix/variants/standard/mpconfigvariant.h +++ b/ports/unix/variants/standard/mpconfigvariant.h @@ -27,5 +27,9 @@ // Set base feature level. #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) +// #define MICROPY_PY_SYS_SETTRACE (1) +// #define MICROPY_PY_SYS_SETTRACE_SAVE_NAMES (1) // Save local variable names for debugging + + // Enable extra Unix features. #include "../mpconfigvariant_common.h" diff --git a/py/compile.c b/py/compile.c index 7a1151bcd66f0..219fcf57eecab 100644 --- a/py/compile.c +++ b/py/compile.c @@ -38,6 +38,9 @@ #include "py/nativeglue.h" #include "py/persistentcode.h" #include "py/smallint.h" +#if MICROPY_PY_SYS_SETTRACE_SAVE_NAMES +#include "py/localnames.h" +#endif #if MICROPY_ENABLE_COMPILER @@ -3435,6 +3438,21 @@ static void scope_compute_things(scope_t *scope) { } } + #if MICROPY_PY_SYS_SETTRACE_SAVE_NAMES + // Save the local variable names in the raw_code for debugging + if (SCOPE_IS_FUNC_LIKE(scope->kind) && scope->num_locals > 0) { + // Initialize local_names structure with variable names + for (int i = 0; i < scope->id_info_len; i++) { + id_info_t *id = &scope->id_info[i]; + if ((id->kind == ID_INFO_KIND_LOCAL || id->kind == ID_INFO_KIND_CELL) && + id->local_num < scope->num_locals && + id->local_num < MICROPY_PY_SYS_SETTRACE_NAMES_MAX) { + mp_local_names_add(&scope->raw_code->local_names, id->local_num, id->qst); + } + } + } + #endif + // compute the index of free vars // make sure they are in the order of the parent scope if (scope->parent != NULL) { diff --git a/py/emitglue.c b/py/emitglue.c index 27cbb349ef602..a1ec52f11a74f 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -57,6 +57,9 @@ mp_raw_code_t *mp_emit_glue_new_raw_code(void) { #if MICROPY_PY_SYS_SETTRACE rc->line_of_definition = 0; #endif + #if MICROPY_PY_SYS_SETTRACE_SAVE_NAMES + mp_local_names_init(&rc->local_names); + #endif return rc; } diff --git a/py/emitglue.h b/py/emitglue.h index 126462671b003..1c2fffd23f7eb 100644 --- a/py/emitglue.h +++ b/py/emitglue.h @@ -28,6 +28,9 @@ #include "py/obj.h" #include "py/bc.h" +#if MICROPY_PY_SYS_SETTRACE_SAVE_NAMES +#include "py/localnames.h" +#endif // These variables and functions glue the code emitters to the runtime. @@ -96,6 +99,9 @@ typedef struct _mp_raw_code_t { uint32_t asm_n_pos_args : 8; uint32_t asm_type_sig : 24; // compressed as 2-bit types; ret is MSB, then arg0, arg1, etc #endif + #if MICROPY_PY_SYS_SETTRACE_SAVE_NAMES + mp_local_names_t local_names; // Maps local variable indices to names + #endif } mp_raw_code_t; // Version of mp_raw_code_t but without the asm_n_pos_args/asm_type_sig entries, which are diff --git a/py/localnames.c b/py/localnames.c new file mode 100644 index 0000000000000..90bbe01adf4e8 --- /dev/null +++ b/py/localnames.c @@ -0,0 +1,112 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Jos Verlinde + * + * Permission is hereby granted, free + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/localnames.h" + +#if MICROPY_PY_SYS_SETTRACE_SAVE_NAMES + +// Initialize the local names structure +void mp_local_names_init(mp_local_names_t *local_names) { + if (local_names == NULL) { + return; + } + + local_names->num_locals = 0; + local_names->order_count = 0; + + // Initialize all entries with null qstrs and invalid indices + for (uint16_t i = 0; i < MICROPY_PY_SYS_SETTRACE_NAMES_MAX; i++) { + local_names->local_names[i] = MP_QSTRnull; + local_names->local_nums[i] = UINT16_MAX; // Invalid index marker + } +} + +// Get the name of a local variable by its index +qstr mp_local_names_get_name(const mp_local_names_t *local_names, uint16_t local_num) { + if (local_names == NULL || local_num >= MICROPY_PY_SYS_SETTRACE_NAMES_MAX) { + return MP_QSTRnull; + } + + // Direct array access + return local_names->local_names[local_num]; +} + +// Look up the original local_num by order index (source code order) +uint16_t mp_local_names_get_local_num(const mp_local_names_t *local_names, uint16_t order_idx) { + if (local_names == NULL || order_idx >= local_names->order_count) { + return UINT16_MAX; // Invalid index + } + + return local_names->local_nums[order_idx]; +} + +// Add or update a name mapping for a local variable +void mp_local_names_add(mp_local_names_t *local_names, uint16_t local_num, qstr qstr_name) { + if (local_names == NULL || local_num >= MICROPY_PY_SYS_SETTRACE_NAMES_MAX) { + return; + } + + // Store name directly using local_num as index + local_names->local_names[local_num] = qstr_name; + + // Update number of locals if needed + if (local_num >= local_names->num_locals) { + local_names->num_locals = local_num + 1; + } + + // Also store in order of definition for correct runtime mapping + if (local_names->order_count < MICROPY_PY_SYS_SETTRACE_NAMES_MAX) { + uint16_t idx = local_names->order_count; + local_names->local_nums[idx] = local_num; + local_names->order_count++; + } + + // Refine runtime slot mapping logic + // Test the hypothesis that variables are assigned from highest slots down + uint16_t runtime_slot = local_num; // Default to direct mapping + + if (local_names->order_count > 0) { + // Find position in order array + for (uint16_t i = 0; i < local_names->order_count; ++i) { + if (local_names->local_nums[i] == local_num) { + runtime_slot = i; + break; + } + } + } + local_names->runtime_slots[local_num] = runtime_slot; +} + +// Get the runtime slot for a local variable by its index +uint16_t mp_local_names_get_runtime_slot(const mp_local_names_t *local_names, uint16_t local_num) { + if (local_names == NULL || local_num >= MICROPY_PY_SYS_SETTRACE_NAMES_MAX) { + return UINT16_MAX; // Invalid slot + } + return local_names->runtime_slots[local_num]; +} + +#endif // MICROPY_PY_SYS_SETTRACE_SAVE_NAMES diff --git a/py/localnames.h b/py/localnames.h new file mode 100644 index 0000000000000..fc968c56b6ab0 --- /dev/null +++ b/py/localnames.h @@ -0,0 +1,63 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Contributors to the MicroPython project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_PY_LOCALNAMES_H +#define MICROPY_INCLUDED_PY_LOCALNAMES_H + +#include "py/obj.h" +#include "py/qstr.h" + +#if MICROPY_PY_SYS_SETTRACE_SAVE_NAMES + +#define MICROPY_PY_SYS_SETTRACE_NAMES_MAX 32 // Maximum number of local variables to store names for + +// Structure to hold variable name mappings for a function scope +typedef struct _mp_local_names_t { + uint16_t num_locals; // Total number of local variables with names + qstr local_names[MICROPY_PY_SYS_SETTRACE_NAMES_MAX]; // Array of variable names, indexed by local_num + uint16_t local_nums[MICROPY_PY_SYS_SETTRACE_NAMES_MAX]; // Reverse mapping: name index -> local_num (for correct state array mapping) + uint16_t order_count; // Number of variables stored in order they were defined + uint16_t runtime_slots[MICROPY_PY_SYS_SETTRACE_NAMES_MAX]; // Mapping of local_num to runtime slots +} mp_local_names_t; + +// Initialize the local names structure +void mp_local_names_init(mp_local_names_t *local_names); + +// Function to look up a variable name by its index +qstr mp_local_names_get_name(const mp_local_names_t *local_names, uint16_t local_num); + +// Function to look up the original local_num by order index (source code order) +uint16_t mp_local_names_get_local_num(const mp_local_names_t *local_names, uint16_t order_idx); + +// Function to add or update a name mapping for a local variable +void mp_local_names_add(mp_local_names_t *local_names, uint16_t local_num, qstr qstr_name); + +// Function to get the runtime slot of a local variable by its index +uint16_t mp_local_names_get_runtime_slot(const mp_local_names_t *local_names, uint16_t local_num); + +#endif // MICROPY_PY_SYS_SETTRACE_SAVE_NAMES + +#endif // MICROPY_INCLUDED_PY_LOCALNAMES_H diff --git a/py/modsys.c b/py/modsys.c index 9ab02293b9063..dfdc5a76170ce 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -238,6 +238,21 @@ 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); + +static mp_obj_t mp_sys_gettrace(void) { + 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; + 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 +337,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/mpconfig.h b/py/mpconfig.h index 01712bd5b4d90..d0e7db64410d5 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -238,6 +238,11 @@ #define MICROPY_ALLOC_PARSE_RESULT_INC (16) #endif +// If not explicitly enabled, disable local variable name saving +#ifndef MICROPY_PY_SYS_SETTRACE_SAVE_NAMES +#define MICROPY_PY_SYS_SETTRACE_SAVE_NAMES (0) +#endif + // Strings this length or less will be interned by the parser #ifndef MICROPY_ALLOC_PARSE_INTERN_STRING_LEN #define MICROPY_ALLOC_PARSE_INTERN_STRING_LEN (10) diff --git a/py/objfun.c b/py/objfun.c index a742c5267254c..088c013c57ae6 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -34,7 +34,11 @@ #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_PY_SYS_SETTRACE_SAVE_NAMES +#include "py/localnames.h" +#endif #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) @@ -366,17 +370,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 diff --git a/py/profile.c b/py/profile.c index 397d0291f9fad..da7d82e8a6ab5 100644 --- a/py/profile.c +++ b/py/profile.c @@ -28,6 +28,9 @@ #include "py/bc0.h" #include "py/gc.h" #include "py/objfun.h" +#if MICROPY_PY_SYS_SETTRACE_SAVE_NAMES +#include "py/localnames.h" +#endif #if MICROPY_PY_SYS_SETTRACE @@ -85,18 +88,29 @@ static void frame_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t ); } +static mp_obj_t frame_f_locals(mp_obj_t self_in); // Forward declaration + 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,9 +126,129 @@ 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] = frame_f_locals(self_in); + break; } } +static mp_obj_t frame_f_locals(mp_obj_t self_in) { + // This function returns a dictionary of local variables in the current frame. + if (gc_is_locked()) { + return MP_OBJ_NULL; // Cannot create locals dict when GC is locked + } + mp_obj_frame_t *frame = MP_OBJ_TO_PTR(self_in); + mp_obj_dict_t *locals_dict = mp_obj_new_dict(frame->code_state->n_state); + + const mp_code_state_t *code_state = frame->code_state; + + if (code_state == NULL) { + // Return empty dictionary if state is invalid + return MP_OBJ_FROM_PTR(locals_dict); + } + + #if MICROPY_PY_SYS_SETTRACE_SAVE_NAMES + const mp_raw_code_t *raw_code = code_state->fun_bc->rc; + + // First, handle function parameters (these should have fixed positions) + uint16_t n_pos_args = raw_code->prelude.n_pos_args; + uint16_t n_kwonly_args = raw_code->prelude.n_kwonly_args; + uint16_t param_count = n_pos_args + n_kwonly_args; + + // Add parameters first (they have fixed slot assignments) + for (uint16_t i = 0; i < param_count && i < code_state->n_state; i++) { + if (code_state->state[i] == NULL) { + continue; + } + + qstr var_name_qstr = MP_QSTRnull; + if (i < MICROPY_PY_SYS_SETTRACE_NAMES_MAX) { + var_name_qstr = mp_local_names_get_name(&raw_code->local_names, i); + } + + if (var_name_qstr == MP_QSTRnull) { + vstr_t vstr; + vstr_init(&vstr, 16); // Initialize with enough space + vstr_printf(&vstr, "arg_%d", (int)(order_idx + 1)); + var_name_qstr = qstr_from_str(vstr_str(&vstr)); + vstr_clear(&vstr); + + if (var_name_qstr == MP_QSTR_NULL) { + continue; + } + } + } + + // Handle local variables with REVERSE SLOT ASSIGNMENT + bool used_slots[MICROPY_PY_SYS_SETTRACE_NAMES_MAX] = {false}; + + // Mark parameter slots as used + for (uint16_t i = 0; i < param_count; i++) { + if (i < MICROPY_PY_SYS_SETTRACE_NAMES_MAX) { + used_slots[i] = true; + } + } + + // Process variables using their source order but REVERSE slot assignment + for (uint16_t order_idx = 0; order_idx < raw_code->local_names.order_count; order_idx++) { + uint16_t local_num = mp_local_names_get_local_num(&raw_code->local_names, order_idx); + + // Skip parameters and invalid entries + if (local_num == UINT16_MAX || local_num < param_count) { + continue; + } + + qstr var_name_qstr = mp_local_names_get_name(&raw_code->local_names, local_num); + if (var_name_qstr == MP_QSTRnull) { + continue; + } + + // REVERSE SLOT ASSIGNMENT: Variables assigned from highest available slot down + uint16_t total_locals = code_state->n_state; + uint16_t reverse_slot = total_locals - 1 - order_idx; + // Validate and assign + if (reverse_slot >= param_count && reverse_slot < total_locals && + code_state->state[reverse_slot] != NULL && !used_slots[reverse_slot]) { + mp_obj_dict_store(locals_dict, MP_OBJ_NEW_QSTR(var_name_qstr), code_state->state[reverse_slot]); + used_slots[reverse_slot] = true; + } else { + // Fallback: try runtime slot or direct mapping + uint16_t runtime_slot = mp_local_names_get_runtime_slot(&raw_code->local_names, local_num); + if (runtime_slot != UINT16_MAX && runtime_slot < total_locals && + code_state->state[runtime_slot] != NULL && !used_slots[runtime_slot]) { + mp_obj_dict_store(locals_dict, MP_OBJ_NEW_QSTR(var_name_qstr), code_state->state[runtime_slot]); + used_slots[runtime_slot] = true; + } + } + } + + #else + // Fallback when variable names aren't saved + // Use reverse slot assignment: local variables are numbered from highest slot down + uint16_t total_locals = code_state->n_state; + for (uint16_t order_idx = 0; order_idx < total_locals; ++order_idx) { + uint16_t reverse_slot = total_locals - 1 - order_idx; + if (code_state->state[reverse_slot] == NULL) { + continue; + } + vstr_t vstr; + qstr var_name_qstr; + vstr_init(&vstr, 16); // Initialize with enough space + vstr_printf(&vstr, "local_%d", (int)(order_idx + 1)); + var_name_qstr = qstr_from_str(vstr_str(&vstr)); + vstr_clear(&vstr); + if (var_name_qstr == MP_QSTR_NULL) { + continue; + } + mp_obj_dict_store(locals_dict, MP_OBJ_NEW_QSTR(var_name_qstr), code_state->state[reverse_slot]); + } + #endif + return MP_OBJ_FROM_PTR(locals_dict); +} + MP_DEFINE_CONST_OBJ_TYPE( mp_type_frame, MP_QSTR_frame, @@ -148,6 +282,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 +322,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/py/py.mk b/py/py.mk index e352d89792bfd..5a79113c26659 100644 --- a/py/py.mk +++ b/py/py.mk @@ -141,6 +141,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ argcheck.o \ warning.o \ profile.o \ + localnames.o \ map.o \ obj.o \ objarray.o \ 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()