Skip to content

rust: step_out of main lands in FnOnce::call_once with no 'no program frame' note, a symbol label as location, and a nameless junk local #1131

Open

Description

@debugmcpdev

Found by the 2026-11-11 /testdebugger run (main 198784fc, v0.26.0, CodeLLDB 1.12.3, host rustc 1.91). Reproduced on three servers (local stdio, npx HTTP, npx stdio) and on every retry.

What happens

step_out from main in examples/rust/hello_world (any line of main, e.g. 18 or 22) lands in Rust's start-up shim:

{"message":"Stepped out","state":"paused",
 "location":{"file":"@core::ops::function::FnOnce::call_once","line":6,"column":0},
 "snapshot":{"frames":[{"name":"core::ops::function::FnOnce::call_once","file":"@core::ops::function::FnOnce::call_once","line":6}, …],
             "locals":{"":"<Unable to determine byte size.>"}}}

The same landing on C/C++ (__libc_start_call_main) carries the honest note "No program frame is active…", and pause and wait answers carry it in this situation too. The Rust answer:

  1. Has no note and no noProgramFrame, although none of the shown frames is program code.
  2. Shows a nameless junk local. snapshot.locals is {"": "<Unable to determine byte size.>"} (on some runs {"": "()"}). get_local_variables then returns [{"name":"","value":"<Unable to determine byte size.>","type":"void"}] with no note.
  3. Uses a symbol label as location.file (@core::ops::function::FnOnce::call_once), with no unresolvedSource flag. get_stack_trace adds that flag on the same frame.

Likely cause

noProgramFrame comes from policyUserFrames(frames, policy) (src/session/inspection/stop-anchor.ts), which asks the policy's isInternalFrame. In packages/shared/src/interfaces/lldb-policy-shared.ts, an @symbol frame counts as missing-source (isSystemOrMissingSource) only when its name also matches an internal-name pattern ("Only consulted for name-matched frames"). core::ops::function::FnOnce::call_once matches none, so a sourceless @… frame is classed as program code. namesASource() in stop-anchor.ts already knows @… is not a source; the user-frame test does not use it.

Expected

The same answer as the C/C++ landing: a note saying no program frame is active, and noProgramFrame: true. Locals with an empty name and an LLDB error string should be dropped or explained rather than presented as a variable.

Activity

  1. debugmcpdev commented on Nov 11, 2026

    @debugmcpdev
    CollaboratorAuthor

    Second manifestation of the same root cause, found in the Docker image (GnuCOBOL 3.2 / glibc there), reproduced on both Docker servers: pause_execution on examples/cobol/pause.cob answers location: {file: "@__nanosleep", line: 11}, with the note "inspection anchors on '__nanosleep' (@__nanosleep:11)". clock_nanosleep and cob_sys_sleep on either side of it are hidden as internal. On the host the same pause anchors on PAUSE: 0000-MAIN (pause.cob:12) because the host's frame is the name-matched __GI___clock_nanosleep. The container's sourceless @__nanosleep frame matches no internal-name pattern, so it counts as program code: the same isSystemOrMissingSource only-for-name-matched-frames rule described above. Treating any @… (sourceless) frame as non-program would fix both the Rust step_out case and this one.

  2. debugmcpdev commented on Nov 13, 2026

    @debugmcpdev
    CollaboratorAuthor

    The Rust row of #1135 lands here.

    #1153 (#1135) makes get_local_variables and evaluate_expression carry noProgramFrame: true and the "No program frame is active…" note whenever the frame-anchor resolver gives that verdict for the stack. For C/C++ and JavaScript it does. For Rust it does not, for the reason this issue already gives: the frame a step_out of main lands in is not classified as runtime, so there is no verdict to carry.

    Measured 2026-11-13 on Windows, main at 0f1eb6ac, examples/rust/hello_world, breakpoint at src/main.rs:30, then step_out:

    {
      "success": true, "message": "Stepped out", "state": "paused",
      "snapshot": {
        "frames": [
          { "name": "core::ops::function::FnOnce::call_once", "file": "@core::ops::function::FnOnce::call_once", "line": 6 },
          { "name": "std::sys::backtrace::__rust_begin_short_backtrace", "file": "@.text", "line": 6 },
          { "name": "std::rt::lang_start::{{closure}}", "file": "@std::rt::lang_start::{{closure}}", "line": 7 }
        ],
        "locals": { "": "()" }
      },
      "location": { "file": "@core::ops::function::FnOnce::call_once", "line": 6, "column": 0 }
    }

    No note, and the snapshot reads the frame's nameless local as a program local. The same step in C++ (hello_world.cpp) answers "No program frame is active on thread N: all 4 frames are internal/runtime (top: '__tmainCRTStartup' …)".

    Once the Rust policy classifies these frames as runtime, the #1135 fix covers Rust with no further change: the flag and note on get_local_variables and evaluate_expression come from the same verdict.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions