Skip to content

Commit cc09431

Browse files
committed
refactor: tighten dwarf read plan boundary
Move pointer element planning into ghostscope-dwarf so compiler lowering consumes read/materialization plans without rewriting VariableLocation. Keep legacy variable-query APIs out of the main compiler and tool paths, and update tests to use PC-context visible-variable/read-plan entry points. Refs #148.
1 parent e2323c0 commit cc09431

11 files changed

Lines changed: 175 additions & 166 deletions

File tree

bins/dwarf-tool/src/main.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ enum Commands {
165165
}
166166

167167
#[derive(Debug, serde::Serialize)]
168-
struct VariableInfo {
168+
struct JsonVariableInfo {
169169
name: String,
170170
type_name: String,
171171
location: String,
@@ -180,7 +180,7 @@ struct AddressInfo {
180180
source_file: Option<String>,
181181
source_line: Option<u32>,
182182
source_column: Option<u32>,
183-
variables: Vec<VariableInfo>,
183+
variables: Vec<JsonVariableInfo>,
184184
}
185185

186186
#[derive(Debug, serde::Serialize)]
@@ -641,8 +641,8 @@ fn total_variables_in_query_results(addresses: &[AddressQueryResult]) -> usize {
641641
addresses.iter().map(query_address_variable_count).sum()
642642
}
643643

644-
fn variable_info_from_query(variable: &ghostscope_dwarf::VisibleVariable) -> VariableInfo {
645-
VariableInfo {
644+
fn variable_info_from_query(variable: &ghostscope_dwarf::VisibleVariable) -> JsonVariableInfo {
645+
JsonVariableInfo {
646646
name: variable.name.clone(),
647647
type_name: variable.type_name.clone(),
648648
location: format!("{}", variable.location),
@@ -1090,7 +1090,8 @@ async fn run_source_line_benchmark(
10901090
let mut run_total_variables = 0usize;
10911091

10921092
for module_address in &addresses {
1093-
run_total_variables += analyzer.get_all_variables_at_address(module_address)?.len();
1093+
let pc_context = analyzer.resolve_pc(module_address)?;
1094+
run_total_variables += analyzer.visible_variables(&pc_context)?.len();
10941095
}
10951096

10961097
query_times.push(start.elapsed());

e2e-tests/tests/member_pointer_compilation.rs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ use common::{init, OptimizationLevel, FIXTURES};
44

55
const TRACE_LINE: u32 = 68;
66

7+
fn field_path(fields: &[&str]) -> ghostscope_dwarf::VariableAccessPath {
8+
ghostscope_dwarf::VariableAccessPath::fields(fields.iter().map(|field| (*field).to_string()))
9+
}
10+
711
async fn compile_member_pointer_script(
812
script: &str,
913
opt_level: OptimizationLevel,
@@ -66,41 +70,34 @@ async fn test_member_pointer_planner_resolves_o2_chain_accesses() -> anyhow::Res
6670
);
6771

6872
for module_address in &addrs {
73+
let pc_context = analyzer.resolve_pc(module_address)?;
6974
let key_data = analyzer
70-
.plan_chain_access_read_plan(
71-
module_address,
72-
"h",
73-
&["key".to_string(), "data".to_string()],
74-
)
75+
.plan_variable_access_by_name(&pc_context, "h", &field_path(&["key", "data"]))
7576
.map_err(|e| {
7677
anyhow::anyhow!(
77-
"plan_chain_access_read_plan failed for h.key.data at 0x{:x}: {}",
78+
"plan_variable_access_by_name failed for h.key.data at 0x{:x}: {}",
7879
module_address.address,
7980
e
8081
)
8182
})?;
8283
anyhow::ensure!(
8384
key_data.is_some(),
84-
"plan_chain_access_read_plan returned None for h.key.data at 0x{:x}",
85+
"plan_variable_access_by_name returned None for h.key.data at 0x{:x}",
8586
module_address.address
8687
);
8788

8889
let header_pos = analyzer
89-
.plan_chain_access_read_plan(
90-
module_address,
91-
"r",
92-
&["header_in".to_string(), "pos".to_string()],
93-
)
90+
.plan_variable_access_by_name(&pc_context, "r", &field_path(&["header_in", "pos"]))
9491
.map_err(|e| {
9592
anyhow::anyhow!(
96-
"plan_chain_access_read_plan failed for r.header_in.pos at 0x{:x}: {}",
93+
"plan_variable_access_by_name failed for r.header_in.pos at 0x{:x}: {}",
9794
module_address.address,
9895
e
9996
)
10097
})?;
10198
anyhow::ensure!(
10299
header_pos.is_some(),
103-
"plan_chain_access_read_plan returned None for r.header_in.pos at 0x{:x}",
100+
"plan_variable_access_by_name returned None for r.header_in.pos at 0x{:x}",
104101
module_address.address
105102
);
106103
}
@@ -531,11 +528,12 @@ async fn test_complex_bitfield_chain_planner_resolves_member_offsets() -> anyhow
531528
);
532529

533530
for module_address in &addrs {
531+
let pc_context = analyzer.resolve_pc(module_address)?;
534532
let active = analyzer
535-
.plan_chain_access_read_plan(module_address, "c", &["active".to_string()])?
533+
.plan_variable_access_by_name(&pc_context, "c", &field_path(&["active"]))?
536534
.ok_or_else(|| anyhow::anyhow!("missing plan for c.active at {:?}", module_address))?;
537535
let flags = analyzer
538-
.plan_chain_access_read_plan(module_address, "c", &["flags".to_string()])?
536+
.plan_variable_access_by_name(&pc_context, "c", &field_path(&["flags"]))?
539537
.ok_or_else(|| anyhow::anyhow!("missing plan for c.flags at {:?}", module_address))?;
540538

541539
let expected_steps = vec![

e2e-tests/tests/optimized_inline_execution.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,18 +170,20 @@ async fn test_optimized_inline_struct_member_access_resolves_inline_parameter_na
170170
"No DWARF addresses found for inline_callsite_program.c:{INLINE_STATE_TRACE_LINE}"
171171
);
172172
for module_address in &addrs {
173+
let pc_context = analyzer.resolve_pc(module_address)?;
174+
let access_path = ghostscope_dwarf::VariableAccessPath::fields(["total_bytes".to_string()]);
173175
let planned = analyzer
174-
.plan_chain_access_read_plan(module_address, "state", &["total_bytes".to_string()])
176+
.plan_variable_access_by_name(&pc_context, "state", &access_path)
175177
.map_err(|e| {
176178
anyhow::anyhow!(
177-
"exec-path plan_chain_access_read_plan failed for 0x{:x}: {}",
179+
"exec-path plan_variable_access_by_name failed for 0x{:x}: {}",
178180
module_address.address,
179181
e
180182
)
181183
})?;
182184
anyhow::ensure!(
183185
planned.is_some(),
184-
"exec-path plan_chain_access_read_plan returned None for 0x{:x}",
186+
"exec-path plan_variable_access_by_name returned None for 0x{:x}",
185187
module_address.address
186188
);
187189
}
@@ -198,18 +200,20 @@ async fn test_optimized_inline_struct_member_access_resolves_inline_parameter_na
198200
"No PID-backed DWARF addresses found for inline_callsite_program.c:{INLINE_STATE_TRACE_LINE}"
199201
);
200202
for module_address in &pid_addrs {
203+
let pc_context = pid_analyzer.resolve_pc(module_address)?;
204+
let access_path = ghostscope_dwarf::VariableAccessPath::fields(["total_bytes".to_string()]);
201205
let planned = pid_analyzer
202-
.plan_chain_access_read_plan(module_address, "state", &["total_bytes".to_string()])
206+
.plan_variable_access_by_name(&pc_context, "state", &access_path)
203207
.map_err(|e| {
204208
anyhow::anyhow!(
205-
"pid-backed plan_chain_access_read_plan failed for 0x{:x}: {}",
209+
"pid-backed plan_variable_access_by_name failed for 0x{:x}: {}",
206210
module_address.address,
207211
e
208212
)
209213
})?;
210214
anyhow::ensure!(
211215
planned.is_some(),
212-
"pid-backed plan_chain_access_read_plan returned None for 0x{:x}",
216+
"pid-backed plan_variable_access_by_name returned None for 0x{:x}",
213217
module_address.address
214218
);
215219
}

ghostscope-compiler/src/ebpf/codegen.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -533,17 +533,38 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
533533
// Try DWARF resolution for the pointer side
534534
if let Some(var) = self.query_dwarf_for_complex_expr(ptr_side)? {
535535
if var.dwarf_type.is_some() {
536-
// Determine pointed-to/element type and compute location with scaled offset
537536
let index = sign * int_side;
538-
let (location, elem_ty) =
539-
self.compute_pointed_location_with_index(ptr_side, index)?;
540-
let address = ghostscope_dwarf::PlannedAddress::from_location(location)
541-
.ok_or_else(|| {
542-
CodeGenError::DwarfError(
537+
let pointed_plan = var
538+
.plan_pointer_element_index(index)
539+
.map_err(|err| CodeGenError::DwarfError(err.to_string()))?;
540+
let pc_address = self.get_compile_time_context()?.pc_address;
541+
let materialized =
542+
self.variable_read_plan_to_materialization(pointed_plan, pc_address)?;
543+
let elem_ty = materialized.dwarf_type.clone().ok_or_else(|| {
544+
CodeGenError::DwarfError(
545+
"Expression has no DWARF type information".to_string(),
546+
)
547+
})?;
548+
let address = match materialized.materialization {
549+
ghostscope_dwarf::VariableMaterialization::UserMemoryRead {
550+
address,
551+
} => address,
552+
ghostscope_dwarf::VariableMaterialization::Unavailable {
553+
availability,
554+
} => {
555+
return Err(Self::dwarf_expression_unavailable_error(
556+
&materialized.name,
557+
&availability,
558+
pc_address,
559+
))
560+
}
561+
_ => {
562+
return Err(CodeGenError::DwarfError(
543563
"pointer arithmetic did not produce an address-backed plan"
544564
.to_string(),
545-
)
546-
})?;
565+
))
566+
}
567+
};
547568
let data_len = Self::compute_read_size_for_type(&elem_ty);
548569
let module_hint = self.take_module_hint();
549570
if data_len == 0 {

ghostscope-compiler/src/ebpf/dwarf_bridge.rs

Lines changed: 2 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
66
use super::context::{CodeGenError, EbpfContext, Result};
77
use ghostscope_dwarf::{
8-
semantics::{add_location_offset, dereference_location},
98
AddressOrigin, Availability, ComputeStep, EntryValueCase, MemoryAccessSize, PlannedAddress,
109
PlannedAddressKind, SectionType, TypeInfo, VariableAccessPath, VariableAccessSegment,
1110
VariableLocation, VariableMaterializationPlan, VariableReadPlan,
@@ -1227,7 +1226,7 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
12271226
}
12281227

12291228
if let Some((global_module, plan)) = analyzer
1230-
.plan_global_chain_access_read_plan(&prefer_module, var_name, &[])
1229+
.plan_global_access_read_plan(&prefer_module, var_name, &VariableAccessPath::default())
12311230
.map_err(|err| CodeGenError::DwarfError(err.to_string()))?
12321231
{
12331232
debug!("Found DWARF global '{}' via variable read plan", var_name);
@@ -1382,74 +1381,6 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
13821381
};
13831382
Ok(Some((base, VariableAccessPath::new(segments))))
13841383
}
1385-
1386-
/// Compute a typed pointed-to location for expressions like `ptr +/- K` where K is an element index.
1387-
/// Returns a computed location along with the pointed-to DWARF type.
1388-
/// The offset is scaled by the element size of the pointer/array target type.
1389-
pub fn compute_pointed_location_with_index(
1390-
&mut self,
1391-
ptr_expr: &crate::script::Expr,
1392-
index: i64,
1393-
) -> Result<(VariableLocation, TypeInfo)> {
1394-
use ghostscope_dwarf::TypeInfo;
1395-
1396-
// Resolve the pointer expression via DWARF
1397-
let ptr_var = self
1398-
.query_dwarf_for_complex_expr(ptr_expr)?
1399-
.ok_or_else(|| CodeGenError::VariableNotFound(format!("{ptr_expr:?}")))?;
1400-
1401-
let ptr_ty = ptr_var.dwarf_type.as_ref().ok_or_else(|| {
1402-
CodeGenError::DwarfError("Expression has no DWARF type information".to_string())
1403-
})?;
1404-
1405-
// Unwrap typedef/qualified wrappers
1406-
let mut ty = ptr_ty;
1407-
loop {
1408-
match ty {
1409-
TypeInfo::TypedefType {
1410-
underlying_type, ..
1411-
} => ty = underlying_type.as_ref(),
1412-
TypeInfo::QualifiedType {
1413-
underlying_type, ..
1414-
} => ty = underlying_type.as_ref(),
1415-
_ => break,
1416-
}
1417-
}
1418-
1419-
// Extract pointed-to (element) type and element size
1420-
let (elem_ty, elem_size) = match ty {
1421-
TypeInfo::PointerType { target_type, .. } => {
1422-
let et = target_type.as_ref().clone();
1423-
let es = et.size();
1424-
let es = if es == 0 { 1 } else { es };
1425-
(et, es)
1426-
}
1427-
TypeInfo::ArrayType { element_type, .. } => {
1428-
let et = element_type.as_ref().clone();
1429-
let es = et.size();
1430-
let es = if es == 0 { 1 } else { es };
1431-
(et, es)
1432-
}
1433-
TypeInfo::FunctionType { .. } => {
1434-
return Err(CodeGenError::TypeError(
1435-
"Pointer arithmetic is not supported on function pointers".to_string(),
1436-
))
1437-
}
1438-
_ => {
1439-
return Err(CodeGenError::TypeError(
1440-
"Pointer arithmetic requires a pointer or array expression".to_string(),
1441-
))
1442-
}
1443-
};
1444-
1445-
let base_location = dereference_location(&ptr_var.location)
1446-
.map_err(|err| CodeGenError::DwarfError(err.to_string()))?;
1447-
let byte_offset = index.saturating_mul(elem_size as i64);
1448-
let location = add_location_offset(base_location, byte_offset)
1449-
.map_err(|err| CodeGenError::DwarfError(err.to_string()))?;
1450-
1451-
Ok((location, elem_ty))
1452-
}
14531384
}
14541385

14551386
#[cfg(test)]
@@ -1673,21 +1604,14 @@ mod tests {
16731604
"ptr",
16741605
"int*",
16751606
Some(ptr_ty),
1676-
location.clone(),
1607+
location,
16771608
Availability::Available,
16781609
);
16791610

16801611
let value = ctx
16811612
.variable_read_plan_to_llvm_value(&plan, 0, None)
16821613
.expect("absolute address value should lower");
16831614
assert!(matches!(value, BasicValueEnum::IntValue(_)));
1684-
1685-
let pointee = dereference_location(&location)
1686-
.expect("absolute address value should dereference to memory");
1687-
assert_eq!(
1688-
pointee,
1689-
VariableLocation::Address(AddressExpr::constant(0x2000))
1690-
);
16911615
}
16921616

16931617
#[test]

ghostscope-dwarf/src/analyzer/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ impl DwarfAnalyzer {
103103
let mut variables = Vec::new();
104104
let mut parameters = Vec::new();
105105

106-
for variable in self.get_all_variables_at_address(module_address)? {
106+
for variable in self.visible_variables_at_address(module_address)? {
107107
if variable.is_parameter {
108108
parameters.push(variable);
109109
} else {

ghostscope-dwarf/src/analyzer/plan_global.rs

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -74,20 +74,6 @@ impl DwarfAnalyzer {
7474
results
7575
}
7676

77-
/// Plan a global/static member chain as a neutral read plan.
78-
pub fn plan_global_chain_access_read_plan(
79-
&self,
80-
prefer_module: &PathBuf,
81-
base: &str,
82-
fields: &[String],
83-
) -> Result<Option<(PathBuf, VariableReadPlan)>> {
84-
self.plan_global_access_read_plan(
85-
prefer_module,
86-
base,
87-
&VariableAccessPath::fields(fields.iter().cloned()),
88-
)
89-
}
90-
9177
/// Plan a global/static source-level access path as a neutral read plan.
9278
pub fn plan_global_access_read_plan(
9379
&self,
@@ -162,16 +148,6 @@ impl DwarfAnalyzer {
162148
die_off: gimli::UnitOffset,
163149
provenance: Provenance,
164150
) -> Result<VariableReadPlan> {
165-
let variable = self.resolve_variable_by_offsets_in_module(module_path, cu_off, die_off)?;
166-
Ok(Self::read_plan_from_variable(variable, provenance))
167-
}
168-
169-
fn resolve_variable_by_offsets_in_module<P: AsRef<Path>>(
170-
&self,
171-
module_path: P,
172-
cu_off: gimli::DebugInfoOffset,
173-
die_off: gimli::UnitOffset,
174-
) -> Result<crate::parser::VariableWithEvaluation> {
175151
let path_buf = module_path.as_ref().to_path_buf();
176152
if let Some(module_data) = self.modules.get(&path_buf) {
177153
let items = vec![(cu_off, die_off)];
@@ -190,7 +166,7 @@ impl DwarfAnalyzer {
190166
var.dwarf_type = Some(ti);
191167
}
192168
}
193-
Ok(var)
169+
Ok(Self::read_plan_from_variable(var, provenance))
194170
} else {
195171
Err(anyhow::anyhow!(
196172
"Module {} not loaded",

0 commit comments

Comments
 (0)