use probe_rs::CoreType;
use probe_rs::InstructionSet;
use probe_rs::MemoryInterface;
use probe_rs_debug::DebugRegisters;
use super::FunctionAddress;
fn dwarf_unwind_memory_interface(
memory: &mut impl MemoryInterface,
debug_info: &probe_rs_debug::DebugInfo,
initial_registers: DebugRegisters,
core_type: CoreType,
instruction_set: Option<InstructionSet>,
) -> Vec<FunctionAddress> {
let exception_handler = probe_rs_debug::exception_handler_for_core(core_type);
let stack_frames = debug_info
.unwind(
memory,
initial_registers,
exception_handler.as_ref(),
instruction_set,
usize::MAX,
)
.unwrap_or_else(|_| {
tracing::warn!("Unable to unwind, recording empty sample");
Vec::new()
});
let function_addresses: Vec<FunctionAddress> = stack_frames
.into_iter()
.enumerate()
.filter(|(_, frame)| !frame.is_inlined)
.map(|(idx, frame)| {
let registers = &frame.registers;
let pc_reg = registers
.get_program_counter()
.expect("A register with PC role exists")
.value
.unwrap_or(frame.pc);
let addr: u64 = pc_reg.try_into().expect("Can convert PC reg to u64");
match idx {
0 => FunctionAddress::ProgramCounter(addr),
_ => FunctionAddress::AdjustedReturnAddress(addr),
}
})
.rev()
.collect();
function_addresses
}
pub(crate) fn dwarf_unwind<'a>(
core: &mut probe_rs::Core<'a>,
debug_info: &probe_rs_debug::DebugInfo,
) -> Vec<FunctionAddress> {
let debug_registers = DebugRegisters::from_core(core);
let core_type = core.core_type();
let instruction_set = core.instruction_set().ok();
dwarf_unwind_memory_interface(
core,
debug_info,
debug_registers,
core_type,
instruction_set,
)
}
#[cfg(test)]
mod test {
use probe_rs_debug::{DebugError, DebugInfo, DebugRegisters};
use probe_rs::CoreDump;
use super::super::test::{addresses_to_callstack, coredump_path, get_path_for_test_files};
use super::*;
fn load_test_elf_as_debug_info(elf_file: &str) -> DebugInfo {
let path = get_path_for_test_files(elf_file);
DebugInfo::from_file(&path).unwrap_or_else(|err: DebugError| {
panic!("Failed to open file {}: {:?}", path.display(), err)
})
}
fn dwarf_unwind_core_dump(
core_dump: &mut CoreDump,
debug_info: &probe_rs_debug::DebugInfo,
) -> Vec<FunctionAddress> {
let initial_registers = DebugRegisters::from_coredump(core_dump);
let core_type = core_dump.core_type();
let instruction_set = core_dump.instruction_set();
dwarf_unwind_memory_interface(
core_dump,
debug_info,
initial_registers,
core_type,
Some(instruction_set),
)
}
fn check_dwarf_unwind(test_name: &str, expect: &Vec<FunctionAddress>) {
let coredump_path = coredump_path(test_name);
let mut core_dump = CoreDump::load(&coredump_path).unwrap();
let debug_info = load_test_elf_as_debug_info(&format!("{test_name}.elf"));
let res = dwarf_unwind_core_dump(&mut core_dump, &debug_info);
assert_eq!(&res, expect);
}
#[test]
fn test_dwarf_unwind_riscv32() {
let test_name = "esp32c6_coredump_elf";
let expect = addresses_to_callstack(&[
0x4200124e, 0x420054f2, 0x420054f2, 0x420054f2, 0x42000202, 0x42000202, 0x420052ec, 0x42000244, 0x42000510, 0x4200438c, 0x42000132, ]);
check_dwarf_unwind(test_name, &expect);
}
#[test]
fn test_dwarf_unwind_armv7em() {
let test_name = "atsamd51p19a";
let expect = addresses_to_callstack(&[
0x1474, 0x14da, 0x1538, 0x978, 0x0,
]);
check_dwarf_unwind(test_name, &expect);
}
#[test]
fn test_dwarf_unwind_xtensa() {
let test_name = "esp32s3_coredump_elf";
let expect = addresses_to_callstack(&[
0x420045e3, 0x4200587a, 0x42000f69, 0x42000d10, 0x42004c4e, 0x42000bb9, 0x42000f7f, 0x42004483, ]);
check_dwarf_unwind(test_name, &expect);
}
}