backtrace_parser/
lib.rs

1//! This crate implements a parser for backtraces.
2//!
3//! The aim is to parse backtraces in the standard format
4//! that any Rust program can generate, for instance when
5//! crashing due to a panic, by creating a `failure::Error`,
6//! or by using the [`backtrace`][1] crate directly.
7//!
8//! The parser follows a zero-copy approach, which means that
9//! the input string can be provided by reference, and will not
10//! be copied during parsing. This has the effect that parsing
11//! a captured backtrace tends to be very performant.
12//!
13//! [1]: https://crates.io/crates/backtrace
14//!
15//! ## Example
16//!
17//! ```rust
18//! use backtrace_parser::Backtrace;
19//!
20//! # let input = "stack backtrace: 0: 0x0 - <no info>";
21//! let backtrace = Backtrace::parse(input).unwrap();
22//!
23//! for frame in backtrace.frames() {
24//!     for symbol in frame.symbols() {
25//!         println!("symbol: {:?}", symbol);
26//!     }
27//! }
28//! ```
29//!
30
31#![deny(warnings)]
32#![deny(missing_docs)]
33#![deny(missing_debug_implementations)]
34
35extern crate pest;
36#[macro_use]
37extern crate pest_derive;
38
39use std::error;
40use std::fmt;
41use std::path::Path;
42
43use pest::Parser;
44
45mod parser;
46use self::parser::{BacktraceParser, Rule};
47
48#[derive(Debug)]
49/// Represents a parser error.
50pub struct Error<'a> {
51    inner: pest::Error<'a, Rule>,
52}
53
54impl<'a> fmt::Display for Error<'a> {
55    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56        fmt::Display::fmt(&self.inner, f)
57    }
58}
59
60impl<'a> error::Error for Error<'a> {}
61
62#[derive(Debug)]
63/// Represents a parsed backtrace.
64pub struct Backtrace<'a> {
65    pairs: pest::iterators::Pairs<'a, Rule>,
66}
67
68impl<'a> Backtrace<'a> {
69    /// Parse the provided input string and return either a parsed backtrace,
70    /// or a parse error.
71    pub fn parse(input: &'a str) -> Result<Backtrace<'a>, Error<'a>> {
72        let pairs =
73            BacktraceParser::parse(Rule::backtrace, input).map_err(|err| Error { inner: err })?;
74
75        Ok(Backtrace { pairs })
76    }
77
78    /// Create an iterator over the stack frames in this backtrace.
79    pub fn frames(&self) -> Frames<'a> {
80        Frames {
81            inner: self.pairs.clone(),
82        }
83    }
84}
85
86#[derive(Debug)]
87/// Iterator over the stack frames in a parsed backtrace.
88pub struct Frames<'a> {
89    inner: pest::iterators::Pairs<'a, Rule>,
90}
91
92impl<'a> Iterator for Frames<'a> {
93    type Item = Frame<'a>;
94
95    fn next(&mut self) -> Option<Self::Item> {
96        if let Some(frame) = self.inner.next() {
97            debug_assert!(frame.as_rule() == Rule::frame);
98            let mut frame_inner = frame.into_inner();
99
100            let frame_index = frame_inner.next().unwrap();
101            debug_assert!(frame_index.as_rule() == Rule::frame_index);
102            let frame_pointer = frame_inner.next().unwrap();
103            debug_assert!(frame_pointer.as_rule() == Rule::frame_pointer);
104
105            Some(Frame { pairs: frame_inner })
106        } else {
107            None
108        }
109    }
110}
111
112#[derive(Debug)]
113/// Represents a parsed stack frame.
114pub struct Frame<'a> {
115    pairs: pest::iterators::Pairs<'a, Rule>,
116}
117
118impl<'a> Frame<'a> {
119    /// Create an iterator over the symbols in this stack frame.
120    pub fn symbols(&self) -> Symbols<'a> {
121        Symbols {
122            inner: self.pairs.clone(),
123        }
124    }
125}
126
127#[derive(Debug)]
128/// Iterator over the symbols in a parsed stack frame.
129pub struct Symbols<'a> {
130    inner: pest::iterators::Pairs<'a, Rule>,
131}
132
133impl<'a> Iterator for Symbols<'a> {
134    type Item = Symbol<'a>;
135
136    fn next(&mut self) -> Option<Self::Item> {
137        if let Some(symbol) = self.inner.next() {
138            match symbol.as_rule() {
139                Rule::symbol_non_empty => {
140                    let mut parsed_symbol = Symbol {
141                        name: None,
142                        filename: None,
143                        lineno: None,
144                    };
145                    let mut symbol_inner = symbol.into_inner();
146                    let symbol_name = symbol_inner.next().unwrap();
147                    match symbol_name.as_rule() {
148                        Rule::symbol_name_known => {
149                            parsed_symbol.name = Some(symbol_name.into_span().as_str())
150                        }
151                        _ => {}
152                    }
153                    if let Some(symbol_location) = symbol_inner.next() {
154                        debug_assert!(symbol_location.as_rule() == Rule::symbol_location);
155                        let mut symbol_location_inner = symbol_location.into_inner();
156                        let symbol_location_path = symbol_location_inner.next().unwrap();
157                        debug_assert!(symbol_location_path.as_rule() == Rule::symbol_location_path);
158                        parsed_symbol.filename =
159                            Some(Path::new(symbol_location_path.into_span().as_str()));
160                        let symbol_location_lineno = symbol_location_inner.next().unwrap();
161                        debug_assert!(
162                            symbol_location_lineno.as_rule() == Rule::symbol_location_lineno
163                        );
164                        parsed_symbol.lineno =
165                            symbol_location_lineno.into_span().as_str().parse().ok();
166                    }
167                    Some(parsed_symbol)
168                }
169                _ => None,
170            }
171        } else {
172            None
173        }
174    }
175}
176
177#[derive(Debug)]
178/// Represents a parsed symbol.
179pub struct Symbol<'a> {
180    name: Option<&'a str>,
181    filename: Option<&'a Path>,
182    lineno: Option<u32>,
183}
184
185impl<'a> Symbol<'a> {
186    /// Return the name of the symbol, if resolved.
187    pub fn name(&self) -> Option<&'a str> {
188        self.name
189    }
190
191    /// Return the path of the source file, if known.
192    pub fn filename(&self) -> Option<&'a Path> {
193        self.filename
194    }
195
196    /// Return the line number in source file, if known.
197    pub fn lineno(&self) -> Option<u32> {
198        self.lineno
199    }
200}