Skip to main content

ghostscope_dwarf/core/
plan.rs

1//! Neutral semantic plans produced before runtime-specific lowering.
2
3use crate::core::{plan_expr_steps_to_expression, Availability, MemoryAccessSize, PlanExprOp};
4use std::fmt;
5
6/// Address expression that can be evaluated by a later lowering layer.
7#[derive(Debug, Clone, PartialEq)]
8pub struct AddressExpr {
9    pub steps: Vec<PlanExprOp>,
10}
11
12impl AddressExpr {
13    pub fn constant(address: u64) -> Self {
14        Self {
15            steps: vec![PlanExprOp::PushConstant(address as i64)],
16        }
17    }
18
19    pub fn register_relative(dwarf_reg: u16, offset: i64) -> Self {
20        let mut steps = vec![PlanExprOp::LoadRegister(dwarf_reg)];
21        if offset != 0 {
22            steps.push(PlanExprOp::PushConstant(offset));
23            steps.push(PlanExprOp::Add);
24        }
25        Self { steps }
26    }
27}
28
29/// PC-sensitive variable location before BPF lowering.
30#[derive(Debug, Clone, PartialEq)]
31pub enum VariableLocation {
32    Address(AddressExpr),
33    AbsoluteAddressValue(AddressExpr),
34    RegisterValue { dwarf_reg: u16 },
35    RegisterAddress { dwarf_reg: u16, offset: i64 },
36    FrameBaseRelative { offset: i64 },
37    ComputedValue(Vec<PlanExprOp>),
38    ComputedAddress(Vec<PlanExprOp>),
39    ImplicitValue(Vec<u8>),
40    Pieces(Vec<PieceLocation>),
41    OptimizedOut,
42    Unknown,
43}
44
45#[derive(Debug, Clone, PartialEq)]
46pub struct PieceLocation {
47    pub bit_offset: u32,
48    pub bit_size: u32,
49    pub location: Box<VariableLocation>,
50}
51
52/// Semantic location produced after DWARF expression evaluation.
53#[derive(Debug, Clone, PartialEq)]
54pub(crate) struct ParsedLocation {
55    pub location: VariableLocation,
56    pub availability: Availability,
57}
58
59impl ParsedLocation {
60    pub(crate) fn new(location: VariableLocation) -> Self {
61        let availability = location.availability();
62        Self {
63            location,
64            availability,
65        }
66    }
67}
68
69impl VariableLocation {
70    pub(crate) fn availability(&self) -> Availability {
71        Availability::from_variable_location(self)
72    }
73}
74
75impl Availability {
76    pub(crate) fn from_variable_location(location: &VariableLocation) -> Self {
77        match location {
78            VariableLocation::OptimizedOut => Self::OptimizedOut,
79            VariableLocation::Pieces(pieces) => {
80                if pieces.is_empty() {
81                    Self::Available
82                } else if pieces
83                    .iter()
84                    .all(|piece| matches!(piece.location.as_ref(), VariableLocation::OptimizedOut))
85                {
86                    Self::OptimizedOut
87                } else if pieces
88                    .iter()
89                    .any(|piece| matches!(piece.location.as_ref(), VariableLocation::OptimizedOut))
90                {
91                    Self::PartiallyAvailable
92                } else {
93                    Self::Available
94                }
95            }
96            _ => Self::Available,
97        }
98    }
99}
100
101impl fmt::Display for VariableLocation {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        match self {
104            VariableLocation::Address(expr) => {
105                write!(f, "[Memory] {}", location_display_for_address_expr(expr))
106            }
107            VariableLocation::AbsoluteAddressValue(expr) => {
108                write!(f, "[DirectValue] {}", address_value_display(expr))
109            }
110            VariableLocation::RegisterValue { dwarf_reg } => {
111                write!(f, "[DirectValue] {}", register_display(*dwarf_reg))
112            }
113            VariableLocation::RegisterAddress { dwarf_reg, offset } => {
114                write!(
115                    f,
116                    "[Memory] {}",
117                    register_address_display(*dwarf_reg, *offset)
118                )
119            }
120            VariableLocation::FrameBaseRelative { offset } => {
121                if *offset >= 0 {
122                    write!(f, "[Memory] @[frame_base+{offset}]")
123                } else {
124                    write!(f, "[Memory] @[frame_base{offset}]")
125                }
126            }
127            VariableLocation::ComputedValue(steps) => {
128                write!(f, "[DirectValue] ={}", plan_expr_steps_to_expression(steps))
129            }
130            VariableLocation::ComputedAddress(steps) => {
131                write!(f, "[Memory] @[{}]", plan_expr_steps_to_expression(steps))
132            }
133            VariableLocation::ImplicitValue(bytes) => {
134                write!(f, "[DirectValue] {}", implicit_value_display(bytes))
135            }
136            VariableLocation::Pieces(pieces) => write!(f, "Composite[{} pieces]", pieces.len()),
137            VariableLocation::OptimizedOut => write!(f, "<optimized out>"),
138            VariableLocation::Unknown => write!(f, "<unknown>"),
139        }
140    }
141}
142
143fn location_display_for_address_expr(expr: &AddressExpr) -> String {
144    if let [PlanExprOp::PushConstant(address)] = expr.steps.as_slice() {
145        return format!("@0x{:x}", *address as u64);
146    }
147
148    format!("@[{}]", plan_expr_steps_to_expression(&expr.steps))
149}
150
151fn address_value_display(expr: &AddressExpr) -> String {
152    if let [PlanExprOp::PushConstant(address)] = expr.steps.as_slice() {
153        return format!("&@0x{:x}", *address as u64);
154    }
155
156    format!("={}", plan_expr_steps_to_expression(&expr.steps))
157}
158
159fn register_display(dwarf_reg: u16) -> String {
160    ghostscope_platform::register_mapping::dwarf_reg_to_name(dwarf_reg)
161        .map(str::to_string)
162        .unwrap_or_else(|| format!("r{dwarf_reg}"))
163}
164
165fn register_address_display(dwarf_reg: u16, offset: i64) -> String {
166    let reg_name = register_display(dwarf_reg);
167    if offset >= 0 {
168        format!("@[{reg_name}+{offset}]")
169    } else {
170        format!("@[{reg_name}{offset}]")
171    }
172}
173
174fn implicit_value_display(bytes: &[u8]) -> String {
175    if bytes.len() > 8 {
176        return format!("implicit[{} bytes]", bytes.len());
177    }
178
179    let hex = bytes
180        .iter()
181        .map(|byte| format!("{byte:02x}"))
182        .collect::<Vec<_>>()
183        .join(" ");
184    format!("implicit[{hex}]")
185}
186
187/// A memory read requested by a future runtime lowering pass.
188#[derive(Debug, Clone, PartialEq)]
189pub struct UserMemoryRead {
190    pub address: AddressExpr,
191    pub size: MemoryAccessSize,
192}