stackdump_trace/
lib.rs

1#![doc = include_str!("../README.md")]
2// #![warn(missing_docs)]
3
4use render_colors::{Theme, ThemeColors};
5pub use stackdump_core;
6
7use crate::type_value_tree::variable_type::Archetype;
8use gimli::{EndianReader, EvaluationResult, Piece, RunTimeEndian};
9use std::{
10    fmt::{Debug, Display},
11    rc::Rc,
12};
13use type_value_tree::{rendering::render_type_value_tree, TypeValueTree};
14
15pub mod error;
16mod gimli_extensions;
17pub mod platform;
18pub mod render_colors;
19pub mod type_value_tree;
20mod variables;
21
22type DefaultReader = EndianReader<RunTimeEndian, Rc<[u8]>>;
23
24/// A source code location
25#[derive(Debug, Clone, Default)]
26pub struct Location {
27    /// The file path of the piece of code
28    pub file: Option<String>,
29    /// The line of the piece of code
30    pub line: Option<u64>,
31    /// The column of the piece of code
32    pub column: Option<u64>,
33}
34
35impl Display for Location {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        if let Some(file) = self.file.clone() {
38            write!(f, "{}", file)?;
39            if let Some(line) = self.line {
40                write!(f, ":{}", line)?;
41                if let Some(column) = self.column {
42                    write!(f, ":{}", column)?;
43                }
44            }
45        }
46
47        Ok(())
48    }
49}
50
51/// An object containing a de-inlined stack frame.
52/// Exceptions/interrupts are also a frame.
53#[derive(Debug, Clone)]
54pub struct Frame<ADDR: funty::Integral> {
55    /// The name of the function the frame is in
56    pub function: String,
57    /// The code location of the frame
58    pub location: Location,
59    /// The type of the frame
60    pub frame_type: FrameType,
61    /// The variables and their values that are present in the frame
62    pub variables: Vec<Variable<ADDR>>,
63}
64
65impl<ADDR: funty::Integral> Frame<ADDR> {
66    /// Get a string that can be displayed to a user
67    ///
68    /// - `show_parameters`: When true, any variable that is a parameter will be shown
69    /// - `show_inlined_vars`: When true, any variable that is inlined will be shown
70    /// - `show_zero_sized_vars`: When true, any variable that is zero-sized will be shown
71    pub fn display(
72        &self,
73        show_parameters: bool,
74        show_inlined_vars: bool,
75        show_zero_sized_vars: bool,
76        theme: Theme,
77    ) -> String {
78        use std::fmt::Write;
79
80        let mut display = String::new();
81
82        writeln!(
83            display,
84            "{} ({})",
85            theme.color_function(&self.function),
86            theme.color_info(&self.frame_type)
87        )
88        .unwrap();
89
90        let location_text = self.location.to_string();
91        if !location_text.is_empty() {
92            writeln!(display, "  at {}", theme.color_url(location_text)).unwrap();
93        }
94
95        let filtered_variables = self.variables.iter().filter(|v| {
96            (show_inlined_vars || !v.kind.inlined)
97                && (show_zero_sized_vars || !v.kind.zero_sized)
98                && (show_parameters || !v.kind.parameter)
99                // Hide the vtables
100                && v.type_value.data().variable_type.archetype != Archetype::ObjectMemberPointer
101        });
102        if filtered_variables.clone().count() > 0 {
103            writeln!(display, "  variables:").unwrap();
104            for variable in filtered_variables {
105                writeln!(display, "    {}", variable.display(theme)).unwrap();
106            }
107        }
108
109        display
110    }
111}
112
113/// The type of a frame
114#[derive(Debug, Clone)]
115pub enum FrameType {
116    /// A real function
117    Function,
118    /// An inline function (does not really exist in the binary)
119    InlineFunction,
120    /// An interrupt or exception
121    Exception,
122    /// The frame could not be (fully) read, so the frame is corrupted. The string says what the problem is.
123    Corrupted(String),
124    /// This is not really a frame, but has all the statically available data
125    Static,
126}
127
128impl Display for FrameType {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        match self {
131            FrameType::Function => write!(f, "Function"),
132            FrameType::InlineFunction => write!(f, "Inline Function"),
133            FrameType::Exception => write!(f, "Exception"),
134            FrameType::Corrupted(reason) => write!(f, "Corrupted: \"{reason}\""),
135            FrameType::Static => write!(f, "Static"),
136        }
137    }
138}
139
140/// A variable that was found in the tracing procedure
141#[derive(Debug, Clone)]
142pub struct Variable<ADDR: funty::Integral> {
143    /// The name of the variable
144    pub name: String,
145    /// The kind of variable (normal, parameter, etc)
146    pub kind: VariableKind,
147    pub type_value: TypeValueTree<ADDR>,
148    /// The code location of where this variable is declared
149    pub location: Location,
150}
151
152impl<ADDR: funty::Integral> Variable<ADDR> {
153    pub fn display(&self, theme: Theme) -> String {
154        let mut kind_text = self.kind.to_string();
155        if !kind_text.is_empty() {
156            kind_text = theme.color_info(format!("({}) ", kind_text)).to_string();
157        }
158
159        let mut location_text = self.location.to_string();
160        if !location_text.is_empty() {
161            location_text = format!("at {}", theme.color_url(location_text));
162        }
163
164        format!(
165            "{}{}: {} = {} ({})",
166            kind_text,
167            theme.color_variable_name(&self.name),
168            theme.color_type_name(&self.type_value.root().data().variable_type.name),
169            render_type_value_tree(&self.type_value, theme),
170            location_text,
171        )
172    }
173}
174
175#[derive(Debug, Clone)]
176pub enum VariableLocationResult {
177    /// The DW_AT_location attribute is missing
178    NoLocationAttribute,
179    /// The location list could not be found in the ELF
180    LocationListNotFound,
181    /// This variable is not present in memory at this point
182    NoLocationFound,
183    /// A required step of the location evaluation logic has not been implemented yet
184    LocationEvaluationStepNotImplemented(Rc<EvaluationResult<DefaultReader>>),
185    /// The variable is split up into multiple pieces of memory
186    LocationsFound(Vec<Piece<DefaultReader, usize>>),
187}
188
189/// Type representing what kind of variable something is
190#[derive(Debug, Clone, Copy, Default)]
191pub struct VariableKind {
192    /// The variable is a zero-sized type
193    pub zero_sized: bool,
194    /// The variable is actually part of another function (either our caller or our callee), but is present in our function already
195    pub inlined: bool,
196    /// The variable is a parameter of a function
197    pub parameter: bool,
198}
199
200impl Display for VariableKind {
201    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202        let mut elements = vec![];
203
204        if self.zero_sized {
205            elements.push("zero-sized");
206        }
207        if self.inlined {
208            elements.push("inlined");
209        }
210        if self.parameter {
211            elements.push("parameter");
212        }
213
214        write!(f, "{}", elements.join(" "))
215    }
216}