Skip to content

Commit 2b4727b

Browse files
author
Glauber Costa
authored
implement From<Frame> for BacktraceFrame (#420)
* implement From<Frame> for BacktraceFrame There are situations where we can only capture raw `Frame` (example: capturing from a signal handler), but it could still be that we can process them later and are not fully nostd in the environment. With a conversion from Frame to BacktraceFrame, this is trivial. But without it, we can't really use the pretty printers as they are reliant on BacktraceFrame, not Frame. Fixes: #419 * add test * fixes for miri Every backtrace that miri generates - conversion or no conversion, has totally different addresses. They all resolve to the same thing, though, so test with that. * fix another broken test `cargo test --no-default-features --features std` fails because frames are not resolved to symbols in that case. We'll just remove the assert, rather than disabling the whole test on that case. At least the code paths are stressed.
1 parent 221483e commit 2b4727b

File tree

1 file changed

+65
-0
lines changed

1 file changed

+65
-0
lines changed

src/capture.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,15 @@ impl From<Vec<BacktraceFrame>> for Backtrace {
249249
}
250250
}
251251

252+
impl From<crate::Frame> for BacktraceFrame {
253+
fn from(frame: crate::Frame) -> BacktraceFrame {
254+
BacktraceFrame {
255+
frame: Frame::Raw(frame),
256+
symbols: None,
257+
}
258+
}
259+
}
260+
252261
impl Into<Vec<BacktraceFrame>> for Backtrace {
253262
fn into(self) -> Vec<BacktraceFrame> {
254263
self.frames
@@ -518,3 +527,59 @@ mod serde_impls {
518527
}
519528
}
520529
}
530+
531+
#[cfg(test)]
532+
mod tests {
533+
use super::*;
534+
535+
#[test]
536+
fn test_frame_conversion() {
537+
// captures an original backtrace, and makes sure that the manual conversion
538+
// to frames yields the same results.
539+
let mut bt = Backtrace::new();
540+
bt.resolve();
541+
let original_frames = bt.frames();
542+
543+
let mut frames = vec![];
544+
crate::trace(|frame| {
545+
let converted = BacktraceFrame::from(frame.clone());
546+
frames.push(converted);
547+
true
548+
});
549+
550+
let mut manual = Backtrace::from(frames);
551+
manual.resolve();
552+
let frames = manual.frames();
553+
554+
// the first frames can be different because we call from slightly different places,
555+
// and the `trace` version has an extra capture. But because of inlining the number of
556+
// frames that differ may be different between release and debug versions. Plus who knows
557+
// what the compiler will do in the future. So we just take 4 frames from the end and make
558+
// sure they match
559+
//
560+
// For miri, every backtrace that is taken yields different addresses and different
561+
// instruction pointers. That is irrespective of conversion and happens even if
562+
// Backtrace::new() is invoked in a row. They all resolve to the same names, though, so to
563+
// make sure this works on miri we resolve the symbols and compare the results. It is okay
564+
// if some addresses don't have symbols, but if we scan enough frames at least some will do
565+
for (converted, og) in frames
566+
.iter()
567+
.rev()
568+
.take(4)
569+
.zip(original_frames.iter().rev().take(4))
570+
{
571+
let converted_symbols = converted.symbols();
572+
let og_symbols = og.symbols();
573+
574+
assert_eq!(og_symbols.len(), converted_symbols.len());
575+
for (os, cs) in og_symbols.iter().zip(converted_symbols.iter()) {
576+
assert_eq!(
577+
os.name().map(|x| x.as_bytes()),
578+
cs.name().map(|x| x.as_bytes())
579+
);
580+
assert_eq!(os.filename(), cs.filename());
581+
assert_eq!(os.lineno(), cs.lineno());
582+
}
583+
}
584+
}
585+
}

0 commit comments

Comments
 (0)