#![doc = include_str!("../README.md")]
use render_colors::{Theme, ThemeColors};
pub use stackdump_core;
use crate::type_value_tree::variable_type::Archetype;
use gimli::{EndianReader, EvaluationResult, Piece, RunTimeEndian};
use std::{
fmt::{Debug, Display},
rc::Rc,
};
use type_value_tree::{rendering::render_type_value_tree, TypeValueTree};
pub mod error;
mod gimli_extensions;
pub mod platform;
pub mod render_colors;
pub mod type_value_tree;
mod variables;
type DefaultReader = EndianReader<RunTimeEndian, Rc<[u8]>>;
#[derive(Debug, Clone, Default)]
pub struct Location {
pub file: Option<String>,
pub line: Option<u64>,
pub column: Option<u64>,
}
impl Display for Location {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(file) = self.file.clone() {
write!(f, "{}", file)?;
if let Some(line) = self.line {
write!(f, ":{}", line)?;
if let Some(column) = self.column {
write!(f, ":{}", column)?;
}
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct Frame<ADDR: funty::Integral> {
pub function: String,
pub location: Location,
pub frame_type: FrameType,
pub variables: Vec<Variable<ADDR>>,
}
impl<ADDR: funty::Integral> Frame<ADDR> {
pub fn display(
&self,
show_parameters: bool,
show_inlined_vars: bool,
show_zero_sized_vars: bool,
theme: Theme,
) -> String {
use std::fmt::Write;
let mut display = String::new();
writeln!(
display,
"{} ({})",
theme.color_function(&self.function),
theme.color_info(&self.frame_type)
)
.unwrap();
let location_text = self.location.to_string();
if !location_text.is_empty() {
writeln!(display, " at {}", theme.color_url(location_text)).unwrap();
}
let filtered_variables = self.variables.iter().filter(|v| {
(show_inlined_vars || !v.kind.inlined)
&& (show_zero_sized_vars || !v.kind.zero_sized)
&& (show_parameters || !v.kind.parameter)
&& v.type_value.data().variable_type.archetype != Archetype::ObjectMemberPointer
});
if filtered_variables.clone().count() > 0 {
writeln!(display, " variables:").unwrap();
for variable in filtered_variables {
writeln!(display, " {}", variable.display(theme)).unwrap();
}
}
display
}
}
#[derive(Debug, Clone)]
pub enum FrameType {
Function,
InlineFunction,
Exception,
Corrupted(String),
Static,
}
impl Display for FrameType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FrameType::Function => write!(f, "Function"),
FrameType::InlineFunction => write!(f, "Inline Function"),
FrameType::Exception => write!(f, "Exception"),
FrameType::Corrupted(reason) => write!(f, "Corrupted: \"{reason}\""),
FrameType::Static => write!(f, "Static"),
}
}
}
#[derive(Debug, Clone)]
pub struct Variable<ADDR: funty::Integral> {
pub name: String,
pub kind: VariableKind,
pub type_value: TypeValueTree<ADDR>,
pub location: Location,
}
impl<ADDR: funty::Integral> Variable<ADDR> {
pub fn display(&self, theme: Theme) -> String {
let mut kind_text = self.kind.to_string();
if !kind_text.is_empty() {
kind_text = theme.color_info(format!("({}) ", kind_text)).to_string();
}
let mut location_text = self.location.to_string();
if !location_text.is_empty() {
location_text = format!("at {}", theme.color_url(location_text));
}
format!(
"{}{}: {} = {} ({})",
kind_text,
theme.color_variable_name(&self.name),
theme.color_type_name(&self.type_value.root().data().variable_type.name),
render_type_value_tree(&self.type_value, theme),
location_text,
)
}
}
#[derive(Debug, Clone)]
pub enum VariableLocationResult {
NoLocationAttribute,
LocationListNotFound,
NoLocationFound,
LocationEvaluationStepNotImplemented(Rc<EvaluationResult<DefaultReader>>),
LocationsFound(Vec<Piece<DefaultReader, usize>>),
}
#[derive(Debug, Clone, Copy, Default)]
pub struct VariableKind {
pub zero_sized: bool,
pub inlined: bool,
pub parameter: bool,
}
impl Display for VariableKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut elements = vec![];
if self.zero_sized {
elements.push("zero-sized");
}
if self.inlined {
elements.push("inlined");
}
if self.parameter {
elements.push("parameter");
}
write!(f, "{}", elements.join(" "))
}
}