Skip to main content

ghostscope_dwarf/core/
diagnostic.rs

1//! Precise semantic availability and diagnostic categories.
2
3/// Whether a semantic result is usable at the requested PC.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum Availability {
6    Available,
7    PartiallyAvailable,
8    OptimizedOut,
9    NotInScope,
10    Unsupported(UnsupportedReason),
11    Requires(RuntimeRequirement),
12    Ambiguous(AmbiguityReason),
13}
14
15impl Availability {
16    pub fn is_available(&self) -> bool {
17        matches!(self, Self::Available | Self::PartiallyAvailable)
18    }
19}
20
21/// DWARF or semantic shapes the current engine cannot represent yet.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum UnsupportedReason {
24    DwarfOp { op: String },
25    ExpressionShape { detail: String },
26    TypeLayout { detail: String },
27    AddressClass { detail: String },
28    RegisterMapping { dwarf_reg: u16 },
29}
30
31/// Runtime feature required before a semantic plan can be lowered safely.
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub enum RuntimeRequirement {
34    CallerFrame,
35    SleepableUprobe,
36    UserMemoryRead,
37    DwarfCfiRecovery,
38}
39
40/// Reason a query could not pick one unambiguous semantic interpretation.
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub enum AmbiguityReason {
43    InlineContext { detail: String },
44    VariableDeclaration { detail: String },
45    TypeResolution { detail: String },
46}
47
48/// Where a semantic answer came from.
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum Provenance {
51    DirectDie,
52    AbstractOrigin,
53    Specification,
54    LocationList,
55    CallSite,
56    Cfi,
57    Synthesized { detail: String },
58}
59
60/// Where DWARF debug information for a loaded module came from.
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub enum DebugInfoSource {
63    Embedded { path: String },
64    Explicit { path: String },
65    Debuglink { path: String },
66    Debuginfod { path: String },
67    Missing,
68}
69
70impl DebugInfoSource {
71    pub fn kind_label(&self) -> &'static str {
72        match self {
73            Self::Embedded { .. } => "embedded",
74            Self::Explicit { .. } => "explicit",
75            Self::Debuglink { .. } => "debuglink",
76            Self::Debuginfod { .. } => "debuginfod",
77            Self::Missing => "missing",
78        }
79    }
80
81    pub fn display_path(&self) -> Option<&str> {
82        match self {
83            Self::Embedded { path }
84            | Self::Explicit { path }
85            | Self::Debuglink { path }
86            | Self::Debuginfod { path } => Some(path),
87            Self::Missing => None,
88        }
89    }
90
91    pub fn summary(&self) -> String {
92        match self.display_path() {
93            Some(path) => format!("{} ({path})", self.kind_label()),
94            None => self.kind_label().to_string(),
95        }
96    }
97}
98
99/// Capabilities available to a future BPF lowering pass.
100#[derive(Debug, Clone, PartialEq, Eq)]
101pub struct RuntimeCapabilities {
102    pub regular_uprobe: bool,
103    pub sleepable_uprobe: bool,
104    pub uprobe_multi: bool,
105    pub copy_from_user_task: bool,
106    pub max_bpf_stack_bytes: usize,
107    pub bounded_loops: bool,
108    pub arch: TargetArch,
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub enum TargetArch {
113    X86_64,
114    Aarch64,
115    Unknown,
116}
117
118impl TargetArch {
119    pub fn current() -> Self {
120        if cfg!(target_arch = "x86_64") {
121            Self::X86_64
122        } else if cfg!(target_arch = "aarch64") {
123            Self::Aarch64
124        } else {
125            Self::Unknown
126        }
127    }
128}
129
130impl Default for RuntimeCapabilities {
131    fn default() -> Self {
132        Self {
133            regular_uprobe: true,
134            sleepable_uprobe: false,
135            uprobe_multi: false,
136            copy_from_user_task: false,
137            max_bpf_stack_bytes: 512,
138            bounded_loops: true,
139            arch: TargetArch::current(),
140        }
141    }
142}
143
144/// User-memory helper strategy selected by a lowering plan.
145#[derive(Debug, Clone, PartialEq, Eq)]
146pub enum HelperMode {
147    NoUserMemoryRead,
148    ProbeReadUser,
149    CopyFromUserTask,
150}
151
152/// Coarse verifier risk surfaced before backend codegen.
153#[derive(Debug, Clone, PartialEq, Eq)]
154pub enum VerifierRisk {
155    Low,
156    RequiresBoundedLoops,
157    StackBudgetExceeded { estimated: usize, max: usize },
158    Unsupported { reason: String },
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164    use crate::core::{PieceLocation, VariableLocation};
165
166    #[test]
167    fn optimized_location_is_unavailable() {
168        assert_eq!(
169            Availability::from_variable_location(&VariableLocation::OptimizedOut),
170            Availability::OptimizedOut
171        );
172    }
173
174    #[test]
175    fn mixed_composite_location_is_partially_available() {
176        let location = VariableLocation::Pieces(vec![
177            PieceLocation {
178                location: Box::new(VariableLocation::RegisterValue { dwarf_reg: 0 }),
179                bit_size: 32,
180                bit_offset: 0,
181            },
182            PieceLocation {
183                location: Box::new(VariableLocation::OptimizedOut),
184                bit_size: 32,
185                bit_offset: 32,
186            },
187        ]);
188
189        assert_eq!(
190            Availability::from_variable_location(&location),
191            Availability::PartiallyAvailable
192        );
193    }
194}