headless_lms_utils/error/
backtrace_formatter.rs

1/*!
2Custom formatting for stack backtraces inteded to be printed to the console for developers.
3*/
4
5use core::fmt;
6
7use backtrace::{Backtrace, BacktraceFmt, BacktraceFrame, PrintFmt, SymbolName};
8
9/**
10Formats backtraces for printing but omits unnecessary stack frames.
11
12Customized version of `impl fmt::Debug` from the `Backtrace` crate.
13*/
14pub fn format_backtrace(backtrace: &Backtrace, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
15    let cwd = std::env::current_dir().ok();
16    let cwd_copy = cwd.clone();
17    let mut print_path = move |fmt: &mut fmt::Formatter<'_>,
18                               path: backtrace::BytesOrWideString<'_>| {
19        let cwd_path_buf = path.into_path_buf();
20
21        if let Some(cwd) = &cwd_copy {
22            if let Ok(suffix) = cwd_path_buf.strip_prefix(cwd) {
23                return fmt::Display::fmt(&suffix.display(), fmt);
24            }
25        }
26
27        fmt::Display::fmt(&cwd_path_buf.display(), fmt)
28    };
29
30    let mut f = BacktraceFmt::new(fmt, PrintFmt::Short, &mut print_path);
31    f.add_context()?;
32
33    let mut skipped_n_frames = 0;
34    let frames = backtrace.frames();
35    // Skip 1 because our errors are always contructed with a constructor and we want the trace to start where the constructor was called.
36    for frame in frames.iter().skip(1) {
37        let frame_from_this_project = frame.symbols().iter().any(|symbol| {
38            symbol
39                .filename()
40                .map(|path| {
41                    if let Some(cwd) = cwd.clone() {
42                        // If the path starts with the cwd, we assume it's from the current crate
43                        path.starts_with(cwd)
44                    } else {
45                        false
46                    }
47                })
48                .unwrap_or(false)
49        });
50        if !frame_from_this_project {
51            skipped_n_frames += 1;
52            continue;
53        } else {
54            if skipped_n_frames > 0 {
55                print_filtered_frame_placeholder(skipped_n_frames, &mut f, frame)?;
56            }
57            skipped_n_frames = 0;
58        }
59
60        f.frame().backtrace_frame(frame)?;
61    }
62    if skipped_n_frames > 0 {
63        // Just need some frame, taking the first one because the last one has a null adddress and those are not printed by default.
64        if let Some(some_frame) = frames.first() {
65            print_filtered_frame_placeholder(skipped_n_frames, &mut f, some_frame)?;
66        }
67    }
68
69    f.finish()?;
70    Ok(())
71}
72
73fn print_filtered_frame_placeholder(
74    skipped_n_frames: i32,
75    f: &mut BacktraceFmt,
76    reference_frame: &BacktraceFrame,
77) -> fmt::Result {
78    let mut backtrace_frame_fmt = f.frame();
79    let word = if skipped_n_frames == 1 {
80        "frame"
81    } else {
82        "frames"
83    };
84    backtrace_frame_fmt.print_raw(
85        // Using some frame ip to get the formatter to print.
86        reference_frame.ip(),
87        Some(SymbolName::new(
88            format!("<----- {} filtered {} ----->", skipped_n_frames, word).as_bytes(),
89        )),
90        None,
91        None,
92    )?;
93    Ok(())
94}