probe_rs_debug/
stack_frame.rs

1use super::*;
2use probe_rs::RegisterValue;
3
4#[cfg(test)]
5pub use test::TestFormatter;
6
7/// Helper struct to pass around multiple pieces of `StackFrame` related information.
8#[derive(Clone, Copy)]
9pub struct StackFrameInfo<'a> {
10    /// The current register state represented in this stackframe.
11    pub registers: &'a registers::DebugRegisters,
12
13    /// The DWARF debug info defines a `DW_AT_frame_base` attribute which can be used to calculate the memory location of variables in a stack frame.
14    /// The rustc compiler, has a compile flag, `-C force-frame-pointers`, which when set to `on`, will usually result in this being a pointer to the register value of the platform frame pointer.
15    /// However, some isa's (e.g. RISC-V) uses a default of `-C force-frame-pointers off` and will then use the stack pointer as the frame base address.
16    /// We store the frame_base of the relevant non-inlined parent function, to ensure correct calculation of the [`Variable::memory_location`] values.
17    pub frame_base: Option<u64>,
18
19    /// The value of the stack pointer just before the CALL instruction in the parent function.
20    pub canonical_frame_address: Option<u64>,
21}
22
23/// A full stack frame with all its information contained.
24#[derive(PartialEq, Serialize)]
25pub struct StackFrame {
26    /// The stackframe ID.
27    #[serde(skip_serializing)]
28    pub id: ObjectRef,
29    /// The name of the function this stackframe belongs to.
30    pub function_name: String,
31    /// The source location the function this stackframe belongs to originates.
32    pub source_location: Option<SourceLocation>,
33    /// The current register state represented in this stackframe.
34    pub registers: registers::DebugRegisters,
35    /// The program counter / address of the current instruction when this stack frame was created
36    pub pc: RegisterValue,
37    /// The DWARF debug info defines a `DW_AT_frame_base` attribute which can be used to calculate the memory location of variables in a stack frame.
38    /// The rustc compiler, has a compile flag, `-C force-frame-pointers`, which when set to `on`, will usually result in this being a pointer to the register value of the platform frame pointer.
39    /// However, some isa's (e.g. RISC-V) uses a default of `-C force-frame-pointers off` and will then use the stack pointer as the frame base address.
40    /// We store the frame_base of the relevant non-inlined parent function, to ensure correct calculation of the [`Variable::memory_location`] values.
41    pub frame_base: Option<u64>,
42    /// Indicate if this stack frame belongs to an inlined function.
43    pub is_inlined: bool,
44    /// A cache of 'local' scoped variables for this stackframe, with a `Variable` for each in-scope variable.
45    /// - Complex variables and pointers will have additional children.
46    ///   - This structure is recursive until a base type is encountered.
47    pub local_variables: Option<VariableCache>,
48    /// The value of the stack pointer just before the CALL instruction in the parent function.
49    pub canonical_frame_address: Option<u64>,
50}
51
52impl std::fmt::Display for StackFrame {
53    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
54        // Header info for the StackFrame
55        writeln!(f, "Frame: {}", self.function_name)?;
56        if let Some(si) = &self.source_location {
57            write!(f, "\t{}", si.path.to_path().display())?;
58
59            if let (Some(column), Some(line)) = (si.column, si.line) {
60                match column {
61                    ColumnType::Column(c) => write!(f, ":{line}:{c}")?,
62                    ColumnType::LeftEdge => write!(f, ":{line}")?,
63                }
64            }
65        }
66        writeln!(f)
67    }
68}
69
70#[cfg(test)]
71mod test {
72    use super::StackFrame;
73
74    /// Helper struct used to format a StackFrame for testing.
75    pub struct TestFormatter<'s>(pub &'s StackFrame);
76
77    impl std::fmt::Display for TestFormatter<'_> {
78        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
79            writeln!(f, "Frame:")?;
80            writeln!(f, " function:        {}", self.0.function_name)?;
81
82            writeln!(f, " source_location:")?;
83            match &self.0.source_location {
84                Some(location) => {
85                    writeln!(f, "  path: {}", location.path.to_path().display())?;
86                    writeln!(f, "  line: {:?}", location.line)?;
87                    writeln!(f, "  column: {:?}", location.column)?;
88                }
89                None => writeln!(f, "None")?,
90            }
91            writeln!(f, " frame_base:      {:08x?}", self.0.frame_base)?;
92
93            Ok(())
94        }
95    }
96}