-
-
Notifications
You must be signed in to change notification settings - Fork 15.4k
Add nightly-only support for Cargo unremap trim-paths files in rust-gdb
#160560
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| # 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" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not blocking (consider it just a thought out of curiosity) -- should this be looking up in some gdb facility for finding these? E.g., so that you can fetch this from debuginfod servers or look next to the debuginfo on disk rather than the actual binary?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a interesting idea. I haven't looked it up much, but it seems like What I haven't figure-out is how to access the fetched content, there doesn't seems to be an easy way for that. We could invoke Leaving a comment inside the script to look at it. |
||
|
|
||
| # 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]) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not blocking, but it seems a little odd to me to include a version like this as a JSON blob. Maybe we should have magic starting bytes instead? E.g., specifically
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think header follow what Cargo also does for SBOM, @weihanglo you know more I think?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess my feeling is that if we're going to have a versioned header, I'd much rather treat it as a magic byte sequence than a JSON blob. That seems more reliable against future iteration (e.g., allows us to move away from JSON for it).
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, though I think fi we ever change the format, tools should just give up parsing
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, but it's generally noisy and more expensive to attempt to parse JSON than to look for a fixed string. Here it's not terrible, we have Python anyway, but if we were doing this in (say) C then that becomes much more gnarly. Of course, if the actual format is JSON then that's no big deal :)
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah. I meant we can always change the format in a new toolchain version. People anyway need to align with the parsing logic, so it doesn't matter if this version it is JSON and next becomes ad-hoc binary format. |
||
| 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, | ||
| ) | ||
|
Urgau marked this conversation as resolved.
|
||
| return | ||
|
|
||
| # We only handle version 1 | ||
| if ver == 1: | ||
| _process_v1_trim_paths(lines, trim_paths_path) | ||
|
Urgau marked this conversation as resolved.
|
||
| 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) | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How should we test this? Do you have any manual steps to follow?
View changes since the review
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Side note: I am adding tests in rust-lang/rust exercising the unremap file itself.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested it by manually modifying the
rust-gdbscript to point to my localgdb_trim_paths.py("$GDB_PYTHON_MODULE_DIRECTORY/gdb_trim_paths.py"to"$YOUR_RUST_LOCAL_CHECKOUT/src/etc/gdb_trim_paths.py"), as otherwise you need to dox.py distwhich annoying.Then I created a dummy hello-world Cargo project,
cargo build-it and manually added thehello-world.trim-paths.jsonlfile (since the Cargo bump PR is not yet merged).After that you can just execute
rust-gdbas always:rust-gdb target/debug/hello-world&runrust-gdb&file target/debug/hello-world&runTip:
show substitute-pathshows all the substitutions.