Skip to content

Instantly share code, notes, and snippets.

@mjpieters
Created June 17, 2024 12:28
Show Gist options
  • Save mjpieters/aae1807e2ef430cff36d3e08568b01b1 to your computer and use it in GitHub Desktop.
Save mjpieters/aae1807e2ef430cff36d3e08568b01b1 to your computer and use it in GitHub Desktop.
CodeLLDB & Rust: Automatically add a source map to LLDB for the Rust stdlib

Automatically set the correct source map for your Rust toolchain

When it comes to debugging Rust in VSCode, the CodeLLDB extension provides a great debugging experience. Until you try to step into some Rust stdlib code and are greeted with assembly.

Of course you can manually add a source map entry for your rust toolchain, which involves finding the current rustc version commit hash and the sysroot path for the source code:

$ rustc --version --verbose
rustc 1.76.0 (07dca489a 2024-02-04)
binary: rustc
commit-hash: 07dca489ac2d933c78d3c5158e3f43beefeb02ce
commit-date: 2024-02-04
host: aarch64-apple-darwin
release: 1.76.0
LLVM version: 17.0.6
$ rust --print sysroot
/Users/USERNAME/.rustup/toolchains/stable-aarch64-apple-darwin

then plugging that into the source map (map from /rustc/{commit-hash} to {sysroot}/lib/rustlib/src/rust). However, every time you update your rust toolchain to a new version, these values may have to be updated.

Let LLDB do the work

LLDB comes with first-class Python scripting support, so why not have LLDB figure this out for you. CodeLLDB lets you run arbitrary LLDB commands before launching a debug session via the lldb.launch.preRunCommands setting, so we can set a source map config every time we need to debug.

Plug the following snippet into your VSCode settings.json file and Bob's your uncle:

    "lldb.launch.preRunCommands": [
        "script import subprocess;rustc=lambda *a: subprocess.run(['rustc',*a],stdout=subprocess.PIPE,check=True,text=True).stdout;sysroot=rustc('--print','sysroot').strip();gitsha=rustc('-vV').partition('commit-hash:')[-1].partition('\\n')[0].strip();lldb.debugger.HandleCommand(f'settings set target.source-map /rustc/{gitsha} \"{sysroot}/lib/rustlib/src/rust\"')"
    ],

Breaking down the script

That's quite a mouthfull, so lets break this down a bit.

The command starts with script, which tells LLDB to interpret the rest of the text as a Python script. The Python code uses ; to delimit multiple statements. Even though Python is a whitespace-significant language, you can still put multiple statements on a single line provided they are simple statements, e.g. anything that doesn't require an indented block underneath.

Here are those same Python statements, with whitespace and comments added:

import subprocess

# Run the `rustc` command with a given set of arguments as a subprocess,
# and return whatever it outputs to stdout as text (decoded from UTF-8).
rustc = lambda *a: subprocess.run(
    ['rustc', *a],
    stdout=subprocess.PIPE,
    check=True,
    text=True
).stdout

# Get the current sysroot and commit hash for rustc, producing cleaned strings
sysroot = rustc('--print', 'sysroot').strip()
# `rustc -vV` is shorthand for `rustc --version --verbose`
gitsha = rustc('-vV').partition('commit-hash:')[-1].partition('\n')[0].strip()

# Call back to the LLDB debugger to add a source-map using the rustc
# configuration retrieved above
lldb.debugger.HandleCommand(
    f'settings set target.source-map /rustc/{gitsha} "{sysroot}/lib/rustlib/src/rust"
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment