ghostscope-dwarf 0.1.5

DWARF parser and symbolizer used by GhostScope to resolve variables and types at runtime.
Documentation
//! Precise semantic availability and diagnostic categories.

/// Whether a semantic result is usable at the requested PC.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Availability {
    Available,
    PartiallyAvailable,
    OptimizedOut,
    NotInScope,
    Unsupported(UnsupportedReason),
    Requires(RuntimeRequirement),
    Ambiguous(AmbiguityReason),
}

impl Availability {
    pub fn is_available(&self) -> bool {
        matches!(self, Self::Available | Self::PartiallyAvailable)
    }
}

/// DWARF or semantic shapes the current engine cannot represent yet.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UnsupportedReason {
    DwarfOp { op: String },
    ExpressionShape { detail: String },
    TypeLayout { detail: String },
    AddressClass { detail: String },
    RegisterMapping { dwarf_reg: u16 },
}

/// Runtime feature required before a semantic plan can be lowered safely.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RuntimeRequirement {
    CallerFrame,
    SleepableUprobe,
    UserMemoryRead,
    DwarfCfiRecovery,
}

/// Reason a query could not pick one unambiguous semantic interpretation.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AmbiguityReason {
    InlineContext { detail: String },
    VariableDeclaration { detail: String },
    TypeResolution { detail: String },
}

/// Where a semantic answer came from.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Provenance {
    DirectDie,
    AbstractOrigin,
    Specification,
    LocationList,
    CallSite,
    Cfi,
    Synthesized { detail: String },
}

/// Where DWARF debug information for a loaded module came from.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DebugInfoSource {
    Embedded { path: String },
    Explicit { path: String },
    Debuglink { path: String },
    Debuginfod { path: String },
    Missing,
}

impl DebugInfoSource {
    pub fn kind_label(&self) -> &'static str {
        match self {
            Self::Embedded { .. } => "embedded",
            Self::Explicit { .. } => "explicit",
            Self::Debuglink { .. } => "debuglink",
            Self::Debuginfod { .. } => "debuginfod",
            Self::Missing => "missing",
        }
    }

    pub fn display_path(&self) -> Option<&str> {
        match self {
            Self::Embedded { path }
            | Self::Explicit { path }
            | Self::Debuglink { path }
            | Self::Debuginfod { path } => Some(path),
            Self::Missing => None,
        }
    }

    pub fn summary(&self) -> String {
        match self.display_path() {
            Some(path) => format!("{} ({path})", self.kind_label()),
            None => self.kind_label().to_string(),
        }
    }
}

/// Capabilities available to a future BPF lowering pass.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RuntimeCapabilities {
    pub regular_uprobe: bool,
    pub sleepable_uprobe: bool,
    pub uprobe_multi: bool,
    pub copy_from_user_task: bool,
    pub max_bpf_stack_bytes: usize,
    pub bounded_loops: bool,
    pub arch: TargetArch,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetArch {
    X86_64,
    Aarch64,
    Unknown,
}

impl TargetArch {
    pub fn current() -> Self {
        if cfg!(target_arch = "x86_64") {
            Self::X86_64
        } else if cfg!(target_arch = "aarch64") {
            Self::Aarch64
        } else {
            Self::Unknown
        }
    }
}

impl Default for RuntimeCapabilities {
    fn default() -> Self {
        Self {
            regular_uprobe: true,
            sleepable_uprobe: false,
            uprobe_multi: false,
            copy_from_user_task: false,
            max_bpf_stack_bytes: 512,
            bounded_loops: true,
            arch: TargetArch::current(),
        }
    }
}

/// User-memory helper strategy selected by a lowering plan.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HelperMode {
    NoUserMemoryRead,
    ProbeReadUser,
    CopyFromUserTask,
}

/// Coarse verifier risk surfaced before backend codegen.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VerifierRisk {
    Low,
    RequiresBoundedLoops,
    StackBudgetExceeded { estimated: usize, max: usize },
    Unsupported { reason: String },
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core::{PieceLocation, VariableLocation};

    #[test]
    fn optimized_location_is_unavailable() {
        assert_eq!(
            Availability::from_variable_location(&VariableLocation::OptimizedOut),
            Availability::OptimizedOut
        );
    }

    #[test]
    fn mixed_composite_location_is_partially_available() {
        let location = VariableLocation::Pieces(vec![
            PieceLocation {
                location: Box::new(VariableLocation::RegisterValue { dwarf_reg: 0 }),
                bit_size: 32,
                bit_offset: 0,
            },
            PieceLocation {
                location: Box::new(VariableLocation::OptimizedOut),
                bit_size: 32,
                bit_offset: 32,
            },
        ]);

        assert_eq!(
            Availability::from_variable_location(&location),
            Availability::PartiallyAvailable
        );
    }
}