| # GDB Python script to handle Cargo `<exe>.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) |