Skip to main content

reverie/
backtrace.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9mod 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/// A backtrace is a list of stack frames. These stack frames may have originated
27/// from a remote process.
28#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
29pub struct Backtrace {
30    /// Thread ID where the backtrace originated. This can be used to get the
31    /// process it came from.
32    thread_id: Pid,
33
34    // Name of the thread where the backtrace originated, or none of the name
35    // could not be derived (e.g. because the thread had exited).
36    thread_name: Option<String>,
37
38    /// The stack frames in the backtrace.
39    frames: Vec<Frame>,
40}
41
42/// A backtrace with file and line information. This is more heavy-weight than a
43/// normal backtrace.
44#[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/// A stack frame.
52#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
53pub struct Frame {
54    /// The value of the instruction pointer.
55    pub ip: u64,
56    /// True if this frame is inside of a signal handler.
57    pub is_signal: bool,
58}
59
60/// A stack frame with debugging information.
61#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
62pub struct PrettyFrame {
63    /// The raw stack frame information.
64    frame: Frame,
65    /// The symbol as found in the symbol table.
66    symbol: Option<Symbol>,
67    /// The source file and line where the instruction pointer is located.
68    locations: Vec<Location>,
69}
70
71/// A symbol from a frame.
72#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
73pub struct Symbol {
74    /// Name of the (potentially demangled) symbol.
75    pub name: String,
76    /// Address of the symbol.
77    pub address: u64,
78    /// The offset of the instruction pointer from `address`.
79    pub offset: u64,
80}
81
82/// The location of a symbol.
83#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
84pub struct Location {
85    /// The path to the source file.
86    file: PathBuf,
87    /// The line in the file. 0 if unknown.
88    line: u32,
89    /// The column in the file. 0 if unknown.
90    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    /// Creates a backtrace from a thread ID and frames.
133    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    /// Generates a pretty backtrace that includes file and line information for
142    /// each frame.
143    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                // Find the file + line number of the instruction pointer.
158                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                            // FIXME support Split DWARF
167                            // let dwo = do_split_dwarf_load(load);
168                            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                    // Find symbol using the symbol table.
185                    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    /// Generates a pretty backtrace that may includes file and line information
208    /// for each frame, if available.
209    pub fn force_pretty(&self) -> PrettyBacktrace {
210        if let Ok(pretty) = self.pretty() {
211            return pretty;
212        }
213
214        // Convert to the structure of a pretty backtrace, but without any
215        // enrichment
216        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    /// Returns an iterator over the frames in the backtrace.
233    pub fn iter(&self) -> impl Iterator<Item = &Frame> {
234        self.frames.iter()
235    }
236
237    /// Returns the thread ID where the backtrace originated.
238    pub fn thread_id(&self) -> Pid {
239        self.thread_id
240    }
241
242    /// Returns the name of the thread for this backtrace.
243    pub fn thread_name(&self) -> Option<String> {
244        self.thread_name.clone()
245    }
246}
247
248impl PrettyBacktrace {
249    /// Returns an iterator over the frames in the backtrace.
250    pub fn iter(&self) -> impl Iterator<Item = &PrettyFrame> {
251        self.frames.iter()
252    }
253
254    /// Returns the name of the thread for this backtrace.
255    pub fn thread_name(&self) -> Option<String> {
256        self.thread_name.clone()
257    }
258}
259
260impl PrettyFrame {
261    /// The symbol for this frame, if any.
262    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        // Ugly formatting with no symbol resolution.
303        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            // Frame number
324            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    // Remove trailing newline character
345    assert_eq!(name.pop(), Some('\n'));
346
347    Ok(name)
348}