chj_util/partialbacktrace.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
//! Wrapper around the `backtrace` crate to show only part of the
//! stack frames (skip some at the beginning and end).
use std::fmt::Write;
use backtrace::Backtrace;
pub struct PartialBacktrace {
bt: Backtrace
}
// Cut away last part from e.g.
// "website::ahtml::HtmlAllocator::new_element::h63d71c1114df562b"
fn cut_hex(mut s: String) -> String {
let err = |s, _msg| -> String {
// warn!("could not cut end of {s:?}: {msg}");
// Happens for "__GI___clone3", "start_thread"
s
};
let mut cs = s.char_indices().rev();
while let Some((_, c)) = cs.next() {
if ! c.is_ascii_hexdigit() {
if c != 'h' { return err(s, "expecting 'h' left of hex digits") }
if let Some((_, c)) = cs.next() {
if c != ':' { return err(s, "expecting ':' left of 'h'") }
if let Some((pos, c)) = cs.next() {
if c != ':' { return err(s, "expecting ':' left of 'h'") }
s.truncate(pos);
return s
} else {
return err(s, "premature end left of ':'")
}
} else {
return err(s, "expecting :: left of 'h'")
}
}
}
return err(s, "string ends early left of hex digits")
}
impl PartialBacktrace {
pub fn new() -> Self {
Self { bt: Backtrace::new() }
}
/// Show the stack frames after the first `skip` ones, until
/// reaching one (excluding it) that refers to a file with a path
/// that ends in `end_file`.
pub fn part_to_string(&self, skip: usize, end_file: &str) -> String {
let mut bt_str = String::new();
let frames = &self.bt.frames()[skip..];
let mut frameno = 0; // starts counting after the skipped area
'outer: for frame in frames.iter() {
let mut subframeno = 0;
for sym in frame.symbols() {
// Have to reimplement everything as Backtrace's frames
// don't have the formatting code, only Backtrace as a
// whole has.
if let Some(path) = sym.filename() {
let p = path.to_string_lossy();
if p.ends_with(end_file) {
break 'outer;
}
let name = sym.name().map(|s| cut_hex(s.to_string()))
.unwrap_or_else(|| " XX missing name ".into());
if subframeno == 0 {
write!(&mut bt_str, "{frameno:4}").unwrap();
} else {
bt_str.push_str(" ");
}
let indent_at = " at ";
write!(&mut bt_str, ": {name}\n\
{indent_at}{p}").unwrap();
if let Some(line) = sym.lineno() {
write!(&mut bt_str, ":{line}").unwrap();
if let Some(col) = sym.colno() {
write!(&mut bt_str, ":{col}").unwrap();
}
}
bt_str.push('\n');
subframeno += 1;
}
}
frameno += 1;
}
writeln!(&mut bt_str, " ({frameno}..{} skipped)",
frames.len() - 1).unwrap();
bt_str
}
}