1mod cache;
10mod library;
11mod symbols;
12
13use core::fmt;
14use std::io;
15use std::path::PathBuf;
16
17use addr2line::LookupContinuation;
18use addr2line::LookupResult;
19use serde::Deserialize;
20use serde::Serialize;
21
22use self::cache::cache;
23use self::library::Libraries;
24use super::Pid;
25
26#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
29pub struct Backtrace {
30 thread_id: Pid,
33
34 thread_name: Option<String>,
37
38 frames: Vec<Frame>,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
45pub struct PrettyBacktrace {
46 thread_id: Pid,
47 thread_name: Option<String>,
48 frames: Vec<PrettyFrame>,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
53pub struct Frame {
54 pub ip: u64,
56 pub is_signal: bool,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
62pub struct PrettyFrame {
63 frame: Frame,
65 symbol: Option<Symbol>,
67 locations: Vec<Location>,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
73pub struct Symbol {
74 pub name: String,
76 pub address: u64,
78 pub offset: u64,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
84pub struct Location {
85 file: PathBuf,
87 line: u32,
89 column: u32,
91}
92
93impl fmt::Display for Frame {
94 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95 write!(f, "{:#016x}: ???", self.ip)?;
96
97 if self.is_signal {
98 write!(f, " (in signal handler)")?;
99 }
100
101 Ok(())
102 }
103}
104
105impl fmt::Display for Location {
106 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107 if self.column != 0 {
108 write!(f, "{}:{}:{}", self.file.display(), self.line, self.column)
109 } else {
110 write!(f, "{}:{}", self.file.display(), self.line)
111 }
112 }
113}
114
115impl fmt::Display for PrettyFrame {
116 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117 if let Some(location) = self.locations.first() {
118 write!(f, "{} at {}", self.frame, location)
119 } else {
120 write!(f, "{}", self.frame)
121 }
122 }
123}
124
125impl fmt::Display for Symbol {
126 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127 write!(f, "{} + {:#x}", self.name, self.offset)
128 }
129}
130
131impl Backtrace {
132 pub fn new(thread_id: Pid, frames: Vec<Frame>) -> Self {
134 Self {
135 thread_id,
136 thread_name: thread_name(thread_id).ok(),
137 frames,
138 }
139 }
140
141 pub fn pretty(&self) -> Result<PrettyBacktrace, anyhow::Error> {
144 let libraries = Libraries::new(self.thread_id)?;
145
146 let mut frames = Vec::new();
147 let mut cache = cache();
148
149 for frame in &self.frames {
150 let ip = frame.ip;
151 let mut locations = Vec::new();
152 let mut symbol = None;
153
154 if let Some((library, addr)) = libraries.ip_to_vaddr(ip) {
155 let symbols = cache.load(library)?;
156
157 let mut lookup_result = symbols.find_frames(addr);
159 if let Ok(mut source_frames) = loop {
160 match lookup_result {
161 LookupResult::Output(result) => break result,
162 LookupResult::Load {
163 load: _,
164 continuation,
165 } => {
166 lookup_result = continuation.resume(None);
169 }
170 }
171 } {
172 while let Ok(Some(f)) = source_frames.next() {
173 if let Some(loc) = f.location {
174 locations.push(Location {
175 file: loc.file.unwrap().into(),
176 line: loc.line.unwrap_or(0),
177 column: loc.column.unwrap_or(0),
178 });
179 }
180 }
181 }
182
183 if symbol.is_none() {
184 symbol = symbols.find_symbol(addr).map(|sym| Symbol {
186 name: addr2line::demangle_auto(sym.name().into(), None).into(),
187 address: sym.address(),
188 offset: addr + symbols.base_addr() - sym.address(),
189 });
190 }
191 }
192
193 frames.push(PrettyFrame {
194 frame: frame.clone(),
195 symbol,
196 locations,
197 });
198 }
199
200 Ok(PrettyBacktrace {
201 thread_id: self.thread_id,
202 thread_name: self.thread_name.clone(),
203 frames,
204 })
205 }
206
207 pub fn force_pretty(&self) -> PrettyBacktrace {
210 if let Ok(pretty) = self.pretty() {
211 return pretty;
212 }
213
214 let frames = self
217 .frames
218 .iter()
219 .map(|frame| PrettyFrame {
220 frame: frame.clone(),
221 symbol: None,
222 locations: Vec::new(),
223 })
224 .collect();
225 PrettyBacktrace {
226 thread_id: self.thread_id,
227 thread_name: self.thread_name.clone(),
228 frames,
229 }
230 }
231
232 pub fn iter(&self) -> impl Iterator<Item = &Frame> {
234 self.frames.iter()
235 }
236
237 pub fn thread_id(&self) -> Pid {
239 self.thread_id
240 }
241
242 pub fn thread_name(&self) -> Option<String> {
244 self.thread_name.clone()
245 }
246}
247
248impl PrettyBacktrace {
249 pub fn iter(&self) -> impl Iterator<Item = &PrettyFrame> {
251 self.frames.iter()
252 }
253
254 pub fn thread_name(&self) -> Option<String> {
256 self.thread_name.clone()
257 }
258}
259
260impl PrettyFrame {
261 pub fn symbol(&self) -> Option<&Symbol> {
263 self.symbol.as_ref()
264 }
265}
266
267impl IntoIterator for Backtrace {
268 type Item = Frame;
269 type IntoIter = std::vec::IntoIter<Self::Item>;
270
271 fn into_iter(self) -> Self::IntoIter {
272 self.frames.into_iter()
273 }
274}
275
276impl IntoIterator for PrettyBacktrace {
277 type Item = PrettyFrame;
278 type IntoIter = std::vec::IntoIter<Self::Item>;
279
280 fn into_iter(self) -> Self::IntoIter {
281 self.frames.into_iter()
282 }
283}
284
285impl From<Backtrace> for Vec<Frame> {
286 fn from(bt: Backtrace) -> Self {
287 bt.frames
288 }
289}
290
291impl fmt::Display for Backtrace {
292 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293 let thread_name = self.thread_name();
294 let thread_name = thread_name.as_deref();
295 let thread_name = thread_name.unwrap_or("<unknown name>");
296 writeln!(
297 f,
298 "Stack trace for thread {} ({:?}):",
299 self.thread_id, thread_name
300 )?;
301
302 for frame in &self.frames {
304 writeln!(f, "{}", frame)?;
305 }
306
307 Ok(())
308 }
309}
310
311impl fmt::Display for PrettyBacktrace {
312 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
313 let thread_name = self.thread_name();
314 let thread_name = thread_name.as_deref();
315 let thread_name = thread_name.unwrap_or("<unknown name>");
316 writeln!(
317 f,
318 "Stack trace for thread {} ({:?}):",
319 self.thread_id, thread_name
320 )?;
321
322 for (i, frame) in self.frames.iter().enumerate() {
323 write!(f, "{:>4}: ", i)?;
325
326 match &frame.symbol {
327 Some(symbol) => writeln!(f, "{:#016x}: {:#}", frame.frame.ip, symbol)?,
328 None => writeln!(f, "{:#}", frame.frame)?,
329 }
330
331 for location in &frame.locations {
332 writeln!(f, " at {}", location)?;
333 }
334 }
335
336 Ok(())
337 }
338}
339
340fn thread_name(thread_id: Pid) -> io::Result<String> {
341 let mut name =
342 crate::process::launch_window::read_to_string(format!("/proc/{}/comm", thread_id))?;
343
344 assert_eq!(name.pop(), Some('\n'));
346
347 Ok(name)
348}