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
use sentry_core::protocol::{Frame, Stacktrace};

use crate::utils::function_starts_with;

lazy_static::lazy_static! {
    static ref WELL_KNOWN_SYS_MODULES: Vec<&'static str> = vec![
        "std::",
        "core::",
        "alloc::",
        "backtrace::",
        "sentry::",
        "sentry_core::",
        "sentry_types::",
        // these are not modules but things like __rust_maybe_catch_panic
        "__rust_",
        "___rust_",
        // these are well-known library frames
        "anyhow::",
        "log::",
    ];

    static ref WELL_KNOWN_BORDER_FRAMES: Vec<&'static str> = vec![
        "std::panicking::begin_panic",
        "core::panicking::panic",
        // well-known library frames
        "anyhow::",
        "<sentry_log::Logger as log::Log>::log",
    ];

    // TODO: remove all of this together with the deprecated `error_chain` support
    static ref SECONDARY_BORDER_FRAMES: Vec<(&'static str, &'static str)> = vec![
        ("error_chain::make_backtrace", "<T as core::convert::Into<U>>::into")
    ];
}

/// A helper function to trim a stacktrace.
pub fn trim_stacktrace<F>(stacktrace: &mut Stacktrace, f: F)
where
    F: Fn(&Frame, &Stacktrace) -> bool,
{
    let known_cutoff = stacktrace
        .frames
        .iter()
        .rev()
        .position(|frame| match frame.function {
            Some(ref func) => is_well_known(&func) || f(frame, stacktrace),
            None => false,
        });

    if let Some(cutoff) = known_cutoff {
        let secondary = {
            let func = stacktrace.frames[stacktrace.frames.len() - cutoff - 1]
                .function
                .as_ref()
                .unwrap();

            SECONDARY_BORDER_FRAMES
                .iter()
                .filter_map(|&(primary, secondary)| {
                    if function_starts_with(func, primary) {
                        Some(secondary)
                    } else {
                        None
                    }
                })
                .next()
        };
        let trunc = stacktrace.frames.len() - cutoff - 1;
        stacktrace.frames.truncate(trunc);

        if let Some(secondary) = secondary {
            let secondary_cutoff =
                stacktrace
                    .frames
                    .iter()
                    .rev()
                    .position(|frame| match frame.function {
                        Some(ref func) => function_starts_with(&func, secondary),
                        None => false,
                    });

            if let Some(cutoff) = secondary_cutoff {
                let trunc = stacktrace.frames.len() - cutoff - 1;
                stacktrace.frames.truncate(trunc);
            }
        }
    }
}

/// Checks if a function is considered to be not in-app
pub fn is_sys_function(func: &str) -> bool {
    WELL_KNOWN_SYS_MODULES
        .iter()
        .any(|m| function_starts_with(func, m))
}

/// Checks if a function is a well-known system function
fn is_well_known(func: &str) -> bool {
    WELL_KNOWN_BORDER_FRAMES
        .iter()
        .any(|m| function_starts_with(&func, m))
}