Skip to main content

ghostscope_dwarf/semantics/
unwind_plan.rs

1use crate::core::{ModuleId, PlanExprOp};
2
3/// A compact, row-oriented unwind table derived from DWARF CFI.
4#[derive(Debug, Clone, PartialEq)]
5pub struct CompactUnwindTable {
6    pub module: ModuleId,
7    pub rows: Vec<CompactUnwindRow>,
8    pub diagnostics: Vec<UnwindDiagnostic>,
9}
10
11impl CompactUnwindTable {
12    pub fn row_for_pc(&self, pc: u64) -> Option<&CompactUnwindRow> {
13        self.rows
14            .iter()
15            .find(|row| row.pc_start <= pc && pc < row.pc_end)
16    }
17
18    pub fn stats(&self) -> CompactUnwindStats {
19        let bpf_supported_rows = self.rows.iter().filter(|row| row.bpf_supported).count();
20        CompactUnwindStats {
21            row_count: self.rows.len(),
22            bpf_supported_rows,
23            unsupported_rows: self.rows.len().saturating_sub(bpf_supported_rows),
24            diagnostic_count: self.diagnostics.len(),
25        }
26    }
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct CompactUnwindStats {
31    pub row_count: usize,
32    pub bpf_supported_rows: usize,
33    pub unsupported_rows: usize,
34    pub diagnostic_count: usize,
35}
36
37#[derive(Debug, Clone, PartialEq)]
38pub struct CompactUnwindRow {
39    pub module: ModuleId,
40    pub pc_start: u64,
41    pub pc_end: u64,
42    pub cfa: CfaRulePlan,
43    pub return_address_register: u16,
44    pub return_address: RegisterRecoveryPlan,
45    pub sp: Option<RegisterRecoveryPlan>,
46    pub rbp: Option<RegisterRecoveryPlan>,
47    pub bpf_supported: bool,
48}
49
50#[derive(Debug, Clone, PartialEq)]
51pub enum CfaRulePlan {
52    RegPlusOffset { register: u16, offset: i64 },
53    Expression { steps: Vec<PlanExprOp> },
54    Unsupported { reason: String },
55}
56
57impl CfaRulePlan {
58    pub fn is_bpf_fast_path_supported(&self) -> bool {
59        matches!(
60            self,
61            Self::RegPlusOffset {
62                register: 6 | 7,
63                ..
64            }
65        )
66    }
67}
68
69#[derive(Debug, Clone, PartialEq)]
70pub enum RegisterRecoveryPlan {
71    Undefined,
72    SameValue {
73        register: u16,
74    },
75    Register {
76        register: u16,
77    },
78    AtCfaOffset {
79        offset: i64,
80    },
81    ValCfaOffset {
82        offset: i64,
83    },
84    Constant {
85        value: u64,
86    },
87    Expression {
88        steps: Vec<PlanExprOp>,
89        dereference: bool,
90    },
91    Unsupported {
92        reason: String,
93    },
94}
95
96impl RegisterRecoveryPlan {
97    pub fn is_bpf_fast_path_supported(&self) -> bool {
98        matches!(
99            self,
100            Self::SameValue { .. }
101                | Self::Register { .. }
102                | Self::AtCfaOffset { .. }
103                | Self::ValCfaOffset { .. }
104        )
105    }
106}
107
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct UnwindDiagnostic {
110    pub pc_start: u64,
111    pub pc_end: u64,
112    pub kind: UnwindDiagnosticKind,
113}
114
115#[derive(Debug, Clone, PartialEq, Eq)]
116pub enum UnwindDiagnosticKind {
117    UnsupportedCfaRule { reason: String },
118    UnsupportedRegisterRule { register: u16, reason: String },
119    MissingReturnAddressRule { register: u16 },
120}