rudy_dwarf/symbols/
names.rs1use std::fmt;
4
5use anyhow::Context;
6use rudy_parser as parser;
7use rudy_types::Layout;
8
9#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct ModuleName {
11 pub segments: Vec<String>,
12}
13
14#[derive(Clone)]
15pub struct TypeName {
16 pub module: ModuleName,
19 pub name: String,
21 pub full_name: String,
24 pub typedef: Layout,
25}
26
27impl fmt::Debug for TypeName {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 write!(f, "{self}")
30 }
31}
32
33impl PartialEq for TypeName {
34 fn eq(&self, other: &Self) -> bool {
35 self.module == other.module && self.name == other.name
36 }
37}
38impl Eq for TypeName {}
39impl PartialOrd for TypeName {
40 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
41 Some(self.cmp(other))
42 }
43}
44
45impl Ord for TypeName {
46 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
47 self.module
48 .cmp(&other.module)
49 .then_with(|| self.name.cmp(&other.name))
50 }
51}
52
53impl std::hash::Hash for TypeName {
54 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
55 self.module.hash(state);
56 self.name.hash(state);
57 }
58}
59
60impl TypeName {
61 pub fn parse(module_path: &[String], name: &str) -> anyhow::Result<Self> {
62 fn known_bad_case(path: &str) -> bool {
63 path.contains("{closure_env#") || path.contains("{impl#") || path.contains("{extern#")
64 }
65
66 let full_name = if module_path.is_empty() {
69 name.to_string()
70 } else {
71 format!("{}::{}", module_path.join("::"), name)
72 };
73
74 tracing::trace!(
75 "TypeName::parse - module_path: {:?}, name: {}, full_name: {}",
76 module_path,
77 name,
78 full_name
79 );
80
81 let parsed_type = parser::parse_type(&full_name).map_err(|e| {
82 if !known_bad_case(&full_name) {
83 tracing::error!("Failed to parse type name `{full_name}`: {e}");
84 }
85 anyhow::anyhow!("Failed to parse type name `{full_name}`")
86 })?;
87
88 let typedef = parsed_type.as_layout();
90
91 tracing::trace!("TypeName::parse - name: {name}, typedef: {typedef:?}",);
92
93 Ok(TypeName {
94 module: ModuleName {
95 segments: module_path.to_vec(),
96 },
97 name: typedef.display_name(),
98 full_name,
99 typedef,
100 })
101 }
102}
103
104impl fmt::Display for TypeName {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 write!(f, "{}", self.full_name)
107 }
108}
109
110#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
111pub struct RawSymbol {
112 name_bytes: Vec<u8>,
113}
114
115impl fmt::Debug for RawSymbol {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 let name_str = std::str::from_utf8(&self.name_bytes)
118 .unwrap_or("<invalid UTF-8>")
119 .to_string();
120 write!(f, "RawSymbol({name_str})")
121 }
122}
123
124impl RawSymbol {
125 pub fn new(name_bytes: Vec<u8>) -> Self {
126 Self { name_bytes }
127 }
128
129 pub fn demangle(&self) -> anyhow::Result<SymbolName> {
130 demangle_symbol(self.clone())
131 }
132}
133
134#[derive(Clone, PartialEq, Eq, Hash, salsa::Update)]
135pub struct SymbolName {
136 pub lookup_name: String,
137 pub hash: String,
138 pub module_path: Vec<String>,
139 full_path: String,
140}
141
142impl SymbolName {
143 pub fn parse(path: &str) -> anyhow::Result<Self> {
144 fn known_bad_case(path: &str) -> bool {
145 path.contains('@')
146 || path.contains("{{")
147 || path.contains("__rustc[")
148 || path.starts_with('$')
149 || path.contains("DW.ref.rust_eh_personality")
150 || path.ends_with(".o")
151 || path.ends_with(".c")
152 || path.ends_with("cgu.0")
153 || path.starts_with("compiler_builtins.")
154 || path.starts_with(|c: char| c.is_ascii_digit())
155 || path.ends_with(".0")
156 || path.ends_with(".0$tlv$init")
157 }
158
159 let (module_path, lookup_name, hash) = parser::parse_symbol(path).map_err(|e| {
160 if !known_bad_case(path) {
162 tracing::error!("Failed to parse symbol path `{path}`: {e}");
163 }
164 anyhow::anyhow!("Failed to parse symbol path `{path}`")
165 })?;
166
167 Ok(SymbolName {
168 full_path: path.to_string(),
169 lookup_name,
170 hash: hash.unwrap_or_default(),
171 module_path,
172 })
173 }
174}
175
176impl fmt::Debug for SymbolName {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 write!(f, "{}", self.full_path)
179 }
180}
181
182impl fmt::Display for SymbolName {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 write!(
185 f,
186 "{}",
187 self.full_path
189 .trim_end_matches(&self.hash)
190 .trim_end_matches("::")
191 )
192 }
193}
194
195impl SymbolName {
196 pub fn matches_name_and_module(&self, name: &str, module: &[String]) -> bool {
197 self.lookup_name == name && self.module_path.ends_with(module)
198 }
199}
200
201impl PartialOrd for SymbolName {
202 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
203 Some(self.cmp(other))
204 }
205}
206
207impl Ord for SymbolName {
208 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
209 self.lookup_name
210 .cmp(&other.lookup_name)
211 .then_with(|| self.module_path.cmp(&other.module_path))
212 .then_with(|| self.full_path.cmp(&other.full_path))
213 }
214}
215
216fn demangle_symbol(symbol: RawSymbol) -> anyhow::Result<SymbolName> {
217 let name_str = std::str::from_utf8(&symbol.name_bytes)
218 .context("Failed to convert symbol bytes to string")?;
219 let name_str = if name_str.starts_with("__Z") {
220 &name_str[1..]
223 } else {
224 name_str
225 };
226 let demangled = rustc_demangle::try_demangle(name_str)
227 .map_err(|_| anyhow::anyhow!("could not demangle symbol as Rust symbol"))?;
228 SymbolName::parse(&demangled.to_string()).context("Failed to parse demangled symbol")
229}