use crate::core::*;
use crate::coverage::*;
use crate::crash::*;
use crate::error::*;
use crate::hooks::*;
use crate::memory::*;
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub struct Backtrace {
pub backtrace: Vec<u64>,
}
impl Backtrace {
const BT_INSNS: &'static [&'static str] = &["bl", "blr"];
pub fn new() -> Self {
Self {
backtrace: Vec::new(),
}
}
pub fn clear(&mut self) {
self.backtrace.clear();
}
pub fn add_hooks<LD: Clone, GD: Clone>(
ranges: Vec<CoverageRange>,
vma: &VirtMemAllocator,
hooks: &mut Hooks<LD, GD>,
) -> Result<()> {
for CoverageRange(range) in ranges.iter() {
for addr in range.clone().step_by(4) {
let mut code = [0; 4];
vma.read(addr, &mut code)?;
let (bt_in, bt_out) = CSE.with(|cs| {
let insns = cs
.disasm_count(&code, addr, 1)
.expect("could not disassemble while adding backtrace hooks");
if let Some(insn) = insns.as_ref().get(0) {
(
Self::BT_INSNS.contains(&insn.mnemonic().unwrap()),
insn.mnemonic().unwrap() == "ret",
)
} else {
(false, false)
}
});
if bt_in {
hooks.add_backtrace_hook(addr, Self::hook_in);
} else if bt_out {
hooks.add_backtrace_hook(addr, Self::hook_out);
}
}
}
Ok(())
}
pub fn hook_in<LD, GD>(args: &mut HookArgs<LD, GD>) -> Result<ExitKind> {
args.bdata.backtrace.push(args.addr);
Ok(ExitKind::Continue)
}
pub fn hook_out<LD, GD>(args: &mut HookArgs<LD, GD>) -> Result<ExitKind> {
args.bdata.backtrace.pop();
Ok(ExitKind::Continue)
}
pub fn get_crash_hash(bt: &Backtrace) -> u64 {
let mut hash = 0;
for pc in bt.backtrace.iter() {
hash ^= pc << 13;
hash ^= hash >> 7;
hash ^= hash >> 17;
}
hash
}
}
impl Default for Backtrace {
fn default() -> Self {
Self::new()
}
}