]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-demangler/main.rs
Auto merge of #76580 - rokob:iss76011, r=estebank
[rust.git] / src / tools / rust-demangler / main.rs
1 //! Demangles rustc mangled names.
2 //!
3 //! This tool uses https://crates.io/crates/rustc-demangle to convert an input buffer of
4 //! newline-separated mangled names into their demangled translations.
5 //!
6 //! This tool can be leveraged by other applications that support third-party demanglers.
7 //! It takes a list of mangled names (one per line) on standard input, and prints a corresponding
8 //! list of demangled names. The tool is designed to support other programs that can leverage a
9 //! third-party demangler, such as `llvm-cov`, via the `-Xdemangler=<path-to-demangler>` option.
10 //!
11 //! To use `rust-demangler`, first build the tool with:
12 //!
13 //! ```shell
14 //! $ ./x.py build rust-demangler
15 //! ```
16 //!
17 //! Then, with `llvm-cov` for example, add the `-Xdemangler=...` option:
18 //!
19 //! ```shell
20 //! $ TARGET="${PWD}/build/x86_64-unknown-linux-gnu"
21 //! $ "${TARGET}"/llvm/bin/llvm-cov show --Xdemangler="${TARGET}"/stage0-tools-bin/rust-demangler \
22 //!   --instr-profile=main.profdata ./main --show-line-counts-or-regions
23 //! ```
24 //!
25 //! Note regarding crate disambiguators:
26 //!
27 //! Some demangled symbol paths can include "crate disambiguator" suffixes, represented as a large
28 //! hexadecimal value enclosed in square braces, and appended to the name of the crate. a suffix to the
29 //! original crate name. For example, the `core` crate, here, includes a disambiguator:
30 //!
31 //! ```rust
32 //!     <generics::Firework<f64> as core[a7a74cee373f048]::ops::drop::Drop>::drop
33 //! ```
34 //!
35 //! These disambiguators are known to vary depending on environmental circumstances. As a result,
36 //! tests that compare results including demangled names can fail across development environments,
37 //! particularly with cross-platform testing. Also, the resulting crate paths are not syntactically
38 //! valid, and don't match the original source symbol paths, which can impact development tools.
39 //!
40 //! For these reasons, by default, `rust-demangler` uses a heuristic to remove crate disambiguators
41 //! from their original demangled representation before printing them to standard output. If crate
42 //! disambiguators are required, add the `-d` (or `--disambiguators`) flag, and the disambiguators
43 //! will not be removed.
44 //!
45 //! Also note that the disambiguators are stripped by a Regex pattern that is tolerant to some
46 //! variation in the number of hexadecimal digits. The disambiguators come from a hash value, which
47 //! typically generates a 16-digit hex representation on a 64-bit architecture; however, leading
48 //! zeros are not included, which can shorten the hex digit length, and a different hash algorithm
49 //! that might also be dependent on the architecture, might shorten the length even further. A
50 //! minimum length of 5 digits is assumed, which should be more than sufficient to support hex
51 //! representations that generate only 8-digits of precision with an extremely rare (but not
52 //! impossible) result with up to 3 leading zeros.
53 //!
54 //! Using a minimum number of digits less than 5 risks the possibility of stripping demangled name
55 //! components with a similar pattern. For example, some closures instantiated multiple times
56 //! include their own disambiguators, demangled as non-hashed zero-based indexes in square brackets.
57 //! These disambiguators seem to have more analytical value (for instance, in coverage analysis), so
58 //! they are not removed.
59
60 use regex::Regex;
61 use rustc_demangle::demangle;
62 use std::io::{self, Read, Write};
63
64 const REPLACE_COLONS: &str = "::";
65
66 fn main() -> io::Result<()> {
67     // FIXME(richkadel): In Issue #77615 discussed updating the `rustc-demangle` library, to provide
68     // an option to generate demangled names without including crate disambiguators. If that
69     // happens, update this tool to use that option (if the `-d` flag is not set) instead stripping
70     // them via the Regex heuristic. The update the doc comments and help.
71
72     // Strip hashed hexadecimal crate disambiguators. Leading zeros are not enforced, and can be
73     // different across different platform/architecture types, so while 16 hex digits are common,
74     // they can also be shorter.
75     //
76     // Also note that a demangled symbol path may include the `[<digits>]` pattern, with zero-based
77     // indexes (such as for closures, and possibly for types defined in anonymous scopes). Preferably
78     // these should not be stripped.
79     //
80     // The minimum length of 5 digits supports the possibility that some target architecture (maybe
81     // a 32-bit or smaller architecture) could generate a hash value with a maximum of 8 digits,
82     // and more than three leading zeros should be extremely unlikely. Conversely, it should be
83     // sufficient to assume the zero-based indexes for closures and anonymous scopes will never
84     // exceed the value 9999.
85     let mut strip_crate_disambiguators = Some(Regex::new(r"\[[a-f0-9]{5,16}\]::").unwrap());
86
87     let mut args = std::env::args();
88     let progname = args.next().unwrap();
89     for arg in args {
90         if arg == "--disambiguators" || arg == "-d" {
91             strip_crate_disambiguators = None;
92         } else {
93             eprintln!();
94             eprintln!("Usage: {} [-d|--disambiguators]", progname);
95             eprintln!();
96             eprintln!(
97                 "This tool converts a list of Rust mangled symbols (one per line) into a\n\
98                 corresponding list of demangled symbols."
99             );
100             eprintln!();
101             eprintln!(
102                 "With -d (--disambiguators), Rust symbols mangled with the v0 symbol mangler may\n\
103                 include crate disambiguators (a hexadecimal hash value, typically up to 16 digits\n\
104                 long, enclosed in square brackets)."
105             );
106             eprintln!();
107             eprintln!(
108                 "By default, crate disambiguators are removed, using a heuristics-based regular\n\
109                 expression. (See the `rust-demangler` doc comments for more information.)"
110             );
111             eprintln!();
112             std::process::exit(1)
113         }
114     }
115
116     let mut buffer = String::new();
117     io::stdin().read_to_string(&mut buffer)?;
118     let lines = buffer.lines();
119     let mut demangled_lines = Vec::new();
120     for mangled in lines {
121         let mut demangled = demangle(mangled).to_string();
122         if let Some(re) = &strip_crate_disambiguators {
123             demangled = re.replace_all(&demangled, REPLACE_COLONS).to_string();
124         }
125         demangled_lines.push(demangled);
126     }
127     demangled_lines.push("".to_string());
128     io::stdout().write_all(demangled_lines.join("\n").as_bytes())?;
129     Ok(())
130 }