1 //! Demangles rustc mangled names.
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.
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.
11 //! To use `rust-demangler`, first build the tool with:
14 //! $ ./x.py build rust-demangler
17 //! Then, with `llvm-cov` for example, add the `-Xdemangler=...` option:
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
25 //! Note regarding crate disambiguators:
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:
32 //! <generics::Firework<f64> as core[a7a74cee373f048]::ops::drop::Drop>::drop
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.
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.
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.
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.
61 use rustc_demangle
::demangle
;
62 use std
::io
::{self, Read, Write}
;
64 const REPLACE_COLONS
: &str = "::";
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.
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.
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.
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());
87 let mut args
= std
::env
::args();
88 let progname
= args
.next().unwrap();
90 if arg
== "--disambiguators" || arg
== "-d" {
91 strip_crate_disambiguators
= None
;
94 eprintln
!("Usage: {} [-d|--disambiguators]", progname
);
97 "This tool converts a list of Rust mangled symbols (one per line) into a\n\
98 corresponding list of demangled symbols."
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)."
108 "By default, crate disambiguators are removed, using a heuristics-based regular\n\
109 expression. (See the `rust-demangler` doc comments for more information.)"
112 std
::process
::exit(1)
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();
125 demangled_lines
.push(demangled
);
127 demangled_lines
.push("".to_string());
128 io
::stdout().write_all(demangled_lines
.join("\n").as_bytes())?
;