|
| 1 | +# GDB Python script to handle Cargo `.trim-paths.jsonl` files from the trim-paths feature |
| 2 | + |
| 3 | +import json |
| 4 | +import os |
| 5 | +import gdb |
| 6 | + |
| 7 | + |
| 8 | +def _process_v1_trim_paths(lines, trim_paths_path): |
| 9 | + for idx, line in enumerate(lines[2:], start=3): |
| 10 | + try: |
| 11 | + entry = json.loads(line) |
| 12 | + if "from" in entry and "to" in entry: |
| 13 | + # https://doc.guix.gnu.org/gdb/16.3/en/html_node/Source-Path.html#index-set-substitute_002dpath |
| 14 | + cmd = f'set substitute-path "{entry["from"]}" "{entry["to"]}"' |
| 15 | + gdb.execute(cmd) |
| 16 | + except json.JSONDecodeError: |
| 17 | + print( |
| 18 | + f"(rust-gdb) warning: invalid JSON on line {idx} of {trim_paths_path}" |
| 19 | + ) |
| 20 | + |
| 21 | + |
| 22 | +def _load_trim_paths(filepath): |
| 23 | + trim_paths_path = f"{filepath}.trim-paths.jsonl" |
| 24 | + |
| 25 | + if not os.path.isfile(trim_paths_path): |
| 26 | + return |
| 27 | + |
| 28 | + try: |
| 29 | + # Load all the lines of the trim-paths file, max 10 lines |
| 30 | + with open(trim_paths_path, "r", encoding="utf-8") as f: |
| 31 | + lines = [line.strip() for line in f] |
| 32 | + |
| 33 | + # Abort if we have less than 3 lines as that means that we cannot have any |
| 34 | + # substitutions (header + metadata is already 2 lines) |
| 35 | + if not lines or len(lines) < 3: |
| 36 | + return |
| 37 | + |
| 38 | + # Try loading the header line, which contains the version (v) field |
| 39 | + try: |
| 40 | + header = json.loads(lines[0]) |
| 41 | + ver = header["v"] |
| 42 | + except json.JSONDecodeError: |
| 43 | + print( |
| 44 | + f"(rust-gdb) warning: header line 1 of {trim_paths_path} is not valid JSON" |
| 45 | + ) |
| 46 | + return |
| 47 | + |
| 48 | + # We only handle version 1 |
| 49 | + if ver == 1: |
| 50 | + _process_v1_trim_paths(lines, trim_paths_path) |
| 51 | + except Exception as e: |
| 52 | + print( |
| 53 | + f"(rust-gdb) warning: failed to process trim-paths mappings: {e} of {trim_paths_path}" |
| 54 | + ) |
| 55 | + |
| 56 | + |
| 57 | +def _on_objfile(objfile): |
| 58 | + filepath = objfile.filename |
| 59 | + |
| 60 | + if not filepath or not objfile.is_valid() or not os.path.isfile(filepath): |
| 61 | + return |
| 62 | + |
| 63 | + _load_trim_paths(filepath) |
| 64 | + |
| 65 | + |
| 66 | +def _on_progspace(progspace): |
| 67 | + filepath = progspace.filename |
| 68 | + |
| 69 | + if not filepath or not os.path.isfile(filepath): |
| 70 | + return |
| 71 | + |
| 72 | + _load_trim_paths(filepath) |
| 73 | + |
| 74 | + |
| 75 | +def _on_new_objfile(event): |
| 76 | + _on_objfile(event.new_objfile) |
| 77 | + |
| 78 | + |
| 79 | +def _on_executable_changed(event): |
| 80 | + _on_progspace(event.progspace) |
| 81 | + |
| 82 | + |
| 83 | +# Setup the events for new objfile (so) and new executable |
| 84 | +# https://doc.guix.gnu.org/gdb/16.3/en/html_node/Events-In-Python.html |
| 85 | +# |
| 86 | +# FIXME: should we handle clear/free events? |
| 87 | +gdb.events.new_objfile.connect(_on_new_objfile) |
| 88 | +gdb.events.executable_changed.connect(_on_executable_changed) |
| 89 | + |
| 90 | +# Load the trim-paths files for the already loaded objfiles |
| 91 | +for objfile in gdb.objfiles(): |
| 92 | + _on_objfile(objfile) |
0 commit comments