Skip to content

Add front-end for debug variable values#18

Closed
djolertrk wants to merge 3 commits into0xMiden:nextfrom
walnuthq:feature/debug-variable-locations-next
Closed

Add front-end for debug variable values#18
djolertrk wants to merge 3 commits into0xMiden:nextfrom
walnuthq:feature/debug-variable-locations-next

Conversation

@djolertrk
Copy link
Collaborator

It depends on the miden-vm and compiler, that is why it is just a placeholder for now.

Implement debug variable tracking and display in the debugger:

- Add DebugVarTracker to track variables throughout execution
- Add DebugVarSnapshot for variable state at specific clock cycles
- Add stub types for DebugVarInfo and DebugVarLocation (pending new miden-core)
- Add resolve_variable_value (currently returns None, pending new miden-core)
- Add :vars/:variables/:locals commands to display current variables

The vars command shows variables in format "name=value" or "name=location"
if the value cannot be resolved.

Note: Full variable value resolution requires new version of miden-core.
Load link libraries and stdlib before calling with_dependencies() to ensure
the dependency resolver has all required libraries registered. This fixes
the "Dependency not found in resolver" panic when running programs that
depend on the standard library.
@djolertrk
Copy link
Collaborator Author

cc @bitwalker @greenhat

Copy link
Collaborator

@bitwalker bitwalker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

There's two changes I'd like to make here:

  1. I think we would benefit from using string interning for debug info, considering how repetitive some of the information is (variable names). I provided more detail in another comment, but the gist is that we can use midenc_hir_symbol for that, and replace uses of String with Symbol anywhere that we expect a string to be used a lot. We could also use compact_str, but Symbol has some additional properties that make it nicer for certain things (namely cheap copies, small size, and using it as a key in key/value data structures).
  2. We should not bake in miden-stdlib (or any Miden package/library), and should instead lean on an installed midenup toolchain for similar convenience. Since most (virtually all) users of the debugger will use it through miden debug, the benefits are the same - and once we migrate to v0.21, we'll additionally be able to use miden-project.toml to resolve dependencies more precisely (and the Library struct is going away in that release as well, so baking in miden-stdlib isn't likely to continue being an option regardless.

/// This is a stub type until miden-core provides DebugVarInfo.
#[derive(Debug, Clone)]
pub struct DebugVarInfo {
name: String,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add midenc_hir_symbol as a dependency, and replace this with midenc_hir_symbol::Symbol. That way all variable names are interned, and the debug info gets a lot more compact in memory.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be part of #17

/// Variable is a local at the given frame offset
Local(u16),
/// Variable location is computed via an expression
Expression(Vec<u8>),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason to represent this as a byte vector, rather than decoding it into the bytecode/opcodes needed to evaluate the expression?

I think we'll want to define those types in miden-mast-package, since that's where the types serialized to the package format are canonically defined, but we can have our own versions of them here in the debugger too that we map the serialized representation to when deserializing (if needed). I think the bytecode ops we'd need to support wouldn't require us to do that, but let me know if there are any issues that come to mind.

If we do that, then we can leave this as-is for the time being, and then surface the actual ops when we migrate to v0.21

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be part of #17

/// This structure maintains a mapping from variable names to their most recent
/// location information at each clock cycle. It's designed to work with the
/// debugger to provide source-level variable inspection.
pub struct DebugVarTracker {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I think we'll be able to remove this once we migrate to v0.21, since we won't need to compute an execution trace (and associated state) before we start debugging.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be part of #17

};

/// Get the standard library as an Arc<Library>
fn get_stdlib() -> Arc<Library> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: I would prefer not to do this - we do it in the compiler out of necessity/historical reasons, but we're moving away from that with the advent of midenup and the toolchain it manages.

Hardcoding it here like this means that the debugger only works with that exact version of the stdlib - and we need it to be much more flexible than that.

What we can do, is check for the MIDENUP_HOME environment variable, which is set by miden when executing any component executables (e.g. miden debug). If present, we can use that to locate and load any packages from the lib directory of the active toolchain. That will make it convenient for your average user (i.e. they won't need to manually pass flags to load the standard library by hand when using miden debug).

One issue I just found, and I'll open a PR for today, is that midenup is only setting MIDENUP_HOME, but not providing the active toolchain sysroot via the environment as well (e.g. MIDENUP_SYSROOT). However, you can obtain this as follows:

# `miden` always sets PATH for the executables it calls, placing the $MIDENUP_SYSROOT/opt directory at the front
let path_raw = std::env::var("PATH").unwrap();
# This is guaranteed to succeed when `miden debug` is used, but we probably want to handle the case where someone has set MIDENUP_HOME in their environment and is running `miden-debug` directly
let toolchain_opt_dir = Path::new(path_raw.split_once(':').unwrap().0);
# The $MIDENUP_SYSROOT/lib directory contains any installed Miden packages that ship as part of the toolchain
let toolchain_lib_dir = toolchain_opt_dir.parent().unwrap().join("lib");

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already fixed this issue with: #23 and #22

@djolertrk
Copy link
Collaborator Author

djolertrk commented Feb 26, 2026

Will continue with #17, since it will be ported to miden-vm v0.21.1, and all of this will be included there.

@djolertrk djolertrk closed this Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants