diff --git a/bins/dwarf-tool/src/main.rs b/bins/dwarf-tool/src/main.rs index 8bda6ef..411c475 100644 --- a/bins/dwarf-tool/src/main.rs +++ b/bins/dwarf-tool/src/main.rs @@ -1061,7 +1061,7 @@ async fn run_source_line_benchmark( let (file_path, line_number) = parse_source_line(source)?; let load_start = Instant::now(); - let (mut analyzer, load_breakdown) = load_analyzer_with_breakdown(pid, target_path).await?; + let (analyzer, load_breakdown) = load_analyzer_with_breakdown(pid, target_path).await?; let loading_time = load_start.elapsed(); let mut query_times = Vec::with_capacity(runs); diff --git a/e2e-tests/tests/cpp_script_execution.rs b/e2e-tests/tests/cpp_script_execution.rs index 601948e..4e12440 100644 --- a/e2e-tests/tests/cpp_script_execution.rs +++ b/e2e-tests/tests/cpp_script_execution.rs @@ -10,7 +10,7 @@ async fn compile_cpp_complex_script( script: &str, ) -> anyhow::Result { let binary_path = FIXTURES.get_test_binary("cpp_complex_program")?; - let mut analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path) + let analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path) .await .map_err(|e| anyhow::anyhow!("failed to load DWARF for cpp_complex_program: {e}"))?; let compile_options = ghostscope_compiler::CompileOptions { @@ -18,7 +18,7 @@ async fn compile_cpp_complex_script( ..Default::default() }; - ghostscope_compiler::compile_script(script, &mut analyzer, None, Some(1), &compile_options) + ghostscope_compiler::compile_script(script, &analyzer, None, Some(1), &compile_options) .map_err(|e| anyhow::anyhow!("compile_script failed: {e}")) } diff --git a/e2e-tests/tests/dwarf_index_regressions.rs b/e2e-tests/tests/dwarf_index_regressions.rs index 0077a4a..0cce099 100644 --- a/e2e-tests/tests/dwarf_index_regressions.rs +++ b/e2e-tests/tests/dwarf_index_regressions.rs @@ -276,7 +276,7 @@ async fn assert_partitioned_ranges_source_line_query_recovers_function_scope( binary_path: PathBuf, scenario: &str, ) -> anyhow::Result<()> { - let mut analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path).await?; + let analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path).await?; let query_results = analyzer .query_source_line_best_effort("partitioned_ranges_program.c", 18) .map_err(|e| anyhow::anyhow!("Failed source-line query for {scenario}: {e}"))?; @@ -614,7 +614,7 @@ async fn test_inline_callsite_clang_dwarf5_resolves_debug_addr_entry_pc() -> any ); let target = spawn_inline_callsite_program(binary_path).await?; let query_result: anyhow::Result<()> = async { - let mut pid_analyzer = ghostscope_dwarf::DwarfAnalyzer::from_pid(target.host_pid()).await?; + let pid_analyzer = ghostscope_dwarf::DwarfAnalyzer::from_pid(target.host_pid()).await?; let query_results = pid_analyzer.query_source_line_best_effort("inline_callsite_program.c", INLINE_TRACE_LINE)?; anyhow::ensure!( diff --git a/e2e-tests/tests/member_pointer_compilation.rs b/e2e-tests/tests/member_pointer_compilation.rs index 90a7906..4c5a76a 100644 --- a/e2e-tests/tests/member_pointer_compilation.rs +++ b/e2e-tests/tests/member_pointer_compilation.rs @@ -9,7 +9,7 @@ async fn compile_member_pointer_script( opt_level: OptimizationLevel, ) -> anyhow::Result { let binary_path = FIXTURES.get_test_binary_with_opt("member_pointer_program", opt_level)?; - let mut analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path) + let analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path) .await .map_err(|e| anyhow::anyhow!("failed to load DWARF for member_pointer_program: {e}"))?; let compile_options = ghostscope_compiler::CompileOptions { @@ -17,7 +17,7 @@ async fn compile_member_pointer_script( ..Default::default() }; - ghostscope_compiler::compile_script(script, &mut analyzer, None, Some(1), &compile_options) + ghostscope_compiler::compile_script(script, &analyzer, None, Some(1), &compile_options) .map_err(|e| anyhow::anyhow!("compile_script failed: {e}")) } @@ -27,7 +27,7 @@ async fn test_member_pointer_planner_resolves_o2_chain_accesses() -> anyhow::Res let binary_path = FIXTURES.get_test_binary_with_opt("member_pointer_program", OptimizationLevel::O2)?; - let mut analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path) + let analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path) .await .map_err(|e| anyhow::anyhow!("failed to load DWARF for member_pointer_program: {e}"))?; let addrs = analyzer.lookup_addresses_by_source_line("member_pointer_program.c", TRACE_LINE); @@ -201,7 +201,7 @@ async fn test_complex_bitfield_chain_planner_resolves_member_offsets() -> anyhow let binary_path = FIXTURES.get_test_binary_with_opt("complex_types_program", OptimizationLevel::Debug)?; - let mut analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path) + let analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path) .await .map_err(|e| anyhow::anyhow!("failed to load DWARF for complex_types_program: {e}"))?; let addrs = analyzer.lookup_addresses_by_source_line("complex_types_program.c", 15); diff --git a/e2e-tests/tests/optimized_inline_call_value_execution.rs b/e2e-tests/tests/optimized_inline_call_value_execution.rs index f305e47..8b50460 100644 --- a/e2e-tests/tests/optimized_inline_call_value_execution.rs +++ b/e2e-tests/tests/optimized_inline_call_value_execution.rs @@ -104,7 +104,7 @@ async fn test_optimized_inline_parameters_are_live_before_internal_call() -> any init(); let binary_path = FIXTURES.get_test_binary("inline_call_value_program")?; - let mut analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path).await?; + let analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path).await?; let addrs = analyzer.lookup_addresses_by_source_line( "inline_call_value_program.c", INLINE_BEFORE_CALL_TRACE_LINE, @@ -134,7 +134,7 @@ async fn test_optimized_inline_parameters_are_live_before_internal_call() -> any } let target = spawn_inline_call_value_program(&binary_path).await?; - let mut pid_analyzer = ghostscope_dwarf::DwarfAnalyzer::from_pid(target.host_pid()).await?; + let pid_analyzer = ghostscope_dwarf::DwarfAnalyzer::from_pid(target.host_pid()).await?; let pid_results = pid_analyzer.query_source_line_best_effort( "inline_call_value_program.c", INLINE_BEFORE_CALL_TRACE_LINE, @@ -157,7 +157,7 @@ async fn test_optimized_inline_parameters_have_exact_values_before_internal_call init(); let binary_path = FIXTURES.get_test_binary("inline_call_value_program")?; - let mut analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path).await?; + let analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path).await?; let addrs = analyzer.lookup_addresses_by_source_line( "inline_call_value_program.c", INLINE_BEFORE_CALL_TRACE_LINE, @@ -221,7 +221,7 @@ async fn test_optimized_inline_parameters_survive_internal_call_sites() -> anyho init(); let binary_path = FIXTURES.get_test_binary("inline_call_value_program")?; - let mut analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path).await?; + let analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path).await?; let addrs = analyzer.lookup_addresses_by_source_line( "inline_call_value_program.c", INLINE_AFTER_CALL_TRACE_LINE, @@ -250,7 +250,7 @@ async fn test_optimized_inline_parameters_survive_internal_call_sites() -> anyho } let target = spawn_inline_call_value_program(&binary_path).await?; - let mut pid_analyzer = ghostscope_dwarf::DwarfAnalyzer::from_pid(target.host_pid()).await?; + let pid_analyzer = ghostscope_dwarf::DwarfAnalyzer::from_pid(target.host_pid()).await?; let pid_results = pid_analyzer.query_source_line_best_effort( "inline_call_value_program.c", INLINE_AFTER_CALL_TRACE_LINE, @@ -274,7 +274,7 @@ async fn test_entry_value_recovers_outer_parameter_inside_optimized_inline_after init(); let binary_path = FIXTURES.get_test_binary("inline_call_value_program")?; - let mut analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path).await?; + let analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path).await?; let addrs = analyzer.lookup_addresses_by_source_line( "inline_call_value_program.c", INLINE_AFTER_CALL_TRACE_LINE, diff --git a/e2e-tests/tests/optimized_inline_execution.rs b/e2e-tests/tests/optimized_inline_execution.rs index 3f22e04..92bbb8c 100644 --- a/e2e-tests/tests/optimized_inline_execution.rs +++ b/e2e-tests/tests/optimized_inline_execution.rs @@ -160,7 +160,7 @@ async fn test_optimized_inline_struct_member_access_resolves_inline_parameter_na init(); let binary_path = FIXTURES.get_test_binary("inline_callsite_program")?; - let mut analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path) + let analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path) .await .map_err(|e| anyhow::anyhow!("Failed to load DWARF for inline_callsite_program: {}", e))?; let addrs = analyzer @@ -190,7 +190,7 @@ async fn test_optimized_inline_struct_member_access_resolves_inline_parameter_na // The analyzer runs in the host test process, so it must inspect the host PID. // `visible_pid_from(observer)` is only correct for processes that actually run // inside the observer sandbox, such as GhostScope itself. - let mut pid_analyzer = ghostscope_dwarf::DwarfAnalyzer::from_pid(target.host_pid()).await?; + let pid_analyzer = ghostscope_dwarf::DwarfAnalyzer::from_pid(target.host_pid()).await?; let pid_addrs = pid_analyzer .lookup_addresses_by_source_line("inline_callsite_program.c", INLINE_STATE_TRACE_LINE); anyhow::ensure!( diff --git a/e2e-tests/tests/script_execution.rs b/e2e-tests/tests/script_execution.rs index 157963c..c7a3f3b 100644 --- a/e2e-tests/tests/script_execution.rs +++ b/e2e-tests/tests/script_execution.rs @@ -420,7 +420,7 @@ async fn test_capture_len_uses_scalar_script_var_from_dwarf_expr() -> anyhow::Re init(); let binary_path = FIXTURES.get_test_binary("sample_program")?; - let mut analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path) + let analyzer = ghostscope_dwarf::DwarfAnalyzer::from_exec_path(&binary_path) .await .map_err(|e| anyhow::anyhow!("failed to load DWARF for sample_program: {e}"))?; let script_content = r#" @@ -436,7 +436,7 @@ trace sample_lib.c:45 { }; let result = ghostscope_compiler::compile_script( script_content, - &mut analyzer, + &analyzer, None, Some(1), &compile_options, diff --git a/ghostscope-compiler/src/ebpf/context.rs b/ghostscope-compiler/src/ebpf/context.rs index e405d39..a998a46 100644 --- a/ghostscope-compiler/src/ebpf/context.rs +++ b/ghostscope-compiler/src/ebpf/context.rs @@ -80,7 +80,7 @@ pub struct EbpfContext<'ctx, 'dw> { pub optimized_out_vars: HashMap, // Optimized out variables pub var_pc_addresses: HashMap, // Variable -> PC address pub variable_context: Option, // Scope validation context - pub process_analyzer: Option<&'dw mut DwarfAnalyzer>, // Multi-module DWARF analyzer + pub(super) process_analyzer: Option<&'dw DwarfAnalyzer>, // Multi-module DWARF analyzer pub current_trace_id: Option, // Current trace_id being compiled pub current_compile_time_context: Option, // PC address and module for DWARF queries @@ -287,7 +287,7 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> { pub fn new_with_process_analyzer( context: &'ctx Context, module_name: &str, - process_analyzer: Option<&'dw mut DwarfAnalyzer>, + process_analyzer: Option<&'dw DwarfAnalyzer>, trace_id: Option, compile_options: &crate::CompileOptions, ) -> Result { diff --git a/ghostscope-compiler/src/ebpf/dwarf_bridge.rs b/ghostscope-compiler/src/ebpf/dwarf_bridge.rs index f4312d4..3d19d92 100644 --- a/ghostscope-compiler/src/ebpf/dwarf_bridge.rs +++ b/ghostscope-compiler/src/ebpf/dwarf_bridge.rs @@ -20,7 +20,7 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> { /// Compute section code for an address within a module (text=0, rodata=1, data=2, bss=3). fn section_code_for_address(&mut self, module_path: &str, link_addr: u64) -> u8 { - if let Some(analyzer) = self.process_analyzer.as_deref_mut() { + if let Some(analyzer) = self.process_analyzer { if let Some(st) = analyzer.classify_section_for_address(module_path, link_addr) { return match st { ghostscope_dwarf::core::SectionType::Text => 0, @@ -1320,7 +1320,6 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> { let analyzer = self .process_analyzer - .as_deref_mut() .ok_or_else(|| CodeGenError::DwarfError("No DWARF analyzer available".to_string()))?; let module_address = ghostscope_dwarf::ModuleAddress::new( @@ -1329,7 +1328,7 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> { ); let module_path_owned = module_path; - let lookup_globals = |analyzer: &mut ghostscope_dwarf::DwarfAnalyzer| -> Result< + let lookup_globals = |analyzer: &ghostscope_dwarf::DwarfAnalyzer| -> Result< Option<(std::path::PathBuf, VariableWithEvaluation)>, > { debug!( @@ -1647,7 +1646,7 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> { let ctx = self.get_compile_time_context()?; let module_path = ctx.module_path.clone(); let pc_address = ctx.pc_address; - let analyzer = self.process_analyzer.as_deref_mut().ok_or_else(|| { + let analyzer = self.process_analyzer.ok_or_else(|| { CodeGenError::DwarfError("No DWARF analyzer available".to_string()) })?; let module_address = ghostscope_dwarf::ModuleAddress::new( @@ -1801,7 +1800,7 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> { let ctx = self.get_compile_time_context()?; let module_path = ctx.module_path.clone(); let pc_address = ctx.pc_address; - let analyzer = self.process_analyzer.as_deref_mut().ok_or_else(|| { + let analyzer = self.process_analyzer.ok_or_else(|| { CodeGenError::DwarfError("No DWARF analyzer available".to_string()) })?; let module_address = ghostscope_dwarf::ModuleAddress::new( @@ -1927,7 +1926,6 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> { let pc_address = ctx.pc_address; let analyzer = self .process_analyzer - .as_deref_mut() .ok_or_else(|| CodeGenError::DwarfError("No DWARF analyzer available".to_string()))?; // First attempt: current module at current PC (locals/params) let module_address = ghostscope_dwarf::ModuleAddress::new( @@ -2095,7 +2093,7 @@ impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> { } let ctx = self.get_compile_time_context()?; let module_path = ctx.module_path.clone(); - if let Some(analyzer) = self.process_analyzer.as_deref_mut() { + if let Some(analyzer) = self.process_analyzer { let mut alias_used: Option = None; for n in candidate_names { // Prefer cross-module definitions first to avoid forward decls with size=0 in current CU diff --git a/ghostscope-compiler/src/lib.rs b/ghostscope-compiler/src/lib.rs index 7db4c83..4623ae3 100644 --- a/ghostscope-compiler/src/lib.rs +++ b/ghostscope-compiler/src/lib.rs @@ -123,7 +123,7 @@ impl Default for CompileOptions { /// to perform compilation across main executable and dynamic libraries pub fn compile_script( script_source: &str, - process_analyzer: &mut ghostscope_dwarf::DwarfAnalyzer, + process_analyzer: &ghostscope_dwarf::DwarfAnalyzer, pid: Option, trace_id: Option, compile_options: &CompileOptions, diff --git a/ghostscope-compiler/src/script/compiler.rs b/ghostscope-compiler/src/script/compiler.rs index c3f0408..b1ae2e4 100644 --- a/ghostscope-compiler/src/script/compiler.rs +++ b/ghostscope-compiler/src/script/compiler.rs @@ -75,7 +75,7 @@ pub struct FailedTarget { /// Unified AST compiler that performs DWARF queries and code generation in single pass pub struct AstCompiler<'a> { - process_analyzer: Option<&'a mut ghostscope_dwarf::DwarfAnalyzer>, + process_analyzer: Option<&'a ghostscope_dwarf::DwarfAnalyzer>, uprobe_configs: Vec, failed_targets: Vec, // Track failed compilation attempts binary_path_hint: Option, @@ -85,7 +85,7 @@ pub struct AstCompiler<'a> { impl<'a> AstCompiler<'a> { pub fn new( - process_analyzer: Option<&'a mut ghostscope_dwarf::DwarfAnalyzer>, + process_analyzer: Option<&'a ghostscope_dwarf::DwarfAnalyzer>, binary_path_hint: Option, starting_trace_id: u32, compile_options: crate::CompileOptions, @@ -223,7 +223,7 @@ impl<'a> AstCompiler<'a> { let default_msg = format!("No addresses resolved for source line {file_path}:{line_number}"); - let analyzer = match &mut self.process_analyzer { + let analyzer = match self.process_analyzer { Some(a) => a, None => return default_msg, }; @@ -329,7 +329,7 @@ impl<'a> AstCompiler<'a> { line_number, } => { // Obtain addresses first in a separate scope to avoid holding a mutable borrow of self - let module_addresses = if let Some(analyzer) = &mut self.process_analyzer { + let module_addresses = if let Some(analyzer) = self.process_analyzer { analyzer.lookup_addresses_by_source_line(file_path, *line_number) } else { Vec::new() @@ -590,7 +590,7 @@ impl<'a> AstCompiler<'a> { } TracePattern::FunctionName(func_name) => { // Resolve all addresses for the function name and generate per-PC programs - let module_addresses = if let Some(analyzer) = &mut self.process_analyzer { + let module_addresses = if let Some(analyzer) = self.process_analyzer { analyzer.lookup_function_addresses(func_name) } else { Vec::new() @@ -756,7 +756,7 @@ impl<'a> AstCompiler<'a> { let mut codegen_new = crate::ebpf::context::NewCodeGen::new_with_process_analyzer( &context, &ebpf_function_name, - self.process_analyzer.as_deref_mut(), + self.process_analyzer, Some(assigned_trace_id), &self.compile_options, ) diff --git a/ghostscope-dwarf/src/analyzer.rs b/ghostscope-dwarf/src/analyzer.rs index 2acf55c..6ecf25d 100644 --- a/ghostscope-dwarf/src/analyzer.rs +++ b/ghostscope-dwarf/src/analyzer.rs @@ -86,29 +86,29 @@ pub struct DwarfAnalyzer { impl DwarfAnalyzer { fn resolve_type_shallow_by_name_in_module_with_tags>( - &mut self, + &self, module_path: P, name: &str, tags: &[gimli::DwTag], ) -> Option { let path_buf = module_path.as_ref().to_path_buf(); self.modules - .get_mut(&path_buf) + .get(&path_buf) .and_then(|module_data| module_data.resolve_type_shallow_by_name_with_tags(name, tags)) } fn resolve_type_shallow_by_name_with_tags( - &mut self, + &self, name: &str, tags: &[gimli::DwTag], ) -> Option { self.modules - .values_mut() + .values() .find_map(|module_data| module_data.resolve_type_shallow_by_name_with_tags(name, tags)) } fn build_address_query_result( - &mut self, + &self, module_address: &ModuleAddress, ) -> Result { let mut variables = Vec::new(); @@ -140,7 +140,7 @@ impl DwarfAnalyzer { } fn query_module_addresses( - &mut self, + &self, module_addresses: Vec, ) -> Result> { module_addresses @@ -150,7 +150,7 @@ impl DwarfAnalyzer { } fn query_module_addresses_best_effort( - &mut self, + &self, module_addresses: Vec, query_label: &str, ) -> Result> { @@ -211,8 +211,8 @@ impl DwarfAnalyzer { /// Classify whether an address is inside an inlined subroutine instance /// Returns Some(true) if inline, Some(false) if a normal (non-inline) context, /// or None if the module/address cannot be resolved. - pub fn is_inline_at(&mut self, module_address: &ModuleAddress) -> Option { - if let Some(module_data) = self.modules.get_mut(&module_address.module_path) { + pub fn is_inline_at(&self, module_address: &ModuleAddress) -> Option { + if let Some(module_data) = self.modules.get(&module_address.module_path) { module_data.is_inline_at(module_address.address) } else { None @@ -221,7 +221,7 @@ impl DwarfAnalyzer { /// Resolve struct/class by name (shallow) in a specific module using only indexes pub fn resolve_struct_type_shallow_by_name_in_module>( - &mut self, + &self, module_path: P, name: &str, ) -> Option { @@ -236,7 +236,7 @@ impl DwarfAnalyzer { } /// Resolve struct/class by name (shallow) across modules (first match) - pub fn resolve_struct_type_shallow_by_name(&mut self, name: &str) -> Option { + pub fn resolve_struct_type_shallow_by_name(&self, name: &str) -> Option { self.resolve_type_shallow_by_name_with_tags( name, &[ @@ -248,7 +248,7 @@ impl DwarfAnalyzer { /// Resolve union by name (shallow) in a specific module pub fn resolve_union_type_shallow_by_name_in_module>( - &mut self, + &self, module_path: P, name: &str, ) -> Option { @@ -260,13 +260,13 @@ impl DwarfAnalyzer { } /// Resolve union by name (shallow) across modules (first match) - pub fn resolve_union_type_shallow_by_name(&mut self, name: &str) -> Option { + pub fn resolve_union_type_shallow_by_name(&self, name: &str) -> Option { self.resolve_type_shallow_by_name_with_tags(name, &[gimli::constants::DW_TAG_union_type]) } /// Resolve enum by name (shallow) in a specific module pub fn resolve_enum_type_shallow_by_name_in_module>( - &mut self, + &self, module_path: P, name: &str, ) -> Option { @@ -278,7 +278,7 @@ impl DwarfAnalyzer { } /// Resolve enum by name (shallow) across modules (first match) - pub fn resolve_enum_type_shallow_by_name(&mut self, name: &str) -> Option { + pub fn resolve_enum_type_shallow_by_name(&self, name: &str) -> Option { self.resolve_type_shallow_by_name_with_tags( name, &[gimli::constants::DW_TAG_enumeration_type], @@ -535,7 +535,7 @@ impl DwarfAnalyzer { } /// Query function debug information across all modules. - pub fn query_function(&mut self, name: &str) -> Result { + pub fn query_function(&self, name: &str) -> Result { let module_addresses = self.lookup_function_addresses(name); let addresses = self.query_module_addresses(module_addresses)?; Ok(FunctionQueryResult { @@ -546,7 +546,7 @@ impl DwarfAnalyzer { /// Query function debug information across all modules, skipping addresses /// that fail to resolve so callers can still display partial results. - pub fn query_function_best_effort(&mut self, name: &str) -> Result { + pub fn query_function_best_effort(&self, name: &str) -> Result { let module_addresses = self.lookup_function_addresses(name); let addresses = self .query_module_addresses_best_effort(module_addresses, &format!("function '{name}'"))?; @@ -576,7 +576,7 @@ impl DwarfAnalyzer { /// # Arguments /// * `module_address` - Module address containing both module path and address offset pub fn get_all_variables_at_address( - &mut self, + &self, module_address: &ModuleAddress, ) -> Result> { tracing::info!( @@ -585,7 +585,7 @@ impl DwarfAnalyzer { module_address.module_display() ); - if let Some(module_data) = self.modules.get_mut(&module_address.module_path) { + if let Some(module_data) = self.modules.get(&module_address.module_path) { module_data.get_all_variables_at_address(module_address.address) } else { tracing::warn!( @@ -601,12 +601,12 @@ impl DwarfAnalyzer { /// Plan a chain access (e.g., r.headers_in) and synthesize a VariableWithEvaluation pub fn plan_chain_access( - &mut self, + &self, module_address: &ModuleAddress, base_var: &str, chain: &[String], ) -> Result> { - if let Some(module_data) = self.modules.get_mut(&module_address.module_path) { + if let Some(module_data) = self.modules.get(&module_address.module_path) { module_data.plan_chain_access(module_address.address, base_var, chain) } else { Ok(None) @@ -666,7 +666,7 @@ impl DwarfAnalyzer { /// /// Returns None if unresolved; never falls back to unrelated globals. pub fn plan_global_chain_access( - &mut self, + &self, prefer_module: &PathBuf, base: &str, fields: &[String], @@ -753,13 +753,13 @@ impl DwarfAnalyzer { /// Resolve a variable by CU/DIE offsets in a specific module at an arbitrary address context (for globals) pub fn resolve_variable_by_offsets_in_module>( - &mut self, + &self, module_path: P, cu_off: gimli::DebugInfoOffset, die_off: gimli::UnitOffset, ) -> Result { let path_buf = module_path.as_ref().to_path_buf(); - if let Some(module_data) = self.modules.get_mut(&path_buf) { + if let Some(module_data) = self.modules.get(&path_buf) { let items = vec![(cu_off, die_off)]; let vars = module_data.resolve_variables_by_offsets_at_address(0, &items)?; let mut var = vars.into_iter().next().ok_or_else(|| { @@ -812,7 +812,7 @@ impl DwarfAnalyzer { /// Compute static offset for a global variable member chain pub fn compute_global_member_static_offset>( - &mut self, + &self, module_path: P, link_address: u64, cu_off: gimli::DebugInfoOffset, @@ -820,7 +820,7 @@ impl DwarfAnalyzer { fields: &[String], ) -> Result> { let path_buf = module_path.as_ref().to_path_buf(); - if let Some(module_data) = self.modules.get_mut(&path_buf) { + if let Some(module_data) = self.modules.get(&path_buf) { module_data.compute_global_member_static_offset(cu_off, var_die, link_address, fields) } else { Err(anyhow::anyhow!( @@ -851,11 +851,8 @@ impl DwarfAnalyzer { /// Lookup source location by module address /// Returns source location for the given module address - pub fn lookup_source_location( - &mut self, - module_address: &ModuleAddress, - ) -> Option { - if let Some(module_data) = self.modules.get_mut(&module_address.module_path) { + pub fn lookup_source_location(&self, module_address: &ModuleAddress) -> Option { + if let Some(module_data) = self.modules.get(&module_address.module_path) { module_data.lookup_source_location(module_address.address) } else { tracing::warn!("Module {} not found", module_address.module_display()); @@ -905,7 +902,7 @@ impl DwarfAnalyzer { /// Query source-line debug information across all modules. pub fn query_source_line( - &mut self, + &self, file_path: &str, line_number: u32, ) -> Result> { @@ -917,7 +914,7 @@ impl DwarfAnalyzer { /// addresses that fail to resolve so callers can still display partial /// results. pub fn query_source_line_best_effort( - &mut self, + &self, file_path: &str, line_number: u32, ) -> Result> { @@ -930,7 +927,7 @@ impl DwarfAnalyzer { /// Query a specific address within a module. pub fn query_address>( - &mut self, + &self, module_path: P, address: u64, ) -> Result { diff --git a/ghostscope-dwarf/src/objfile/globals.rs b/ghostscope-dwarf/src/objfile/globals.rs index 26014a6..9351db5 100644 --- a/ghostscope-dwarf/src/objfile/globals.rs +++ b/ghostscope-dwarf/src/objfile/globals.rs @@ -5,7 +5,7 @@ use std::collections::HashSet; impl LoadedObjfile { pub(crate) fn compute_global_member_static_offset( - &mut self, + &self, cu_off: gimli::DebugInfoOffset, var_die: gimli::UnitOffset, link_address: u64, diff --git a/ghostscope-dwarf/src/objfile/loaded.rs b/ghostscope-dwarf/src/objfile/loaded.rs index 38b0f40..47d89dd 100644 --- a/ghostscope-dwarf/src/objfile/loaded.rs +++ b/ghostscope-dwarf/src/objfile/loaded.rs @@ -10,7 +10,11 @@ use crate::{ parser::{CompilationUnit, DetailedParser}, }; use object::{Object, ObjectSegment}; -use std::{collections::HashMap, path::PathBuf, sync::Arc}; +use std::{ + collections::HashMap, + path::PathBuf, + sync::{Arc, RwLock}, +}; /// Complete DWARF data for a single loaded object file. #[derive(Debug)] @@ -26,7 +30,7 @@ pub(crate) struct LoadedObjfile { pub(super) _dwarf_mapped_file: Arc, pub(super) _binary_mapped_file: Arc, pub(super) text_symbol_starts_by_name: HashMap>, - pub(super) block_index: BlockIndex, + pub(super) block_index: RwLock, pub(super) type_name_index: Arc, pub(super) load_parse_ms: u64, pub(super) load_index_ms: u64, diff --git a/ghostscope-dwarf/src/objfile/loading.rs b/ghostscope-dwarf/src/objfile/loading.rs index 1f29f7f..4465d01 100644 --- a/ghostscope-dwarf/src/objfile/loading.rs +++ b/ghostscope-dwarf/src/objfile/loading.rs @@ -212,7 +212,7 @@ impl LoadedObjfile { cfi_index, dwarf, detailed_parser, - block_index: BlockIndex::new(), + block_index: std::sync::RwLock::new(BlockIndex::new()), type_name_index, _dwarf_mapped_file: mapped_file, _binary_mapped_file: binary_mapped, diff --git a/ghostscope-dwarf/src/objfile/variables.rs b/ghostscope-dwarf/src/objfile/variables.rs index 99963d4..ed2fb95 100644 --- a/ghostscope-dwarf/src/objfile/variables.rs +++ b/ghostscope-dwarf/src/objfile/variables.rs @@ -27,25 +27,55 @@ impl LoadedObjfile { .copied() } - pub(crate) fn is_inline_at(&mut self, address: u64) -> Option { - if self.block_index.find_function_by_pc(address).is_none() { - let builder = BlockIndexBuilder::new(self.dwarf()); - if let Some(func_entry) = self.find_function_index_entry_by_address(address) { - if let Some(fb) = - builder.build_for_function(func_entry.unit_offset, func_entry.die_offset) - { - self.block_index.add_functions(vec![fb]); - } - } else if let Some(cu_off) = self.lightweight_index.find_cu_by_address(address) { - if let Some(funcs) = builder.build_for_unit(cu_off) { - self.block_index.add_functions(funcs); - } + fn ensure_block_index_for_address(&self, address: u64) { + if self + .block_index + .read() + .expect("block index lock poisoned") + .find_function_by_pc(address) + .is_some() + { + return; + } + + let builder = BlockIndexBuilder::new(self.dwarf()); + if let Some(func_entry) = self.find_function_index_entry_by_address(address) { + if let Some(fb) = + builder.build_for_function(func_entry.unit_offset, func_entry.die_offset) + { + self.add_block_index_functions_if_missing(address, vec![fb]); } + } else if let Some(cu_off) = self.lightweight_index.find_cu_by_address(address) { + if let Some(funcs) = builder.build_for_unit(cu_off) { + self.add_block_index_functions_if_missing(address, funcs); + } + } + } + + fn add_block_index_functions_if_missing( + &self, + address: u64, + funcs: Vec, + ) -> bool { + let mut block_index = self.block_index.write().expect("block index lock poisoned"); + if block_index.find_function_by_pc(address).is_some() { + return false; } + block_index.add_functions(funcs); + true + } - let func = self.block_index.find_function_by_pc(address)?; + pub(crate) fn is_inline_at(&self, address: u64) -> Option { + self.ensure_block_index_for_address(address); - if let Some(inline_idx) = Self::find_innermost_inline_node(func, address) { + let func = self + .block_index + .read() + .expect("block index lock poisoned") + .find_function_by_pc(address) + .cloned()?; + + if let Some(inline_idx) = Self::find_innermost_inline_node(&func, address) { let dwarf = self.dwarf(); if let Ok(header) = dwarf.unit_header(func.cu_offset) { if let Ok(unit) = dwarf.unit(header) { @@ -63,7 +93,7 @@ impl LoadedObjfile { Some(false) } pub(super) fn plan_chain_access_from_var( - &mut self, + &self, address: u64, cu_offset: gimli::DebugInfoOffset, subprogram_die: gimli::UnitOffset, @@ -194,7 +224,7 @@ impl LoadedObjfile { } fn resolve_variables_by_offsets_at_address_with_cfa( - &mut self, + &self, address: u64, items: &[(gimli::DebugInfoOffset, gimli::UnitOffset)], get_cfa: Option<&dyn Fn(u64) -> Result>>, @@ -223,7 +253,7 @@ impl LoadedObjfile { } pub(crate) fn resolve_type_shallow_by_name_with_tags( - &mut self, + &self, name: &str, tags: &[gimli::DwTag], ) -> Option { @@ -257,7 +287,7 @@ impl LoadedObjfile { } pub(crate) fn get_all_variables_at_address( - &mut self, + &self, address: u64, ) -> Result> { let t0 = Instant::now(); @@ -269,7 +299,13 @@ impl LoadedObjfile { address ); - if self.block_index.find_function_by_pc(address).is_none() { + if self + .block_index + .read() + .expect("block index lock poisoned") + .find_function_by_pc(address) + .is_none() + { let b0 = Instant::now(); if let Some(cu_off) = self.lightweight_index.find_cu_by_address(address) { let builder = BlockIndexBuilder::new(self.dwarf()); @@ -279,14 +315,22 @@ impl LoadedObjfile { funcs.len(), cu_off ); - built_funcs += funcs.len(); - self.block_index.add_functions(funcs); + let funcs_len = funcs.len(); + if self.add_block_index_functions_if_missing(address, funcs) { + built_funcs += funcs_len; + } } } build_ms = b0.elapsed().as_millis(); } - if let Some(func) = self.block_index.find_function_by_pc(address).cloned() { + if let Some(func) = self + .block_index + .read() + .expect("block index lock poisoned") + .find_function_by_pc(address) + .cloned() + { let vars_in_func = func.nodes.iter().map(|n| n.variables.len()).sum::(); tracing::info!( "DWARF:get_vars fast_path_hit addr=0x{:x} vars_in_func={} built_funcs={} build_ms={} total_ms={}", @@ -397,7 +441,7 @@ impl LoadedObjfile { } pub(crate) fn plan_chain_access( - &mut self, + &self, address: u64, base_var: &str, chain: &[String], @@ -413,26 +457,41 @@ impl LoadedObjfile { chain.len() ); - if self.block_index.find_function_by_pc(address).is_none() { + if self + .block_index + .read() + .expect("block index lock poisoned") + .find_function_by_pc(address) + .is_none() + { let b0 = Instant::now(); let builder = BlockIndexBuilder::new(self.dwarf()); if let Some(func_entry) = self.find_function_index_entry_by_address(address) { if let Some(fb) = builder.build_for_function(func_entry.unit_offset, func_entry.die_offset) { - self.block_index.add_functions(vec![fb]); - built_funcs += 1; + if self.add_block_index_functions_if_missing(address, vec![fb]) { + built_funcs += 1; + } } } else if let Some(cu_off) = self.lightweight_index.find_cu_by_address(address) { if let Some(funcs) = builder.build_for_unit(cu_off) { - built_funcs += funcs.len(); - self.block_index.add_functions(funcs); + let funcs_len = funcs.len(); + if self.add_block_index_functions_if_missing(address, funcs) { + built_funcs += funcs_len; + } } } build_ms = b0.elapsed().as_millis(); } - if let Some(func) = self.block_index.find_function_by_pc(address).cloned() { + if let Some(func) = self + .block_index + .read() + .expect("block index lock poisoned") + .find_function_by_pc(address) + .cloned() + { let cfa_result = if self.cfi_index.is_some() { match self.get_cfa_result(address) { Ok(Some(cfa)) => Some(cfa), @@ -753,7 +812,7 @@ impl LoadedObjfile { } pub(crate) fn resolve_variables_by_offsets_at_address( - &mut self, + &self, address: u64, items: &[(gimli::DebugInfoOffset, gimli::UnitOffset)], ) -> Result> { diff --git a/ghostscope-dwarf/src/parser/detailed_parser.rs b/ghostscope-dwarf/src/parser/detailed_parser.rs index d0e8c73..d6d2a0e 100644 --- a/ghostscope-dwarf/src/parser/detailed_parser.rs +++ b/ghostscope-dwarf/src/parser/detailed_parser.rs @@ -736,7 +736,7 @@ impl DetailedParser { /// Parse a variable and optionally skip full DWARF type resolution #[allow(clippy::too_many_arguments)] pub fn parse_variable_entry_with_mode( - &mut self, + &self, entry: &gimli::DebuggingInformationEntry, unit: &gimli::Unit, dwarf: &gimli::Dwarf,