ghostscope_dwarf/core/
demangle.rs

1//! Demangling helpers for Rust and C++ symbols
2
3use gimli::DwLang;
4
5/// Demangle a symbol string using language hint when available.
6/// Returns None if demangling fails or is not applicable.
7pub fn demangle_by_lang(lang: Option<DwLang>, s: &str) -> Option<String> {
8    match lang {
9        Some(gimli::DW_LANG_Rust) => demangle_rust(s),
10        Some(gimli::DW_LANG_C_plus_plus)
11        | Some(gimli::DW_LANG_C_plus_plus_11)
12        | Some(gimli::DW_LANG_C_plus_plus_14)
13        | Some(gimli::DW_LANG_C_plus_plus_17)
14        | Some(gimli::DW_LANG_C_plus_plus_20) => demangle_cpp(s),
15        _ => {
16            // Try common patterns heuristically
17            if is_rust_mangled(s) {
18                demangle_rust(s)
19            } else if is_itanium_cpp_mangled(s) {
20                demangle_cpp(s)
21            } else {
22                None
23            }
24        }
25    }
26}
27
28/// Return a friendly leaf name from a demangled full name.
29/// For Rust, strips the trailing ::hxxxxxxxx hash if present, then returns the last path segment.
30pub fn demangled_leaf(full: &str) -> String {
31    // Strip Rust hash suffix like ::h1234abcd... if present
32    // Require at least a minimal number of hex digits to avoid truncating valid names like
33    // "foo::h" or "foo::h264". Use a conservative threshold of >= 8 hex digits.
34    let trimmed = match full.rfind("::h") {
35        Some(pos) => {
36            let start = pos + 3; // after '::h'
37            if start < full.len() {
38                let suffix = &full[start..];
39                if suffix.len() >= 8 && suffix.chars().all(|c| c.is_ascii_hexdigit()) {
40                    &full[..pos]
41                } else {
42                    full
43                }
44            } else {
45                full
46            }
47        }
48        None => full,
49    };
50    // Take the last path segment by '::'
51    trimmed.rsplit("::").next().unwrap_or(trimmed).to_string()
52}
53
54/// Heuristic: Rust v0 mangling starts with "_R".
55pub fn is_rust_mangled(s: &str) -> bool {
56    s.starts_with("_R")
57}
58
59/// Heuristic: Itanium C++ mangling starts with "_Z".
60pub fn is_itanium_cpp_mangled(s: &str) -> bool {
61    s.starts_with("_Z")
62}
63
64fn demangle_rust(s: &str) -> Option<String> {
65    match rustc_demangle::try_demangle(s) {
66        Ok(sym) => Some(sym.to_string()),
67        Err(_) => None,
68    }
69}
70
71fn demangle_cpp(s: &str) -> Option<String> {
72    match cpp_demangle::Symbol::new(s) {
73        Ok(sym) => Some(sym.to_string()),
74        Err(_) => None,
75    }
76}