From 2eb8b48eac0d755fa452b832c5d6393ba9e998a4 Mon Sep 17 00:00:00 2001 From: swananan Date: Thu, 23 Apr 2026 16:45:54 +0800 Subject: [PATCH] perf: share fast parser DWARF names Store parsed DIE names as Arc inside fast parser metadata so repeated name propagation and IndexEntry construction avoid deep String clones. Keep the public lightweight index key shape unchanged to limit the scope of the parser allocation optimization. --- ghostscope-dwarf/src/parser/fast_parser.rs | 89 +++++++++++++--------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/ghostscope-dwarf/src/parser/fast_parser.rs b/ghostscope-dwarf/src/parser/fast_parser.rs index 66cd12d3..b459000a 100644 --- a/ghostscope-dwarf/src/parser/fast_parser.rs +++ b/ghostscope-dwarf/src/parser/fast_parser.rs @@ -10,12 +10,15 @@ use crate::{ }; use gimli::Reader; use rayon::prelude::*; -use std::collections::{HashMap, HashSet}; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; use tracing::debug; #[derive(Clone, Default)] struct FunctionMetadata { - name: Option, + name: Option>, has_inline_attribute: bool, is_linkage_name: bool, is_external: Option, @@ -35,8 +38,8 @@ struct FunctionEntrySeed { #[derive(Clone, Default)] struct RawDieAttrs { - name: Option, - linkage_name: Option, + name: Option>, + linkage_name: Option>, has_inline_attribute: bool, is_external: Option, is_declaration: Option, @@ -177,13 +180,13 @@ impl<'a> DwarfParser<'a> { &mut metadata_cache, &mut visited, )?; - if let Some(name) = metadata.name.clone() { + if let Some(name) = metadata.name.as_ref() { let representative_addr = self.extract_representative_address_from_raw(unit, &raw_attrs)?; let entry_pc_cached = raw_attrs.entry_pc; let function_kind = Self::classify_function_kind(tag, representative_addr, entry_pc_cached); - let is_main = Self::is_main_function_from_raw(&raw_attrs, &name); + let is_main = Self::is_main_function_from_raw(&raw_attrs, name.as_ref()); let is_static = metadata .is_external .map(|external| !external) @@ -195,7 +198,7 @@ impl<'a> DwarfParser<'a> { is_linkage: metadata.is_linkage_name, ..Default::default() }; - let linkage_name = raw_attrs.linkage_name.clone(); + let linkage_name = raw_attrs.linkage_name.as_ref().map(Arc::clone); let seed = FunctionEntrySeed { die_offset: entry_offset, tag, @@ -206,7 +209,12 @@ impl<'a> DwarfParser<'a> { entry_pc: entry_pc_cached, function_kind, }; - Self::push_function_entries(&mut shard, &name, linkage_name, &seed); + Self::push_function_entries( + &mut shard, + Arc::clone(name), + linkage_name, + &seed, + ); } } gimli::constants::DW_TAG_inlined_subroutine => { @@ -218,7 +226,7 @@ impl<'a> DwarfParser<'a> { &mut metadata_cache, &mut visited, )?; - if let Some(name) = metadata.name.clone() { + if let Some(name) = metadata.name.as_ref() { let representative_addr = self.extract_representative_address_from_raw(unit, &raw_attrs)?; let entry_pc_cached = raw_attrs.entry_pc; @@ -234,7 +242,7 @@ impl<'a> DwarfParser<'a> { is_linkage: metadata.is_linkage_name, ..Default::default() }; - let linkage_name = raw_attrs.linkage_name.clone(); + let linkage_name = raw_attrs.linkage_name.as_ref().map(Arc::clone); let seed = FunctionEntrySeed { die_offset: entry_offset, tag, @@ -245,7 +253,12 @@ impl<'a> DwarfParser<'a> { entry_pc: entry_pc_cached, function_kind, }; - Self::push_function_entries(&mut shard, &name, linkage_name, &seed); + Self::push_function_entries( + &mut shard, + Arc::clone(name), + linkage_name, + &seed, + ); } } gimli::constants::DW_TAG_variable => { @@ -289,14 +302,14 @@ impl<'a> DwarfParser<'a> { tag_stack.push(tag); continue; } - let mut collected_names: Vec<(String, bool)> = Vec::new(); - let mut push_unique_name = |candidate: String, is_linkage_alias: bool| { + let mut collected_names: Vec<(Arc, bool)> = Vec::new(); + let mut push_unique_name = |candidate: Arc, is_linkage_alias: bool| { if candidate.is_empty() { return; } if collected_names .iter() - .any(|(existing, _)| existing == &candidate) + .any(|(existing, _)| existing.as_ref() == candidate.as_ref()) { return; } @@ -310,8 +323,8 @@ impl<'a> DwarfParser<'a> { push_unique_name(name, false); } - if let Some(linkage_name) = raw_attrs.linkage_name.clone() { - push_unique_name(linkage_name, true); + if let Some(linkage_name) = raw_attrs.linkage_name.as_ref() { + push_unique_name(Arc::clone(linkage_name), true); } if collected_names.is_empty() { @@ -333,7 +346,7 @@ impl<'a> DwarfParser<'a> { let mut entry_flags = flags; entry_flags.is_linkage = is_linkage_alias; let index_entry = IndexEntry { - name: std::sync::Arc::from(name.as_str()), + name: Arc::clone(&name), die_offset: entry_offset, unit_offset, tag, @@ -350,7 +363,7 @@ impl<'a> DwarfParser<'a> { cu_language, entry_offset ); - shard.push_variable_entry(name.clone(), index_entry); + shard.push_variable_entry(name.to_string(), index_entry); } } gimli::constants::DW_TAG_structure_type @@ -368,7 +381,7 @@ impl<'a> DwarfParser<'a> { ..Default::default() }; let index_entry = IndexEntry { - name: std::sync::Arc::from(name.as_str()), + name: Arc::clone(&name), die_offset: entry_offset, unit_offset, tag, @@ -378,7 +391,7 @@ impl<'a> DwarfParser<'a> { entry_pc: None, function_kind: FunctionDieKind::NotFunction, }; - shard.push_type_entry(name, index_entry); + shard.push_type_entry(name.to_string(), index_entry); } } _ => {} @@ -409,9 +422,9 @@ impl<'a> DwarfParser<'a> { } } - fn build_function_index_entry(name: &str, seed: &FunctionEntrySeed) -> IndexEntry { + fn build_function_index_entry(name: Arc, seed: &FunctionEntrySeed) -> IndexEntry { IndexEntry { - name: std::sync::Arc::from(name), + name, die_offset: seed.die_offset, unit_offset: seed.unit_offset, tag: seed.tag, @@ -425,23 +438,25 @@ impl<'a> DwarfParser<'a> { fn push_function_entries( shard: &mut InfoShard, - name: &str, - linkage_name: Option, + name: Arc, + linkage_name: Option>, seed: &FunctionEntrySeed, ) { shard.push_function_entry( - name.to_owned(), - Self::build_function_index_entry(name, seed), + name.to_string(), + Self::build_function_index_entry(Arc::clone(&name), seed), ); - if let Some(linkage_name) = linkage_name.filter(|linkage_name| linkage_name != name) { + if let Some(linkage_name) = + linkage_name.filter(|linkage_name| linkage_name.as_ref() != name.as_ref()) + { let mut alias_seed = seed.clone(); let mut alias_flags = alias_seed.flags; alias_flags.is_linkage = true; alias_seed.flags = alias_flags; shard.push_function_entry( - linkage_name.clone(), - Self::build_function_index_entry(&linkage_name, &alias_seed), + linkage_name.to_string(), + Self::build_function_index_entry(linkage_name, &alias_seed), ); } } @@ -450,10 +465,10 @@ impl<'a> DwarfParser<'a> { dwarf: &gimli::Dwarf, unit: &gimli::Unit, attr_value: gimli::AttributeValue, - ) -> Result> { + ) -> Result>> { if let Ok(string) = dwarf.attr_string(unit, attr_value) { if let Ok(s_str) = string.to_string_lossy() { - return Ok(Some(s_str.into_owned())); + return Ok(Some(Arc::::from(s_str.into_owned()))); } } Ok(None) @@ -586,9 +601,9 @@ impl<'a> DwarfParser<'a> { entry_offset: gimli::UnitOffset, attrs: &RawDieAttrs, visited: &mut HashSet, - ) -> Result> { - if let Some(name) = attrs.name.clone() { - return Ok(Some(name)); + ) -> Result>> { + if let Some(name) = attrs.name.as_ref() { + return Ok(Some(Arc::clone(name))); } if !visited.insert(entry_offset) { @@ -639,8 +654,8 @@ impl<'a> DwarfParser<'a> { self.resolve_name_from_raw(unit, entry_offset, attrs, &mut name_visited)? { metadata.name = Some(name); - } else if let Some(name) = attrs.linkage_name.clone() { - metadata.name = Some(name); + } else if let Some(name) = attrs.linkage_name.as_ref() { + metadata.name = Some(Arc::clone(name)); metadata.is_linkage_name = true; } @@ -666,7 +681,7 @@ impl<'a> DwarfParser<'a> { }; if metadata.name.is_none() { - metadata.name = origin_metadata.name.clone(); + metadata.name = origin_metadata.name.as_ref().map(Arc::clone); } if metadata.is_external.is_none() { metadata.is_external = origin_metadata.is_external;