diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 9028a7a340ca7..404d7d7b2aab0 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -748,6 +748,9 @@ impl Step for DebuggerScripts { cp_debugger_script("gdb_load_rust_pretty_printers.py"); cp_debugger_script("gdb_lookup.py"); cp_debugger_script("gdb_providers.py"); + if builder.build.unstable_features() { + cp_debugger_script("gdb_trim_paths.py"); + } // lldb debugger scripts builder.install( diff --git a/src/etc/gdb_trim_paths.py b/src/etc/gdb_trim_paths.py new file mode 100644 index 0000000000000..695ef1b19a709 --- /dev/null +++ b/src/etc/gdb_trim_paths.py @@ -0,0 +1,105 @@ +# GDB Python script to handle Cargo `.trim-paths.jsonl` files from the trim-paths feature +# - https://github.com/rust-lang/cargo/issues/12137 +# - https://github.com/rust-lang/rust/issues/111540 + +import json +import os +import gdb +import sys + + +# https://doc.guix.gnu.org/gdb/16.3/en/html_node/Source-Path.html#index-set-substitute_002dpath +def _process_v1_trim_paths(lines, trim_paths_path): + for idx, line in enumerate(lines[2:], start=3): + try: + entry = json.loads(line) + if "from" in entry and "to" in entry: + cmd = f'set substitute-path "{entry["from"]}" "{entry["to"]}"' + gdb.execute(cmd) + except json.JSONDecodeError: + print( + f"(rust-gdb) warning: invalid JSON on line {idx} of {trim_paths_path}", + file=sys.stderr, + ) + + +def _load_trim_paths(filepath): + trim_paths_path = f"{filepath}.trim-paths.jsonl" + + # FIXME: It might be worth looking into the debuginfod fetch content if the local file + # doesn't exists (maybe with `debuginfod-find debuginfo`). + if not os.path.isfile(trim_paths_path): + return + + try: + # Load all the lines of the trim-paths file + with open(trim_paths_path, "r", encoding="utf-8") as f: + lines = [line.strip() for line in f] + + # Abort if we have less than 3 lines as that means that we cannot have any + # substitutions (header + metadata is already 2 lines) + if not lines or len(lines) < 3: + return + + # Try loading the header line, which contains the version (v) field + try: + header = json.loads(lines[0]) + ver = header["v"] + except json.JSONDecodeError: + print( + f"(rust-gdb) warning: header line 1 of {trim_paths_path} is not valid JSON", + file=sys.stderr, + ) + return + + # We only handle version 1 + if ver == 1: + _process_v1_trim_paths(lines, trim_paths_path) + else: + print( + f"(rust-gdb) warning: unsupported trim-paths version {ver}: {trim_paths_path}", + file=sys.stderr, + ) + except Exception as e: + print( + f"(rust-gdb) warning: failed to process trim-paths mappings: {e} of {trim_paths_path}", + file=sys.stderr, + ) + + +def _on_objfile(objfile): + filepath = objfile.filename + + if not filepath or not objfile.is_valid() or not os.path.isfile(filepath): + return + + _load_trim_paths(filepath) + + +def _on_progspace(progspace): + filepath = progspace.filename + + if not filepath or not os.path.isfile(filepath): + return + + _load_trim_paths(filepath) + + +def _on_new_objfile(event): + _on_objfile(event.new_objfile) + + +def _on_executable_changed(event): + _on_progspace(event.progspace) + + +# Setup the events for new objfile (so) and new executable +# https://doc.guix.gnu.org/gdb/16.3/en/html_node/Events-In-Python.html +# +# FIXME: should we handle clear/free events? +gdb.events.new_objfile.connect(_on_new_objfile) +gdb.events.executable_changed.connect(_on_executable_changed) + +# Load the trim-paths files for the already loaded objfiles +for objfile in gdb.objfiles(): + _on_objfile(objfile) diff --git a/src/etc/rust-gdb b/src/etc/rust-gdb index 9abed30ea6f73..885f69bfbc35a 100755 --- a/src/etc/rust-gdb +++ b/src/etc/rust-gdb @@ -16,13 +16,24 @@ GDB_PYTHON_MODULE_DIRECTORY="$RUSTC_SYSROOT/lib/rustlib/etc" # Get the commit hash for path remapping RUSTC_COMMIT_HASH="$("$RUSTC" -vV | sed -n 's/commit-hash: \([a-zA-Z0-9_]*\)/\1/p')" -# Run GDB with the additional arguments that load the pretty printers # Set the environment variable `RUST_GDB` to overwrite the call to a # different/specific command (defaults to `gdb`). RUST_GDB="${RUST_GDB:-gdb}" -PYTHONPATH="$PYTHONPATH:$GDB_PYTHON_MODULE_DIRECTORY" exec ${RUST_GDB} \ + +# Run GDB with the additional arguments that load the pretty printers +# Prepend base GDB flags ahead of any arguments passed by the user +set -- \ --directory="$GDB_PYTHON_MODULE_DIRECTORY" \ -iex "add-auto-load-safe-path $GDB_PYTHON_MODULE_DIRECTORY" \ -iex "set substitute-path /rustc/$RUSTC_COMMIT_HASH $RUSTC_SYSROOT/lib/rustlib/src/rust" \ "$@" - + +# Unstable (nightly-only) trim-paths feature, activated only if +# - RUST_GDB_TRIM_PATHS=unstable is set +# - and if the python file is present (only shipped on nightly) +GDB_TRIM_PATHS="$GDB_PYTHON_MODULE_DIRECTORY/gdb_trim_paths.py" +if [ "${RUST_GDB_TRIM_PATHS:-}" = "unstable" ] && [ -f "$GDB_TRIM_PATHS" ]; then + set -- -x "$GDB_TRIM_PATHS" "$@" +fi + +PYTHONPATH="$PYTHONPATH:$GDB_PYTHON_MODULE_DIRECTORY" exec ${RUST_GDB} "$@"