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}