-
Notifications
You must be signed in to change notification settings - Fork 5
Allow sys.settrace() to inspect locals() variables including (optional) name resolution. #4
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
base: pdb_support
Are you sure you want to change the base?
Conversation
Signed-off-by: Jos Verlinde <jos_verlinde@hotmail.com>
Signed-off-by: Jos Verlinde <jos_verlinde@hotmail.com>
Signed-off-by: Jos Verlinde <jos_verlinde@hotmail.com>
Signed-off-by: Jos Verlinde <jos_verlinde@hotmail.com>
Signed-off-by: Jos Verlinde <jos_verlinde@hotmail.com>
I'm not entirely versed in the internals enough to be sure; but the existing locals implementation is just a normal C array of obj's isn't it? (pointer and len). The same array holds a couple of other pieces of stack data at the start before the locals? (old memory of how it works). The current implementation of storing locals feels a bit heavy; Can it be simplified along the lines of "in the compiler where it currently determines how many local variables are in scope to create / set the size of the locals array, also define the array of QSTR references of matching length to the number of local variables. Then it can be populated with the QSTR references at the same time. Also, elsewhere in the compiler names of objects like classes, functions, global variables are all captured as QSTR references; ensure this section is updated (if the #define is enabled) to also capture local variable names as QSTRS's" |
Thanks for the suggestion, I'm also wondering if the current 2 different ways to store/generate local names actually compare wrt to memory usage. I'm not surprised to need a few more iterations. But this seems like another good stepping-stone. |
Oh definitely yes, it's an amazing stepping stone that effectively demonstrates just what is possible! This is what I'm loving about the current crop of AI tools. Even if the first pass implementations aren't great they let you try things so quickly, the code written is "cheap" in that is easy to throw it away and try again! My favourite (by a long margin) is Claude Code; I pointed it at this branch to review your working implantation, then wrote a summary of everything I thought I knew about this area and set it loose. That's the new linked pr just above here, another attempt at local variables (two different options in the one PR), as well as unit tests for both your original commit and the new stuff. Those tests are the only testing I've done so far, the branch was "written" from my phone, I haven't had time at a computer to test it in vscode yet. |
Summary
This enhances the debugpy/pdb support to enable debuggers and profiling tools to access local variable names and values in function frames, which is essential for interactive debugging, breakpoint inspection, and runtime analysis. The implementation includes both basic local variable access and advanced variable name preservation with correct slot mapping.
Key Features Added
1. Basic Frame Local Variables Access (
frame.f_locals
)The initial implementation provides access to local variables in stack frames through the
frame.f_locals
property. This foundational feature enables debuggers to inspect variable values at runtime.Key Components:
frame_f_locals()
function - Core implementation inpy/profile.c
local_01
,local_02
, etc. when variable names unavailableImplementation Features:
2. Advanced Variable Name Storage (
MICROPY_SAVE_LOCAL_VARIABLE_NAMES
)An enhanced compile-time feature that preserves actual local variable names in compiled bytecode for professional debugging capabilities.
Configuration:
Note: The implementation also supports a conditional flag
MICROPY_PY_SYS_SETTRACE_SAVE_NAMES
for more granular control whenMICROPY_PY_SYS_SETTRACE
is enabled.Components:
py/localnames.h
- Data structures for variable name mappingpy/localnames.c
- Functions to manage variable name storage and retrievalpy/debug_locals.h
- Debug utilities headerpy/debug_locals.c
- Debug utilities implementation3. Enhanced Frame Local Variables Access with Real Names
Enhanced the
frame.f_locals
property to return actual local variable names and values instead of generic placeholder names.Evolution:
4. Reverse Slot Assignment Fix
Fixed a critical bug in variable-to-slot mapping where variables were incorrectly mapped to runtime slots.
Problem: Variables were being assigned sequentially from low slots up, but the runtime was expecting them from high slots down.
Solution: Implemented reverse slot assignment where:
5. Debug Configuration Options
Added optional compiler optimization controls for enhanced debugging experience:
Data Structures
mp_local_names_t
Note:
MICROPY_PY_SYS_SETTRACE_NAMES_MAX
defaults to 32 and can be configured at compile time.Enhanced
mp_raw_code_t
API Functions
Core Functions
mp_local_names_init()
- Initialize local names structuremp_local_names_add()
- Add variable name mappingmp_local_names_get_name()
- Get variable name by indexmp_local_names_get_local_num()
- Get local number by order indexmp_local_names_get_runtime_slot()
- Get runtime slot mappingDebug Functions
mp_debug_print_local_variables()
- Print variable mappings for debuggingmp_debug_locals_info()
- Exposed assys.debug_locals_info()
for runtime debuggingCompilation Integration
Enhanced Compiler (
py/compile.c
)Frame Locals Implementation (
py/profile.c
)The implementation of
frame_f_locals()
has evolved through two phases:Phase 1: Basic Implementation
The initial version provides fundamental local variable access:
Phase 2: Enhanced Implementation
The enhanced version adds comprehensive variable name support:
Key Safety Features
Configuration Changes
Unix Standard Port (
ports/unix/variants/standard/mpconfigvariant.h
)The configuration enables the debugging features. The current implementation uses different configuration options:
Configuration Levels:
Basic Debugging (minimum):
sys.settrace()
with generic variable namesframe.f_locals
returns{'local_01': value1, 'local_02': value2, ...}
Enhanced Debugging (settrace-specific):
sys.settrace()
with actual variable names using settrace-specific storageAdvanced Debugging (broader support):
frame.f_locals
returns{'foo': value1, 'bar': value2, ...}
Debug-Optimized Build (for intensive debugging):
Global Configuration (
py/mpconfig.h
)Note: The broader
MICROPY_SAVE_LOCAL_VARIABLE_NAMES
configuration is not currently implemented in the global config, but is used at the implementation level.Build System Integration
Makefile Changes (
py/py.mk
)Note: The build system automatically includes the new object files for local variable name support and debug utilities.
Usage Examples
Basic sys.settrace Usage
Expected Output
Debug Output
When
MICROPY_PY_SYS_SETTRACE_SAVE_NAMES
is enabled, detailed debug information is printed:Memory Considerations
MICROPY_PY_SYS_SETTRACE_NAMES_MAX
(32) local variables per functionMICROPY_PY_SYS_SETTRACE_SAVE_NAMES
is definedCompatibility
Backward Compatibility
MICROPY_PY_SYS_SETTRACE_SAVE_NAMES
is disabled (default), basic functionality still worksMICROPY_PY_SYS_SETTRACE
is enabled,frame.f_locals
uses generic names (local_01
,local_02
, etc.)Platform Support
sys.settrace()
supportMICROPY_PERSISTENT_CODE_SAVE
to be enabledDebugging Commands
Runtime Debug Information
This command prints comprehensive information about:
Testing
Trade-offs and Alternatives
Currently the max number of local names preservers is 32 , to limit the memory impact
if you inspect a scope/closure with more local vars - the names are mapped incorrectly
Not quite sure yet about the memory impact if it is possible to increase this number, or there is a need to fall-back to the
local_<nn>
format. (which also takes memory to allocate the names)