Skip to main content

calltrace/
dwarf_analyzer.rs

1//! DWARF Debug Information Analysis
2//!
3//! This module parses DWARF debugging information to extract function signatures,
4//! parameter types, and other metadata required for argument capture.
5
6use crate::error::{CallTraceError, Result};
7use std::collections::HashMap;
8use std::ffi::c_void;
9use std::fs;
10
11/// Function information extracted from DWARF
12#[derive(Debug, Clone)]
13pub struct FunctionInfo {
14    pub name: String,
15    pub address: u64,
16    pub size: Option<u64>,
17    pub parameters: Vec<ParameterInfo>,
18    pub return_type: Option<TypeInfo>,
19    pub source_file: Option<String>,
20    pub line_number: Option<u32>,
21}
22
23/// Parameter information for function arguments
24#[derive(Debug, Clone)]
25pub struct ParameterInfo {
26    pub name: String,
27    pub type_info: TypeInfo,
28    pub location: Option<String>, // DW_AT_location if available
29}
30
31/// Type information extracted from DWARF
32#[derive(Debug, Clone)]
33pub struct TypeInfo {
34    pub name: String,
35    pub size: Option<u64>,
36    pub is_pointer: bool,
37    pub is_const: bool,
38    pub is_struct: bool,
39    pub is_array: bool,
40    pub array_size: Option<u64>,
41    pub base_type: Option<Box<TypeInfo>>,
42}
43
44/// DWARF analyzer context that caches parsed information
45pub struct DwarfAnalyzer {
46    executable_path: String,
47    function_cache: HashMap<u64, FunctionInfo>,
48    type_cache: HashMap<u64, TypeInfo>,
49    symbol_cache: HashMap<u64, String>, // Cache for resolved symbols
50    base_address: Option<u64>,          // Program base address
51    #[cfg(feature = "dwarf_support")]
52    dwarf_data: Option<DwarfData>,
53}
54
55/// Cached DWARF data
56#[cfg(feature = "dwarf_support")]
57struct DwarfData {
58    #[allow(dead_code)]
59    dwarf: (), // Simplified for now
60    #[allow(dead_code)]
61    file_data: Vec<u8>, // Keep file data alive
62}
63
64impl DwarfAnalyzer {
65    /// Create a new DWARF analyzer for the specified executable
66    pub fn new(executable_path: &str) -> Result<Self> {
67        // Try to get the real executable path
68        let real_exe_path = std::fs::read_link("/proc/self/exe")
69            .unwrap_or_else(|_| executable_path.into())
70            .to_string_lossy()
71            .to_string();
72
73        let mut analyzer = DwarfAnalyzer {
74            executable_path: real_exe_path,
75            function_cache: HashMap::new(),
76            type_cache: HashMap::new(),
77            symbol_cache: HashMap::new(),
78            base_address: None,
79            #[cfg(feature = "dwarf_support")]
80            dwarf_data: None,
81        };
82
83        // Try to load DWARF data
84        #[cfg(feature = "dwarf_support")]
85        if let Err(e) = analyzer.load_dwarf_data() {
86            // Log warning but continue - we can still work with limited functionality
87            eprintln!("Warning: Failed to load DWARF data: {}", e);
88        }
89
90        // Initialize base address
91        analyzer.base_address = analyzer.get_program_base_address();
92
93        Ok(analyzer)
94    }
95
96    /// Load DWARF debugging information from the executable
97    #[cfg(feature = "dwarf_support")]
98    fn load_dwarf_data(&mut self) -> Result<()> {
99        let file_data = fs::read(&self.executable_path)
100            .map_err(|e| CallTraceError::DwarfError(format!("Failed to read executable: {}", e)))?;
101
102        // TODO: Temporary simplified implementation
103        // In the future, this will parse object file and load DWARF sections:
104        /*
105        let object = object::File::parse(&file_data[..])?;
106        let load_section = |id: gimli::SectionId| -> std::result::Result<Cow<[u8]>, gimli::Error> {
107            match object.section_by_name(id.name()) {
108                Some(ref section) => Ok(section
109                    .uncompressed_data()
110                    .unwrap_or(Cow::Borrowed(&[][..]))),
111                None => Ok(Cow::Borrowed(&[][..])),
112            }
113        };
114        let dwarf_cow = Dwarf::load(&load_section)?;
115        */
116        // Keep file data alive for future use
117        self.dwarf_data = Some(DwarfData {
118            dwarf: (),
119            file_data,
120        });
121
122        Ok(())
123    }
124
125    /// Get function information for a given address
126    pub fn get_function_info(&mut self, address: u64) -> Result<FunctionInfo> {
127        // Check cache first
128        if let Some(cached) = self.function_cache.get(&address) {
129            return Ok(cached.clone());
130        }
131
132        // Try to extract from DWARF data
133        #[cfg(feature = "dwarf_support")]
134        if let Some(info) = self.extract_function_info_from_dwarf(address)? {
135            self.function_cache.insert(address, info.clone());
136            return Ok(info);
137        }
138
139        // Fallback: create minimal function info using dladdr
140        let fallback_info = self.create_fallback_function_info(address)?;
141        self.function_cache.insert(address, fallback_info.clone());
142        Ok(fallback_info)
143    }
144
145    /// Extract function information from DWARF data
146    #[cfg(feature = "dwarf_support")]
147    fn extract_function_info_from_dwarf(&mut self, _address: u64) -> Result<Option<FunctionInfo>> {
148        // TODO: Temporary simplified implementation due to gimli API changes
149        // Full implementation will be restored after updating to new gimli API
150        Ok(None)
151    }
152
153    /*
154    // TODO: Restore these functions with updated gimli API
155
156    /// Extract function name from DIE
157    #[cfg(feature = "dwarf_support")]
158    fn extract_function_name(
159        &self,
160        dwarf: &Dwarf<gimli::EndianSlice<'static, gimli::LittleEndian>>,
161        unit: &gimli::Unit<gimli::EndianSlice<'static, gimli::LittleEndian>>,
162        entry: &gimli::DebuggingInformationEntry<gimli::EndianSlice<'static, gimli::LittleEndian>>,
163    ) -> Result<Option<String>> {
164        // Try DW_AT_name first
165        if let Some(name_attr) = entry.attr_value(gimli::DW_AT_name)
166            .map_err(|e| CallTraceError::DwarfError(format!("Error reading name: {}", e)))?
167        {
168            if let gimli::AttributeValue::DebugStrRef(offset) = name_attr {
169                let name = dwarf.debug_str.get_str(offset)
170                    .map_err(|e| CallTraceError::DwarfError(format!("Error reading string: {}", e)))?;
171                return Ok(Some(name.to_string_lossy().to_string()));
172            }
173        }
174
175        // Try DW_AT_linkage_name for mangled names
176        if let Some(linkage_attr) = entry.attr_value(gimli::DW_AT_linkage_name)
177            .map_err(|e| CallTraceError::DwarfError(format!("Error reading linkage name: {}", e)))?
178        {
179            if let gimli::AttributeValue::DebugStrRef(offset) = linkage_attr {
180                let name = dwarf.debug_str.get_str(offset)
181                    .map_err(|e| CallTraceError::DwarfError(format!("Error reading string: {}", e)))?;
182                let demangled = demangle_function_name(&name.to_string_lossy());
183                return Ok(Some(demangled));
184            }
185        }
186
187        Ok(None)
188    }
189
190    /// Extract function parameters from DIE
191    #[cfg(feature = "dwarf_support")]
192    fn extract_function_parameters(
193        &mut self,
194        dwarf: &Dwarf<gimli::EndianSlice<'static, gimli::LittleEndian>>,
195        unit: &gimli::Unit<gimli::EndianSlice<'static, gimli::LittleEndian>>,
196        entry: &gimli::DebuggingInformationEntry<gimli::EndianSlice<'static, gimli::LittleEndian>>,
197    ) -> Result<Vec<ParameterInfo>> {
198        let mut parameters = Vec::new();
199        let mut children = entry.children();
200
201        while let Some(child) = children.next()
202            .map_err(|e| CallTraceError::DwarfError(format!("Error reading child: {}", e)))?
203        {
204            if child.tag() == gimli::DW_TAG_formal_parameter {
205                if let Some(param) = self.extract_parameter_info(dwarf, unit, &child)? {
206                    parameters.push(param);
207                }
208            }
209        }
210
211        Ok(parameters)
212    }
213
214    /// Extract individual parameter information
215    #[cfg(feature = "dwarf_support")]
216    fn extract_parameter_info(
217        &mut self,
218        dwarf: &Dwarf<gimli::EndianSlice<'static, gimli::LittleEndian>>,
219        unit: &gimli::Unit<gimli::EndianSlice<'static, gimli::LittleEndian>>,
220        entry: &gimli::DebuggingInformationEntry<gimli::EndianSlice<'static, gimli::LittleEndian>>,
221    ) -> Result<Option<ParameterInfo>> {
222        // Extract parameter name
223        let name = if let Some(name_attr) = entry.attr_value(gimli::DW_AT_name)
224            .map_err(|e| CallTraceError::DwarfError(format!("Error reading param name: {}", e)))?
225        {
226            if let gimli::AttributeValue::DebugStrRef(offset) = name_attr {
227                dwarf.debug_str.get_str(offset)
228                    .map_err(|e| CallTraceError::DwarfError(format!("Error reading param string: {}", e)))?
229                    .to_string_lossy().to_string()
230            } else {
231                "unnamed".to_string()
232            }
233        } else {
234            "unnamed".to_string()
235        };
236
237        // Extract type information
238        let type_info = if let Some(type_attr) = entry.attr_value(gimli::DW_AT_type)
239            .map_err(|e| CallTraceError::DwarfError(format!("Error reading param type: {}", e)))?
240        {
241            if let gimli::AttributeValue::UnitRef(type_offset) = type_attr {
242                self.extract_type_info_from_offset(dwarf, unit, type_offset)?
243            } else {
244                TypeInfo {
245                    name: "unknown".to_string(),
246                    size: None,
247                    is_pointer: false,
248                    is_const: false,
249                    is_struct: false,
250                    is_array: false,
251                    array_size: None,
252                    base_type: None,
253                }
254            }
255        } else {
256            TypeInfo {
257                name: "unknown".to_string(),
258                size: None,
259                is_pointer: false,
260                is_const: false,
261                is_struct: false,
262                is_array: false,
263                array_size: None,
264                base_type: None,
265            }
266        };
267
268        // Extract location information (register, stack offset, etc.)
269        let location = if let Some(loc_attr) = entry.attr_value(gimli::DW_AT_location)
270            .map_err(|e| CallTraceError::DwarfError(format!("Error reading param location: {}", e)))?
271        {
272            // This would parse DWARF location expressions
273            // For now, we'll just indicate that location data exists
274            Some("dwarf_location".to_string())
275        } else {
276            None
277        };
278
279        Ok(Some(ParameterInfo {
280            name,
281            type_info,
282            location,
283        }))
284    }
285
286    /// Extract type information from a type offset
287    #[cfg(feature = "dwarf_support")]
288    fn extract_type_info_from_offset(
289        &mut self,
290        dwarf: &Dwarf<gimli::EndianSlice<'static, gimli::LittleEndian>>,
291        unit: &gimli::Unit<gimli::EndianSlice<'static, gimli::LittleEndian>>,
292        type_offset: gimli::UnitOffset,
293    ) -> Result<TypeInfo> {
294        // Check cache first
295        let cache_key = type_offset.0 as u64;
296        if let Some(cached_type) = self.type_cache.get(&cache_key) {
297            return Ok(cached_type.clone());
298        }
299
300        // Get the type DIE
301        let mut entries = unit.entries_at_offset(type_offset)
302            .map_err(|e| CallTraceError::DwarfError(format!("Error getting type entry: {}", e)))?;
303
304        let (_, type_entry) = entries.next_entry()
305            .map_err(|e| CallTraceError::DwarfError(format!("Error reading type entry: {}", e)))?
306            .ok_or_else(|| CallTraceError::DwarfError("Type entry not found".to_string()))?;
307
308        let type_info = self.extract_type_info_from_die(dwarf, unit, &type_entry)?;
309
310        // Cache the result
311        self.type_cache.insert(cache_key, type_info.clone());
312
313        Ok(type_info)
314    }
315
316    /// Extract detailed type information from a type DIE
317    #[cfg(feature = "dwarf_support")]
318    fn extract_type_info_from_die(
319        &mut self,
320        dwarf: &Dwarf<gimli::EndianSlice<'static, gimli::LittleEndian>>,
321        unit: &gimli::Unit<gimli::EndianSlice<'static, gimli::LittleEndian>>,
322        entry: &gimli::DebuggingInformationEntry<gimli::EndianSlice<'static, gimli::LittleEndian>>,
323    ) -> Result<TypeInfo> {
324        let tag = entry.tag();
325
326        match tag {
327            gimli::DW_TAG_base_type => {
328                self.extract_base_type_info(dwarf, unit, entry)
329            }
330            gimli::DW_TAG_pointer_type => {
331                self.extract_pointer_type_info(dwarf, unit, entry)
332            }
333            gimli::DW_TAG_array_type => {
334                self.extract_array_type_info(dwarf, unit, entry)
335            }
336            gimli::DW_TAG_structure_type | gimli::DW_TAG_class_type => {
337                self.extract_struct_type_info(dwarf, unit, entry)
338            }
339            gimli::DW_TAG_const_type => {
340                self.extract_const_type_info(dwarf, unit, entry)
341            }
342            gimli::DW_TAG_typedef => {
343                self.extract_typedef_info(dwarf, unit, entry)
344            }
345            _ => {
346                // Fallback for unknown types
347                Ok(TypeInfo {
348                    name: format!("unknown_type_{:?}", tag),
349                    size: entry.attr_value(gimli::DW_AT_byte_size)
350                        .ok()
351                        .flatten()
352                        .and_then(|v| {
353                            if let gimli::AttributeValue::Udata(size) = v {
354                                Some(size)
355                            } else {
356                                None
357                            }
358                        }),
359                    is_pointer: false,
360                    is_const: false,
361                    is_struct: false,
362                    is_array: false,
363                    array_size: None,
364                    base_type: None,
365                })
366            }
367        }
368    }
369
370    /// Extract return type information
371    #[cfg(feature = "dwarf_support")]
372    fn extract_return_type(
373        &mut self,
374        dwarf: &Dwarf<gimli::EndianSlice<'static, gimli::LittleEndian>>,
375        unit: &gimli::Unit<gimli::EndianSlice<'static, gimli::LittleEndian>>,
376        entry: &gimli::DebuggingInformationEntry<gimli::EndianSlice<'static, gimli::LittleEndian>>,
377    ) -> Result<Option<TypeInfo>> {
378        if let Some(type_attr) = entry.attr_value(gimli::DW_AT_type)
379            .map_err(|e| CallTraceError::DwarfError(format!("Error reading return type: {}", e)))?
380        {
381            if let gimli::AttributeValue::UnitRef(type_offset) = type_attr {
382                let type_info = self.extract_type_info_from_offset(dwarf, unit, type_offset)?;
383                return Ok(Some(type_info));
384            }
385        }
386
387        // No return type means void
388        Ok(None)
389    }
390
391    /// Extract source file and line number information
392    #[cfg(feature = "dwarf_support")]
393    fn extract_source_location(
394        &self,
395        dwarf: &Dwarf<gimli::EndianSlice<'static, gimli::LittleEndian>>,
396        unit: &gimli::Unit<gimli::EndianSlice<'static, gimli::LittleEndian>>,
397        entry: &gimli::DebuggingInformationEntry<gimli::EndianSlice<'static, gimli::LittleEndian>>,
398    ) -> Result<(Option<String>, Option<u32>)> {
399        let mut source_file = None;
400        let mut line_number = None;
401
402        // Extract line number
403        if let Some(line_attr) = entry.attr_value(gimli::DW_AT_decl_line)
404            .map_err(|e| CallTraceError::DwarfError(format!("Error reading line: {}", e)))?
405        {
406            if let gimli::AttributeValue::Udata(line) = line_attr {
407                line_number = Some(line as u32);
408            }
409        }
410
411        // Extract source file
412        if let Some(file_attr) = entry.attr_value(gimli::DW_AT_decl_file)
413            .map_err(|e| CallTraceError::DwarfError(format!("Error reading file: {}", e)))?
414        {
415            if let gimli::AttributeValue::Udata(file_index) = file_attr {
416                // This would require parsing the line number table to get the actual filename
417                // For now, we'll just use the file index
418                source_file = Some(format!("file_{}", file_index));
419            }
420        }
421
422        Ok((source_file, line_number))
423    }
424
425    /// Extract base type information (int, char, float, etc.)
426    #[cfg(feature = "dwarf_support")]
427    fn extract_base_type_info(
428        &self,
429        dwarf: &Dwarf<gimli::EndianSlice<'static, gimli::LittleEndian>>,
430        _unit: &gimli::Unit<gimli::EndianSlice<'static, gimli::LittleEndian>>,
431        entry: &gimli::DebuggingInformationEntry<gimli::EndianSlice<'static, gimli::LittleEndian>>,
432    ) -> Result<TypeInfo> {
433        // Extract type name
434        let name = if let Some(name_attr) = entry.attr_value(gimli::DW_AT_name)
435            .map_err(|e| CallTraceError::DwarfError(format!("Error reading base type name: {}", e)))?
436        {
437            if let gimli::AttributeValue::DebugStrRef(offset) = name_attr {
438                dwarf.debug_str.get_str(offset)
439                    .map_err(|e| CallTraceError::DwarfError(format!("Error reading base type string: {}", e)))?
440                    .to_string_lossy().to_string()
441            } else {
442                "unknown_base_type".to_string()
443            }
444        } else {
445            "unknown_base_type".to_string()
446        };
447
448        // Extract size
449        let size = entry.attr_value(gimli::DW_AT_byte_size)
450            .map_err(|e| CallTraceError::DwarfError(format!("Error reading base type size: {}", e)))?
451            .and_then(|v| {
452                if let gimli::AttributeValue::Udata(size) = v {
453                    Some(size)
454                } else {
455                    None
456                }
457            });
458
459        Ok(TypeInfo {
460            name,
461            size,
462            is_pointer: false,
463            is_const: false,
464            is_struct: false,
465            is_array: false,
466            array_size: None,
467            base_type: None,
468        })
469    }
470
471    /// Extract pointer type information
472    #[cfg(feature = "dwarf_support")]
473    fn extract_pointer_type_info(
474        &mut self,
475        dwarf: &Dwarf<gimli::EndianSlice<'static, gimli::LittleEndian>>,
476        unit: &gimli::Unit<gimli::EndianSlice<'static, gimli::LittleEndian>>,
477        entry: &gimli::DebuggingInformationEntry<gimli::EndianSlice<'static, gimli::LittleEndian>>,
478    ) -> Result<TypeInfo> {
479        // Get the pointed-to type
480        let base_type = if let Some(type_attr) = entry.attr_value(gimli::DW_AT_type)
481            .map_err(|e| CallTraceError::DwarfError(format!("Error reading pointer target type: {}", e)))?
482        {
483            if let gimli::AttributeValue::UnitRef(type_offset) = type_attr {
484                Some(Box::new(self.extract_type_info_from_offset(dwarf, unit, type_offset)?))
485            } else {
486                None
487            }
488        } else {
489            // Void pointer
490            None
491        };
492
493        // Construct pointer type name
494        let name = if let Some(ref base) = base_type {
495            format!("{}*", base.name)
496        } else {
497            "void*".to_string()
498        };
499
500        // Pointer size (typically 8 bytes on 64-bit systems)
501        let size = entry.attr_value(gimli::DW_AT_byte_size)
502            .map_err(|e| CallTraceError::DwarfError(format!("Error reading pointer size: {}", e)))?
503            .and_then(|v| {
504                if let gimli::AttributeValue::Udata(size) = v {
505                    Some(size)
506                } else {
507                    None
508                }
509            })
510            .or(Some(8)); // Default to 8 bytes
511
512        Ok(TypeInfo {
513            name,
514            size,
515            is_pointer: true,
516            is_const: false,
517            is_struct: false,
518            is_array: false,
519            array_size: None,
520            base_type,
521        })
522    }
523
524    /// Extract array type information
525    #[cfg(feature = "dwarf_support")]
526    fn extract_array_type_info(
527        &mut self,
528        dwarf: &Dwarf<gimli::EndianSlice<'static, gimli::LittleEndian>>,
529        unit: &gimli::Unit<gimli::EndianSlice<'static, gimli::LittleEndian>>,
530        entry: &gimli::DebuggingInformationEntry<gimli::EndianSlice<'static, gimli::LittleEndian>>,
531    ) -> Result<TypeInfo> {
532        // Get the element type
533        let base_type = if let Some(type_attr) = entry.attr_value(gimli::DW_AT_type)
534            .map_err(|e| CallTraceError::DwarfError(format!("Error reading array element type: {}", e)))?
535        {
536            if let gimli::AttributeValue::UnitRef(type_offset) = type_attr {
537                Some(Box::new(self.extract_type_info_from_offset(dwarf, unit, type_offset)?))
538            } else {
539                None
540            }
541        } else {
542            None
543        };
544
545        // Extract array dimensions by looking at subrange_type children
546        let mut array_size = None;
547        let mut children = entry.children();
548
549        while let Some(child) = children.next()
550            .map_err(|e| CallTraceError::DwarfError(format!("Error reading array child: {}", e)))?
551        {
552            if child.tag() == gimli::DW_TAG_subrange_type {
553                // Try to get the count or upper bound
554                if let Some(count_attr) = child.attr_value(gimli::DW_AT_count)
555                    .map_err(|e| CallTraceError::DwarfError(format!("Error reading array count: {}", e)))?
556                {
557                    if let gimli::AttributeValue::Udata(count) = count_attr {
558                        array_size = Some(count);
559                        break;
560                    }
561                } else if let Some(upper_attr) = child.attr_value(gimli::DW_AT_upper_bound)
562                    .map_err(|e| CallTraceError::DwarfError(format!("Error reading array upper bound: {}", e)))?
563                {
564                    if let gimli::AttributeValue::Udata(upper) = upper_attr {
565                        // Upper bound is typically count - 1 for zero-based arrays
566                        array_size = Some(upper + 1);
567                        break;
568                    }
569                }
570            }
571        }
572
573        // Construct array type name
574        let name = if let Some(ref base) = base_type {
575            if let Some(size) = array_size {
576                format!("{}[{}]", base.name, size)
577            } else {
578                format!("{}[]", base.name)
579            }
580        } else {
581            "unknown_array".to_string()
582        };
583
584        // Calculate total size
585        let total_size = if let (Some(ref base), Some(count)) = (&base_type, array_size) {
586            base.size.map(|element_size| element_size * count)
587        } else {
588            None
589        };
590
591        Ok(TypeInfo {
592            name,
593            size: total_size,
594            is_pointer: false,
595            is_const: false,
596            is_struct: false,
597            is_array: true,
598            array_size,
599            base_type,
600        })
601    }
602
603    /// Extract structure/class type information
604    #[cfg(feature = "dwarf_support")]
605    fn extract_struct_type_info(
606        &self,
607        dwarf: &Dwarf<gimli::EndianSlice<'static, gimli::LittleEndian>>,
608        _unit: &gimli::Unit<gimli::EndianSlice<'static, gimli::LittleEndian>>,
609        entry: &gimli::DebuggingInformationEntry<gimli::EndianSlice<'static, gimli::LittleEndian>>,
610    ) -> Result<TypeInfo> {
611        // Extract struct name
612        let name = if let Some(name_attr) = entry.attr_value(gimli::DW_AT_name)
613            .map_err(|e| CallTraceError::DwarfError(format!("Error reading struct name: {}", e)))?
614        {
615            if let gimli::AttributeValue::DebugStrRef(offset) = name_attr {
616                dwarf.debug_str.get_str(offset)
617                    .map_err(|e| CallTraceError::DwarfError(format!("Error reading struct string: {}", e)))?
618                    .to_string_lossy().to_string()
619            } else {
620                "anonymous_struct".to_string()
621            }
622        } else {
623            "anonymous_struct".to_string()
624        };
625
626        // Extract size
627        let size = entry.attr_value(gimli::DW_AT_byte_size)
628            .map_err(|e| CallTraceError::DwarfError(format!("Error reading struct size: {}", e)))?
629            .and_then(|v| {
630                if let gimli::AttributeValue::Udata(size) = v {
631                    Some(size)
632                } else {
633                    None
634                }
635            });
636
637        // Note: In a complete implementation, we would also extract member information
638        // This would involve iterating through DW_TAG_member children
639
640        Ok(TypeInfo {
641            name,
642            size,
643            is_pointer: false,
644            is_const: false,
645            is_struct: true,
646            is_array: false,
647            array_size: None,
648            base_type: None,
649        })
650    }
651
652    /// Extract const type information
653    #[cfg(feature = "dwarf_support")]
654    fn extract_const_type_info(
655        &mut self,
656        dwarf: &Dwarf<gimli::EndianSlice<'static, gimli::LittleEndian>>,
657        unit: &gimli::Unit<gimli::EndianSlice<'static, gimli::LittleEndian>>,
658        entry: &gimli::DebuggingInformationEntry<gimli::EndianSlice<'static, gimli::LittleEndian>>,
659    ) -> Result<TypeInfo> {
660        // Get the underlying type
661        let mut base_type_info = if let Some(type_attr) = entry.attr_value(gimli::DW_AT_type)
662            .map_err(|e| CallTraceError::DwarfError(format!("Error reading const target type: {}", e)))?
663        {
664            if let gimli::AttributeValue::UnitRef(type_offset) = type_attr {
665                self.extract_type_info_from_offset(dwarf, unit, type_offset)?
666            } else {
667                TypeInfo {
668                    name: "unknown".to_string(),
669                    size: None,
670                    is_pointer: false,
671                    is_const: false,
672                    is_struct: false,
673                    is_array: false,
674                    array_size: None,
675                    base_type: None,
676                }
677            }
678        } else {
679            TypeInfo {
680                name: "unknown".to_string(),
681                size: None,
682                is_pointer: false,
683                is_const: false,
684                is_struct: false,
685                is_array: false,
686                array_size: None,
687                base_type: None,
688            }
689        };
690
691        // Mark as const and update name
692        base_type_info.is_const = true;
693        base_type_info.name = format!("const {}", base_type_info.name);
694
695        Ok(base_type_info)
696    }
697
698    /// Extract typedef information
699    #[cfg(feature = "dwarf_support")]
700    fn extract_typedef_info(
701        &mut self,
702        dwarf: &Dwarf<gimli::EndianSlice<'static, gimli::LittleEndian>>,
703        unit: &gimli::Unit<gimli::EndianSlice<'static, gimli::LittleEndian>>,
704        entry: &gimli::DebuggingInformationEntry<gimli::EndianSlice<'static, gimli::LittleEndian>>,
705    ) -> Result<TypeInfo> {
706        // Extract typedef name
707        let typedef_name = if let Some(name_attr) = entry.attr_value(gimli::DW_AT_name)
708            .map_err(|e| CallTraceError::DwarfError(format!("Error reading typedef name: {}", e)))?
709        {
710            if let gimli::AttributeValue::DebugStrRef(offset) = name_attr {
711                dwarf.debug_str.get_str(offset)
712                    .map_err(|e| CallTraceError::DwarfError(format!("Error reading typedef string: {}", e)))?
713                    .to_string_lossy().to_string()
714            } else {
715                "unknown_typedef".to_string()
716            }
717        } else {
718            "unknown_typedef".to_string()
719        };
720
721        // Get the underlying type
722        let mut base_type_info = if let Some(type_attr) = entry.attr_value(gimli::DW_AT_type)
723            .map_err(|e| CallTraceError::DwarfError(format!("Error reading typedef target type: {}", e)))?
724        {
725            if let gimli::AttributeValue::UnitRef(type_offset) = type_attr {
726                self.extract_type_info_from_offset(dwarf, unit, type_offset)?
727            } else {
728                TypeInfo {
729                    name: "unknown".to_string(),
730                    size: None,
731                    is_pointer: false,
732                    is_const: false,
733                    is_struct: false,
734                    is_array: false,
735                    array_size: None,
736                    base_type: None,
737                }
738            }
739        } else {
740            TypeInfo {
741                name: "unknown".to_string(),
742                size: None,
743                is_pointer: false,
744                is_const: false,
745                is_struct: false,
746                is_array: false,
747                array_size: None,
748                base_type: None,
749            }
750        };
751
752        // Use the typedef name but preserve other type information
753        base_type_info.name = typedef_name;
754
755        Ok(base_type_info)
756    }
757    */
758
759    /// Get program base address from /proc/self/maps
760    fn get_program_base_address(&self) -> Option<u64> {
761        use std::fs;
762
763        let maps = fs::read_to_string("/proc/self/maps").ok()?;
764        for line in maps.lines() {
765            if line.contains(&self.executable_path) && line.contains("r-xp") {
766                let parts: Vec<&str> = line.split('-').collect();
767                if let Some(base_str) = parts.first() {
768                    return u64::from_str_radix(base_str, 16).ok();
769                }
770            }
771        }
772        None
773    }
774
775    /// Create fallback function info using dladdr
776    fn create_fallback_function_info(&self, address: u64) -> Result<FunctionInfo> {
777        use libc::{dladdr, Dl_info};
778        use std::ffi::CStr;
779
780        let mut info: Dl_info = unsafe { std::mem::zeroed() };
781        let result = unsafe { dladdr(address as *const c_void, &mut info) };
782
783        let name = if result != 0 && !info.dli_sname.is_null() {
784            let raw_name = unsafe {
785                CStr::from_ptr(info.dli_sname)
786                    .to_string_lossy()
787                    .into_owned()
788            };
789
790            // Try to demangle C++ function names
791            demangle_function_name(&raw_name)
792        } else {
793            format!("0x{:x}", address)
794        };
795
796        Ok(FunctionInfo {
797            name,
798            address,
799            size: None,
800            parameters: Vec::new(), // No parameter info available
801            return_type: None,
802            source_file: None,
803            line_number: None,
804        })
805    }
806
807    /// Get the number of cached functions
808    pub fn cache_size(&self) -> usize {
809        self.function_cache.len()
810    }
811
812    /// Clear the function cache
813    pub fn clear_cache(&mut self) {
814        self.function_cache.clear();
815        self.type_cache.clear();
816        self.symbol_cache.clear();
817    }
818}
819
820impl Drop for DwarfAnalyzer {
821    fn drop(&mut self) {
822        // Clean up any resources
823        self.clear_cache();
824    }
825}
826
827/// Helper function to demangle C++ function names
828pub fn demangle_function_name(mangled: &str) -> String {
829    // Try multiple demangling approaches
830
831    // First try: Use cxa_demangle from libc++abi
832    if let Some(demangled) = try_cxa_demangle(mangled) {
833        return demangled;
834    }
835
836    // Second try: Use external c++filt tool
837    if let Some(demangled) = try_cppfilt_demangle(mangled) {
838        return demangled;
839    }
840
841    // Fallback: Return original mangled name
842    mangled.to_string()
843}
844
845/// Try to demangle using libc++abi's cxa_demangle function
846fn try_cxa_demangle(mangled: &str) -> Option<String> {
847    use std::ffi::{CStr, CString};
848    use std::ptr;
849
850    // Only process names that look like C++ mangled names
851    if !mangled.starts_with("_Z") && !mangled.starts_with("__Z") {
852        return None;
853    }
854
855    let c_mangled = CString::new(mangled).ok()?;
856    let mut status: libc::c_int = 0;
857
858    unsafe {
859        // Try to get __cxa_demangle dynamically to avoid linking issues
860        let cxa_demangle_fn = libc::dlsym(libc::RTLD_DEFAULT, c"__cxa_demangle".as_ptr());
861        if cxa_demangle_fn.is_null() {
862            // __cxa_demangle not available (e.g., in C programs without libstdc++)
863            return None;
864        }
865
866        // Cast to the correct function pointer type
867        type CxaDemangleFn = extern "C" fn(
868            *const libc::c_char,
869            *mut libc::c_char,
870            *mut libc::size_t,
871            *mut libc::c_int,
872        ) -> *mut libc::c_char;
873
874        let demangle_fn: CxaDemangleFn = std::mem::transmute(cxa_demangle_fn);
875
876        let result = demangle_fn(
877            c_mangled.as_ptr(),
878            ptr::null_mut(),
879            ptr::null_mut(),
880            &mut status,
881        );
882
883        if status == 0 && !result.is_null() {
884            let demangled_cstr = CStr::from_ptr(result);
885            let demangled = demangled_cstr.to_string_lossy().into_owned();
886
887            // Free the memory allocated by __cxa_demangle
888            libc::free(result as *mut libc::c_void);
889
890            Some(demangled)
891        } else {
892            None
893        }
894    }
895}
896
897/// Try to demangle using external c++filt tool
898fn try_cppfilt_demangle(mangled: &str) -> Option<String> {
899    use std::process::Command;
900
901    // Only process names that look like C++ mangled names
902    if !mangled.starts_with("_Z") && !mangled.starts_with("__Z") {
903        return None;
904    }
905
906    // Try to run c++filt command
907    let output = Command::new("c++filt").arg(mangled).output().ok()?;
908
909    if output.status.success() {
910        let demangled = String::from_utf8(output.stdout).ok()?;
911        let trimmed = demangled.trim();
912
913        // If c++filt actually demangled something, it should be different from input
914        if trimmed != mangled && !trimmed.is_empty() {
915            Some(trimmed.to_string())
916        } else {
917            None
918        }
919    } else {
920        None
921    }
922}
923
924/*
925/// Extract type information from a DWARF DIE
926#[cfg(feature = "dwarf_support")]
927#[allow(dead_code)]
928fn extract_type_info<R: Reader>(
929    _dwarf: &Dwarf<R>,
930    _unit: &gimli::Unit<R>,
931    _entry: &DebuggingInformationEntry<R>,
932) -> Result<TypeInfo> {
933    // This would extract detailed type information from DWARF
934    // Including struct layouts, array sizes, pointer levels, etc.
935
936    Ok(TypeInfo {
937        name: "unknown".to_string(),
938        size: None,
939        is_pointer: false,
940        is_const: false,
941        is_struct: false,
942        is_array: false,
943        array_size: None,
944        base_type: None,
945    })
946}
947*/