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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
use std::path::Path; use std::sync::Mutex; use backtrace::Backtrace; struct Inner { backtrace: Option<Backtrace>, resolved: bool, skip: usize, } impl Inner { fn backtrace(&mut self) -> &Backtrace { if !self.resolved { let this_file: &Path = Path::new(file!()); let mut b = self.backtrace.take().unwrap(); b.resolve(); let mut frames: Vec<_> = b.into(); let mut first = None; for (i, f) in frames.iter().enumerate() { if first.is_none() { if f.symbols().iter().find(|s| s.filename() == Some(this_file)).is_some() { first = Some(i); } } else { if f.symbols().iter().find(|s| s.filename() == Some(this_file)).is_some() { first = Some(i); } else { break; } } } if let Some(i) = first { frames.drain(0..=(i + self.skip)); } let mut last = None; for (mut i, f) in frames.iter().enumerate() { if f.symbols().iter().find(|s| if let Some(n) = s.name().map(|n| n.as_str().unwrap_or("")) { if n.starts_with("_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$") { true } else if n.starts_with("_ZN4test8run_test28_$u7b$$u7b$closure$u7d$$u7d$") { i -= 1; true } else { false } } else { false }).is_some() { last = Some(i); break; } } if let Some(i) = last { frames.drain(i..); } assert!(!frames.is_empty()); self.backtrace = Some(frames.into()); self.resolved = true; } self.backtrace.as_ref().unwrap() } } pub struct Stacktrace(Mutex<Inner>); impl Stacktrace { pub fn new_skip(skip: usize) -> Self { Stacktrace(Mutex::new(Inner { backtrace: Some(Backtrace::new_unresolved()), resolved: false, skip, })) } pub fn new() -> Self { Self::new_skip(0) } } impl std::fmt::Display for Stacktrace { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let mut inner = self.0.lock().unwrap(); write!(f, "{:?}", inner.backtrace()) } } impl std::fmt::Debug for Stacktrace { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { struct BacktraceDebug<'a>(&'a Backtrace); impl<'a> std::fmt::Debug for BacktraceDebug<'a> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "Backtrace [{} frames...]", self.0.frames().len()) } } let inner = self.0.lock().unwrap(); f.debug_struct("Stacktrace") .field("backtrace", &inner.backtrace.as_ref().map(|b| BacktraceDebug(b))) .field("resolved", &inner.resolved) .field("skip", &inner.skip) .finish() } }