Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions bins/dwarf-tool/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ enum Commands {
}

#[derive(Debug, serde::Serialize)]
struct VariableInfo {
struct JsonVariableInfo {
name: String,
type_name: String,
location: String,
Expand All @@ -180,7 +180,7 @@ struct AddressInfo {
source_file: Option<String>,
source_line: Option<u32>,
source_column: Option<u32>,
variables: Vec<VariableInfo>,
variables: Vec<JsonVariableInfo>,
}

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

fn variable_info_from_query(variable: &ghostscope_dwarf::VisibleVariable) -> VariableInfo {
VariableInfo {
fn variable_info_from_query(variable: &ghostscope_dwarf::VisibleVariable) -> JsonVariableInfo {
JsonVariableInfo {
name: variable.name.clone(),
type_name: variable.type_name.clone(),
location: format!("{}", variable.location),
Expand Down Expand Up @@ -1090,7 +1090,8 @@ async fn run_source_line_benchmark(
let mut run_total_variables = 0usize;

for module_address in &addresses {
run_total_variables += analyzer.get_all_variables_at_address(module_address)?.len();
let pc_context = analyzer.resolve_pc(module_address)?;
run_total_variables += analyzer.visible_variables(&pc_context)?.len();
}

query_times.push(start.elapsed());
Expand Down
30 changes: 14 additions & 16 deletions e2e-tests/tests/member_pointer_compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ use common::{init, OptimizationLevel, FIXTURES};

const TRACE_LINE: u32 = 68;

fn field_path(fields: &[&str]) -> ghostscope_dwarf::VariableAccessPath {
ghostscope_dwarf::VariableAccessPath::fields(fields.iter().map(|field| (*field).to_string()))
}

async fn compile_member_pointer_script(
script: &str,
opt_level: OptimizationLevel,
Expand Down Expand Up @@ -66,41 +70,34 @@ async fn test_member_pointer_planner_resolves_o2_chain_accesses() -> anyhow::Res
);

for module_address in &addrs {
let pc_context = analyzer.resolve_pc(module_address)?;
let key_data = analyzer
.plan_chain_access_read_plan(
module_address,
"h",
&["key".to_string(), "data".to_string()],
)
.plan_variable_access_by_name(&pc_context, "h", &field_path(&["key", "data"]))
.map_err(|e| {
anyhow::anyhow!(
"plan_chain_access_read_plan failed for h.key.data at 0x{:x}: {}",
"plan_variable_access_by_name failed for h.key.data at 0x{:x}: {}",
module_address.address,
e
)
})?;
anyhow::ensure!(
key_data.is_some(),
"plan_chain_access_read_plan returned None for h.key.data at 0x{:x}",
"plan_variable_access_by_name returned None for h.key.data at 0x{:x}",
module_address.address
);

let header_pos = analyzer
.plan_chain_access_read_plan(
module_address,
"r",
&["header_in".to_string(), "pos".to_string()],
)
.plan_variable_access_by_name(&pc_context, "r", &field_path(&["header_in", "pos"]))
.map_err(|e| {
anyhow::anyhow!(
"plan_chain_access_read_plan failed for r.header_in.pos at 0x{:x}: {}",
"plan_variable_access_by_name failed for r.header_in.pos at 0x{:x}: {}",
module_address.address,
e
)
})?;
anyhow::ensure!(
header_pos.is_some(),
"plan_chain_access_read_plan returned None for r.header_in.pos at 0x{:x}",
"plan_variable_access_by_name returned None for r.header_in.pos at 0x{:x}",
module_address.address
);
}
Expand Down Expand Up @@ -531,11 +528,12 @@ async fn test_complex_bitfield_chain_planner_resolves_member_offsets() -> anyhow
);

for module_address in &addrs {
let pc_context = analyzer.resolve_pc(module_address)?;
let active = analyzer
.plan_chain_access_read_plan(module_address, "c", &["active".to_string()])?
.plan_variable_access_by_name(&pc_context, "c", &field_path(&["active"]))?
.ok_or_else(|| anyhow::anyhow!("missing plan for c.active at {:?}", module_address))?;
let flags = analyzer
.plan_chain_access_read_plan(module_address, "c", &["flags".to_string()])?
.plan_variable_access_by_name(&pc_context, "c", &field_path(&["flags"]))?
.ok_or_else(|| anyhow::anyhow!("missing plan for c.flags at {:?}", module_address))?;

let expected_steps = vec![
Expand Down
16 changes: 10 additions & 6 deletions e2e-tests/tests/optimized_inline_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,18 +170,20 @@ async fn test_optimized_inline_struct_member_access_resolves_inline_parameter_na
"No DWARF addresses found for inline_callsite_program.c:{INLINE_STATE_TRACE_LINE}"
);
for module_address in &addrs {
let pc_context = analyzer.resolve_pc(module_address)?;
let access_path = ghostscope_dwarf::VariableAccessPath::fields(["total_bytes".to_string()]);
let planned = analyzer
.plan_chain_access_read_plan(module_address, "state", &["total_bytes".to_string()])
.plan_variable_access_by_name(&pc_context, "state", &access_path)
.map_err(|e| {
anyhow::anyhow!(
"exec-path plan_chain_access_read_plan failed for 0x{:x}: {}",
"exec-path plan_variable_access_by_name failed for 0x{:x}: {}",
module_address.address,
e
)
})?;
anyhow::ensure!(
planned.is_some(),
"exec-path plan_chain_access_read_plan returned None for 0x{:x}",
"exec-path plan_variable_access_by_name returned None for 0x{:x}",
module_address.address
);
}
Expand All @@ -198,18 +200,20 @@ async fn test_optimized_inline_struct_member_access_resolves_inline_parameter_na
"No PID-backed DWARF addresses found for inline_callsite_program.c:{INLINE_STATE_TRACE_LINE}"
);
for module_address in &pid_addrs {
let pc_context = pid_analyzer.resolve_pc(module_address)?;
let access_path = ghostscope_dwarf::VariableAccessPath::fields(["total_bytes".to_string()]);
let planned = pid_analyzer
.plan_chain_access_read_plan(module_address, "state", &["total_bytes".to_string()])
.plan_variable_access_by_name(&pc_context, "state", &access_path)
.map_err(|e| {
anyhow::anyhow!(
"pid-backed plan_chain_access_read_plan failed for 0x{:x}: {}",
"pid-backed plan_variable_access_by_name failed for 0x{:x}: {}",
module_address.address,
e
)
})?;
anyhow::ensure!(
planned.is_some(),
"pid-backed plan_chain_access_read_plan returned None for 0x{:x}",
"pid-backed plan_variable_access_by_name returned None for 0x{:x}",
module_address.address
);
}
Expand Down
37 changes: 29 additions & 8 deletions ghostscope-compiler/src/ebpf/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,17 +533,38 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
// Try DWARF resolution for the pointer side
if let Some(var) = self.query_dwarf_for_complex_expr(ptr_side)? {
if var.dwarf_type.is_some() {
// Determine pointed-to/element type and compute location with scaled offset
let index = sign * int_side;
let (location, elem_ty) =
self.compute_pointed_location_with_index(ptr_side, index)?;
let address = ghostscope_dwarf::PlannedAddress::from_location(location)
.ok_or_else(|| {
CodeGenError::DwarfError(
let pointed_plan = var
.plan_pointer_element_index(index)
.map_err(|err| CodeGenError::DwarfError(err.to_string()))?;
let pc_address = self.get_compile_time_context()?.pc_address;
let materialized =
self.variable_read_plan_to_materialization(pointed_plan, pc_address)?;
let elem_ty = materialized.dwarf_type.clone().ok_or_else(|| {
CodeGenError::DwarfError(
"Expression has no DWARF type information".to_string(),
)
})?;
let address = match materialized.materialization {
ghostscope_dwarf::VariableMaterialization::UserMemoryRead {
address,
} => address,
ghostscope_dwarf::VariableMaterialization::Unavailable {
availability,
} => {
return Err(Self::dwarf_expression_unavailable_error(
&materialized.name,
&availability,
pc_address,
))
}
_ => {
return Err(CodeGenError::DwarfError(
"pointer arithmetic did not produce an address-backed plan"
.to_string(),
)
})?;
))
}
};
let data_len = Self::compute_read_size_for_type(&elem_ty);
let module_hint = self.take_module_hint();
if data_len == 0 {
Expand Down
80 changes: 2 additions & 78 deletions ghostscope-compiler/src/ebpf/dwarf_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

use super::context::{CodeGenError, EbpfContext, Result};
use ghostscope_dwarf::{
semantics::{add_location_offset, dereference_location},
AddressOrigin, Availability, ComputeStep, EntryValueCase, MemoryAccessSize, PlannedAddress,
PlannedAddressKind, SectionType, TypeInfo, VariableAccessPath, VariableAccessSegment,
VariableLocation, VariableMaterializationPlan, VariableReadPlan,
Expand Down Expand Up @@ -1227,7 +1226,7 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
}

if let Some((global_module, plan)) = analyzer
.plan_global_chain_access_read_plan(&prefer_module, var_name, &[])
.plan_global_access_read_plan(&prefer_module, var_name, &VariableAccessPath::default())
.map_err(|err| CodeGenError::DwarfError(err.to_string()))?
{
debug!("Found DWARF global '{}' via variable read plan", var_name);
Expand Down Expand Up @@ -1382,74 +1381,6 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
};
Ok(Some((base, VariableAccessPath::new(segments))))
}

/// Compute a typed pointed-to location for expressions like `ptr +/- K` where K is an element index.
/// Returns a computed location along with the pointed-to DWARF type.
/// The offset is scaled by the element size of the pointer/array target type.
pub fn compute_pointed_location_with_index(
&mut self,
ptr_expr: &crate::script::Expr,
index: i64,
) -> Result<(VariableLocation, TypeInfo)> {
use ghostscope_dwarf::TypeInfo;

// Resolve the pointer expression via DWARF
let ptr_var = self
.query_dwarf_for_complex_expr(ptr_expr)?
.ok_or_else(|| CodeGenError::VariableNotFound(format!("{ptr_expr:?}")))?;

let ptr_ty = ptr_var.dwarf_type.as_ref().ok_or_else(|| {
CodeGenError::DwarfError("Expression has no DWARF type information".to_string())
})?;

// Unwrap typedef/qualified wrappers
let mut ty = ptr_ty;
loop {
match ty {
TypeInfo::TypedefType {
underlying_type, ..
} => ty = underlying_type.as_ref(),
TypeInfo::QualifiedType {
underlying_type, ..
} => ty = underlying_type.as_ref(),
_ => break,
}
}

// Extract pointed-to (element) type and element size
let (elem_ty, elem_size) = match ty {
TypeInfo::PointerType { target_type, .. } => {
let et = target_type.as_ref().clone();
let es = et.size();
let es = if es == 0 { 1 } else { es };
(et, es)
}
TypeInfo::ArrayType { element_type, .. } => {
let et = element_type.as_ref().clone();
let es = et.size();
let es = if es == 0 { 1 } else { es };
(et, es)
}
TypeInfo::FunctionType { .. } => {
return Err(CodeGenError::TypeError(
"Pointer arithmetic is not supported on function pointers".to_string(),
))
}
_ => {
return Err(CodeGenError::TypeError(
"Pointer arithmetic requires a pointer or array expression".to_string(),
))
}
};

let base_location = dereference_location(&ptr_var.location)
.map_err(|err| CodeGenError::DwarfError(err.to_string()))?;
let byte_offset = index.saturating_mul(elem_size as i64);
let location = add_location_offset(base_location, byte_offset)
.map_err(|err| CodeGenError::DwarfError(err.to_string()))?;

Ok((location, elem_ty))
}
}

#[cfg(test)]
Expand Down Expand Up @@ -1673,21 +1604,14 @@ mod tests {
"ptr",
"int*",
Some(ptr_ty),
location.clone(),
location,
Availability::Available,
);

let value = ctx
.variable_read_plan_to_llvm_value(&plan, 0, None)
.expect("absolute address value should lower");
assert!(matches!(value, BasicValueEnum::IntValue(_)));

let pointee = dereference_location(&location)
.expect("absolute address value should dereference to memory");
assert_eq!(
pointee,
VariableLocation::Address(AddressExpr::constant(0x2000))
);
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion ghostscope-dwarf/src/analyzer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl DwarfAnalyzer {
let mut variables = Vec::new();
let mut parameters = Vec::new();

for variable in self.get_all_variables_at_address(module_address)? {
for variable in self.visible_variables_at_address(module_address)? {
if variable.is_parameter {
parameters.push(variable);
} else {
Expand Down
26 changes: 1 addition & 25 deletions ghostscope-dwarf/src/analyzer/plan_global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,6 @@ impl DwarfAnalyzer {
results
}

/// Plan a global/static member chain as a neutral read plan.
pub fn plan_global_chain_access_read_plan(
&self,
prefer_module: &PathBuf,
base: &str,
fields: &[String],
) -> Result<Option<(PathBuf, VariableReadPlan)>> {
self.plan_global_access_read_plan(
prefer_module,
base,
&VariableAccessPath::fields(fields.iter().cloned()),
)
}

/// Plan a global/static source-level access path as a neutral read plan.
pub fn plan_global_access_read_plan(
&self,
Expand Down Expand Up @@ -162,16 +148,6 @@ impl DwarfAnalyzer {
die_off: gimli::UnitOffset,
provenance: Provenance,
) -> Result<VariableReadPlan> {
let variable = self.resolve_variable_by_offsets_in_module(module_path, cu_off, die_off)?;
Ok(Self::read_plan_from_variable(variable, provenance))
}

fn resolve_variable_by_offsets_in_module<P: AsRef<Path>>(
&self,
module_path: P,
cu_off: gimli::DebugInfoOffset,
die_off: gimli::UnitOffset,
) -> Result<crate::parser::VariableWithEvaluation> {
let path_buf = module_path.as_ref().to_path_buf();
if let Some(module_data) = self.modules.get(&path_buf) {
let items = vec![(cu_off, die_off)];
Expand All @@ -190,7 +166,7 @@ impl DwarfAnalyzer {
var.dwarf_type = Some(ti);
}
}
Ok(var)
Ok(Self::read_plan_from_variable(var, provenance))
} else {
Err(anyhow::anyhow!(
"Module {} not loaded",
Expand Down
Loading
Loading