probe_rs_debug/
exception_handling.rs

1//! This module (and its children) contains the implementation of the [`ExceptionInterface`] for the various ARM core
2//! variants.
3
4use std::ops::ControlFlow;
5
6use probe_rs_target::CoreType;
7
8use crate::unwind_pc_without_debuginfo;
9use probe_rs::{InstructionSet, MemoryInterface};
10
11use super::{DebugError, DebugInfo, DebugRegisters, StackFrame};
12
13pub(crate) mod armv6m;
14/// Where applicable, this defines shared logic for implementing exception handling across the various ARMv6-m and
15/// ARMv7-m [`crate::CoreType`]'s.
16pub(crate) mod armv6m_armv7m_shared;
17// NOTE: There is also a [`CoreType::Armv7em`] variant, but it is not currently used/implemented in probe-rs.
18pub(crate) mod armv7m;
19pub(crate) mod armv8m;
20pub(crate) mod riscv;
21pub(crate) mod xtensa;
22
23/// Creates a new exception interface for the [`CoreType`] at hand.
24pub fn exception_handler_for_core(core_type: CoreType) -> Box<dyn ExceptionInterface> {
25    use self::{armv6m, armv7m, armv8m};
26    match core_type {
27        CoreType::Armv6m => Box::new(armv6m::ArmV6MExceptionHandler),
28        CoreType::Armv7m | CoreType::Armv7em => Box::new(armv7m::ArmV7MExceptionHandler),
29        CoreType::Armv8m => Box::new(armv8m::ArmV8MExceptionHandler),
30        CoreType::Xtensa => Box::new(xtensa::XtensaExceptionHandler),
31        CoreType::Riscv => Box::new(riscv::RiscvExceptionHandler),
32        CoreType::Armv7a | CoreType::Armv8a => Box::new(UnimplementedExceptionHandler),
33    }
34}
35
36/// Placeholder for exception handling for cores where handling exceptions is not yet supported.
37pub struct UnimplementedExceptionHandler;
38
39impl ExceptionInterface for UnimplementedExceptionHandler {}
40
41/// A struct containing key information about an exception.
42/// The exception details are architecture specific, and the abstraction is handled in the
43/// architecture specific implementations of [`ExceptionInterface`].
44#[derive(PartialEq)]
45pub struct ExceptionInfo {
46    /// The exception number.
47    /// This is architecture specific and can be used to decode the architecture specific exception reason.
48    pub raw_exception: u32,
49    /// A human readable explanation for the exception.
50    pub description: String,
51    /// A populated [`StackFrame`] to represent the stack data in the exception handler.
52    pub handler_frame: StackFrame,
53}
54
55/// A generic interface to identify and decode exceptions during unwind processing.
56pub trait ExceptionInterface {
57    /// Using the `stackframe_registers` for a "called frame",
58    /// determine if the given frame was called from an exception handler,
59    /// and resolve the relevant details about the exception, including the reason for the exception,
60    /// and the stackframe registers for the frame that triggered the exception.
61    /// A return value of `Ok(None)` indicates that the given frame was called from within the current thread,
62    /// and the unwind should continue normally.
63    fn exception_details(
64        &self,
65        _memory: &mut dyn MemoryInterface,
66        _stackframe_registers: &DebugRegisters,
67        _debug_info: &DebugInfo,
68    ) -> Result<Option<ExceptionInfo>, DebugError> {
69        // For architectures where the exception handling has not been implemented in probe-rs,
70        // this will result in maintaining the current `unwind` behavior, i.e. unwinding will include up
71        // to the first frame that was called from an exception handler.
72        Ok(None)
73    }
74
75    /// Using the `stackframe_registers` for a "called frame", retrieve updated register values for the "calling frame".
76    fn calling_frame_registers(
77        &self,
78        _memory: &mut dyn MemoryInterface,
79        _stackframe_registers: &crate::DebugRegisters,
80        _raw_exception: u32,
81    ) -> Result<crate::DebugRegisters, DebugError> {
82        Err(DebugError::NotImplemented("calling frame registers"))
83    }
84
85    /// Retrieve the architecture specific exception number.
86    fn raw_exception(
87        &self,
88        _stackframe_registers: &crate::DebugRegisters,
89    ) -> Result<u32, DebugError> {
90        Err(DebugError::NotImplemented("raw exception"))
91    }
92
93    /// Convert the architecture specific exception number into a human readable description.
94    /// Where possible, the implementation may read additional registers from the core, to provide additional context.
95    fn exception_description(
96        &self,
97        _raw_exception: u32,
98        _memory: &mut dyn MemoryInterface,
99    ) -> Result<String, DebugError> {
100        Err(DebugError::NotImplemented("exception description"))
101    }
102
103    /// Unwind the stack without debug info.
104    ///
105    /// This method can be implemented to provide a stack trace using frame pointers, for example.
106    fn unwind_without_debuginfo(
107        &self,
108        unwind_registers: &mut DebugRegisters,
109        frame_pc: u64,
110        _stack_frames: &[StackFrame],
111        instruction_set: Option<InstructionSet>,
112        _memory: &mut dyn MemoryInterface,
113    ) -> ControlFlow<Option<DebugError>> {
114        unwind_pc_without_debuginfo(unwind_registers, frame_pc, instruction_set)
115    }
116}