Skip to content

Pdb support - local variables #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/nxp_driver
Submodule nxp_driver updated 4296 files
2 changes: 1 addition & 1 deletion lib/protobuf-c
Submodule protobuf-c updated 53 files
+175 −60 .github/workflows/build.yml
+2 −2 .gitignore
+43 −0 CHANGELOG.md
+0 −0 ChangeLog.old
+1 −1 LICENSE
+102 −92 Makefile.am
+8 −10 README.md
+1 −0 build-cmake/.gitignore
+334 −192 build-cmake/CMakeLists.txt
+2 −0 build-cmake/Config.cmake.in
+5 −19 configure.ac
+8 −6 protobuf-c/protobuf-c.c
+10 −9 protobuf-c/protobuf-c.h
+0 −22 protoc-c/main.cc
+20 −25 protoc-gen-c/c_bytes_field.cc
+17 −22 protoc-gen-c/c_bytes_field.h
+40 −51 protoc-gen-c/c_enum.cc
+15 −26 protoc-gen-c/c_enum.h
+19 −26 protoc-gen-c/c_enum_field.cc
+16 −20 protoc-gen-c/c_enum_field.h
+10 −15 protoc-gen-c/c_extension.cc
+14 −25 protoc-gen-c/c_extension.h
+58 −53 protoc-gen-c/c_field.cc
+22 −36 protoc-gen-c/c_field.h
+20 −29 protoc-gen-c/c_file.cc
+19 −30 protoc-gen-c/c_file.h
+15 −20 protoc-gen-c/c_generator.cc
+18 −19 protoc-gen-c/c_generator.h
+54 −118 protoc-gen-c/c_helpers.cc
+50 −49 protoc-gen-c/c_helpers.h
+284 −207 protoc-gen-c/c_message.cc
+24 −35 protoc-gen-c/c_message.h
+17 −24 protoc-gen-c/c_message_field.cc
+13 −22 protoc-gen-c/c_message_field.h
+51 −53 protoc-gen-c/c_primitive_field.cc
+13 −21 protoc-gen-c/c_primitive_field.h
+39 −44 protoc-gen-c/c_service.cc
+20 −31 protoc-gen-c/c_service.h
+21 −25 protoc-gen-c/c_string_field.cc
+15 −22 protoc-gen-c/c_string_field.h
+58 −0 protoc-gen-c/compat.h
+53 −0 protoc-gen-c/main.cc
+4 −1 t/generated-code2/test-generated-code2.c
+2 −0 t/issue204/issue204.proto
+2 −0 t/issue220/issue220.proto
+2 −0 t/issue251/issue251.proto
+1 −0 t/issue745/.gitignore
+41 −0 t/issue745/issue745.c
+32 −0 t/issue745/issue745.proto
+2 −0 t/test-full.proto
+2 −0 t/test-optimized.proto
+2 −0 t/test.proto
+1 −1 t/version/version.c
4 changes: 4 additions & 0 deletions ports/unix/variants/standard/mpconfigvariant.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
18 changes: 18 additions & 0 deletions py/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions py/emitglue.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
6 changes: 6 additions & 0 deletions py/emitglue.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand Down
112 changes: 112 additions & 0 deletions py/localnames.c
Original file line number Diff line number Diff line change
@@ -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
63 changes: 63 additions & 0 deletions py/localnames.h
Original file line number Diff line number Diff line change
@@ -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
17 changes: 17 additions & 0 deletions py/modsys.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions py/mpconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
22 changes: 11 additions & 11 deletions py/objfun.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
Loading