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
74 changes: 71 additions & 3 deletions ghostscope-dwarf/src/core/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,74 @@ pub struct IndexEntry {
/// For variables: vec![(address, address)] if static
/// Empty vec if no address (e.g., types, inlined functions without concrete instances)
pub address_ranges: Vec<(u64, u64)>,
/// Optional DW_AT_entry_pc for inline/call site DIEs (single-point locations)
/// Optional raw DW_AT_entry_pc for inline/call site DIEs.
/// Callers should prefer `validated_entry_pc()` when selecting probe PCs.
pub entry_pc: Option<u64>,
/// Explicit function role for addressable function-like DIEs.
pub function_kind: FunctionDieKind,
}

impl IndexEntry {
pub fn function_kind(&self) -> FunctionDieKind {
if self.function_kind != FunctionDieKind::NotFunction {
return self.function_kind;
}

match self.tag {
gimli::constants::DW_TAG_inlined_subroutine => FunctionDieKind::InlineInstance,
gimli::constants::DW_TAG_subprogram => {
if self.flags.is_inline_instance {
FunctionDieKind::InlineInstance
} else if !self.address_ranges.is_empty() || self.entry_pc.is_some() {
FunctionDieKind::ConcreteSubprogram
} else {
FunctionDieKind::AbstractSubprogram
}
}
_ => FunctionDieKind::NotFunction,
}
}

/// True when this DIE is a concrete DW_TAG_inlined_subroutine instance.
pub fn is_inline_instance(&self) -> bool {
self.function_kind() == FunctionDieKind::InlineInstance
}

/// True when this DIE is a concrete, addressable subprogram body.
pub fn is_concrete_subprogram(&self) -> bool {
self.function_kind() == FunctionDieKind::ConcreteSubprogram
}

/// Return entry_pc when it is usable as this DIE's own entry address.
///
/// Most DIEs with an entry_pc also carry ranges, and some producers emit
/// caller-side setup PCs that do not belong to the inline instance itself.
/// Reject those out-of-range PCs. However, DWARF can also encode
/// single-point inline/call-site scopes using only entry_pc and no ranges;
/// in that shape the point entry_pc is the only addressable location and
/// should be preserved.
pub fn validated_entry_pc(&self) -> Option<u64> {
self.entry_pc.filter(|pc| {
self.address_ranges.is_empty()
|| self
.address_ranges
.iter()
.any(|(start, end)| *start <= *pc && *pc < *end)
})
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FunctionDieKind {
#[default]
NotFunction,
/// Addressless DW_TAG_subprogram, typically an abstract definition or
/// declaration-like node used only for origin/specification metadata.
AbstractSubprogram,
/// Concrete out-of-line DW_TAG_subprogram with executable code ranges.
ConcreteSubprogram,
/// Concrete DW_TAG_inlined_subroutine instance inside a caller.
InlineInstance,
}

/// Index flags (inspired by GDB's cooked_index_flag_enum)
Expand All @@ -95,8 +161,10 @@ pub struct IndexFlags {
pub is_static: bool,
/// True if this is the program's main function
pub is_main: bool,
/// True if this is an inline function
pub is_inline: bool,
/// True if this DIE is a concrete DW_TAG_inlined_subroutine instance.
pub is_inline_instance: bool,
/// True if this DIE carries DW_AT_inline (or inherits that declaration).
pub has_inline_attribute: bool,
/// True if this entry uses the linkage name
pub is_linkage: bool,
/// True if this is just a type declaration (not definition)
Expand Down
9 changes: 5 additions & 4 deletions ghostscope-dwarf/src/index/lightweight_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,20 +386,21 @@ impl LightweightIndex {

tracing::trace!("Found {} entries for function '{}'", entries.len(), name);
for entry in &entries {
let display_addr = if entry.flags.is_inline {
let display_addr = if entry.is_inline_instance() {
entry
.entry_pc
.validated_entry_pc()
.or_else(|| entry.address_ranges.first().map(|(start, _)| *start))
} else {
entry.address_ranges.first().map(|(start, _)| *start)
};

if let Some(addr) = display_addr {
tracing::trace!(
" - {} at 0x{:x} (inline={}, {} ranges)",
" - {} at 0x{:x} (role={:?}, inline={}, {} ranges)",
entry.name,
addr,
entry.flags.is_inline,
entry.function_kind(),
entry.is_inline_instance(),
entry.address_ranges.len()
);
}
Expand Down
Loading
Loading