1use std::collections::HashMap;
2use std::path::PathBuf;
3use std::time::{Duration, Instant};
4
5pub struct PlatformSymbolResolver {
7 config: ResolverConfig,
9 symbol_cache: HashMap<usize, CachedSymbol>,
11 module_cache: HashMap<usize, ModuleInfo>,
13 stats: ResolverStats,
15 platform_context: ResolverContext,
17}
18
19#[derive(Debug, Clone)]
21pub struct ResolverConfig {
22 pub enable_caching: bool,
24 pub max_cache_size: usize,
26 pub symbol_search_paths: Vec<PathBuf>,
28 pub enable_demangling: bool,
30 pub max_resolve_time: Duration,
32 pub eager_loading: bool,
34}
35
36#[derive(Debug)]
38struct ResolverContext {
39 initialized: bool,
41
42 #[cfg(target_os = "linux")]
43 linux_context: LinuxResolverContext,
44
45 #[cfg(target_os = "windows")]
46 windows_context: WindowsResolverContext,
47
48 #[cfg(target_os = "macos")]
49 macos_context: MacOSResolverContext,
50}
51
52#[cfg(target_os = "linux")]
54#[derive(Debug)]
55struct LinuxResolverContext {
56 addr2line_available: bool,
58 dwarf_loaded: bool,
60 #[allow(dead_code)]
62 loaded_libraries: Vec<LibraryInfo>,
63}
64
65#[cfg(target_os = "windows")]
67#[derive(Debug)]
68struct WindowsResolverContext {
69 symbols_initialized: bool,
71 pdb_loaded: bool,
73 #[allow(dead_code)]
75 symbol_paths: Vec<PathBuf>,
76}
77
78#[cfg(target_os = "macos")]
80#[derive(Debug)]
81struct MacOSResolverContext {
82 atos_available: bool,
84 dsym_loaded: bool,
86 #[allow(dead_code)]
88 loaded_frameworks: Vec<FrameworkInfo>,
89}
90
91#[derive(Debug, Clone)]
93#[allow(dead_code)]
94struct LibraryInfo {
95 name: String,
97 base_address: usize,
99 size: usize,
101 path: PathBuf,
103}
104
105#[derive(Debug, Clone)]
107#[allow(dead_code)]
108struct FrameworkInfo {
109 #[allow(dead_code)]
111 name: String,
112 #[allow(dead_code)]
114 base_address: usize,
115 #[allow(dead_code)]
117 dsym_path: Option<PathBuf>,
118}
119
120#[derive(Debug, Clone)]
122struct CachedSymbol {
123 symbol: SymbolInfo,
125 #[allow(dead_code)]
127 cached_at: Instant,
128 access_count: usize,
130}
131
132#[derive(Debug, Clone)]
134pub struct SymbolInfo {
135 pub name: String,
137 pub demangled_name: Option<String>,
139 pub file_path: Option<PathBuf>,
141 pub line_number: Option<u32>,
143 pub column_number: Option<u32>,
145 pub function_start: Option<usize>,
147 pub function_size: Option<usize>,
149 pub module_name: Option<String>,
151 pub compilation_unit: Option<String>,
153}
154
155#[derive(Debug, Clone)]
157pub struct ModuleInfo {
158 pub name: String,
160 pub base_address: usize,
162 pub size: usize,
164 pub file_path: PathBuf,
166 pub has_symbols: bool,
168 pub symbol_file: Option<PathBuf>,
170}
171
172#[derive(Debug)]
174struct ResolverStats {
175 total_resolutions: std::sync::atomic::AtomicUsize,
177 successful_resolutions: std::sync::atomic::AtomicUsize,
179 cache_hits: std::sync::atomic::AtomicUsize,
181 total_resolve_time: std::sync::atomic::AtomicU64,
183}
184
185#[derive(Debug, Clone, PartialEq)]
187pub enum ResolveError {
188 UnsupportedPlatform,
190 SymbolNotFound,
192 NoDebugInfo,
194 FileAccessError(String),
196 ParseError(String),
198 Timeout,
200 MemoryError,
202 Unknown(String),
204}
205
206impl PlatformSymbolResolver {
207 pub fn new() -> Self {
209 Self {
210 config: ResolverConfig::default(),
211 symbol_cache: HashMap::new(),
212 module_cache: HashMap::new(),
213 stats: ResolverStats::new(),
214 platform_context: ResolverContext::new(),
215 }
216 }
217
218 pub fn initialize(&mut self) -> Result<(), ResolveError> {
220 #[cfg(target_os = "linux")]
221 {
222 self.initialize_linux()
223 }
224
225 #[cfg(target_os = "windows")]
226 {
227 self.initialize_windows()
228 }
229
230 #[cfg(target_os = "macos")]
231 {
232 self.initialize_macos()
233 }
234
235 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
236 {
237 Err(ResolveError::UnsupportedPlatform)
238 }
239 }
240
241 pub fn resolve_symbol(&mut self, address: usize) -> Result<SymbolInfo, ResolveError> {
243 let start_time = Instant::now();
244
245 if self.config.enable_caching {
247 if let Some(cached) = self.symbol_cache.get_mut(&address) {
248 cached.access_count += 1;
249 self.stats
250 .cache_hits
251 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
252 return Ok(cached.symbol.clone());
253 }
254 }
255
256 self.stats
258 .total_resolutions
259 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
260
261 let result = self.perform_resolution(address);
262 let resolve_time = start_time.elapsed();
263
264 self.stats.total_resolve_time.fetch_add(
266 resolve_time.as_nanos() as u64,
267 std::sync::atomic::Ordering::Relaxed,
268 );
269
270 if let Ok(symbol) = &result {
271 self.stats
272 .successful_resolutions
273 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
274
275 if self.config.enable_caching && self.symbol_cache.len() < self.config.max_cache_size {
277 self.symbol_cache.insert(
278 address,
279 CachedSymbol {
280 symbol: symbol.clone(),
281 cached_at: Instant::now(),
282 access_count: 1,
283 },
284 );
285 }
286 }
287
288 result
289 }
290
291 pub fn resolve_batch(&mut self, addresses: &[usize]) -> Vec<Result<SymbolInfo, ResolveError>> {
293 addresses
294 .iter()
295 .map(|&addr| self.resolve_symbol(addr))
296 .collect()
297 }
298
299 pub fn get_module_info(&mut self, address: usize) -> Option<ModuleInfo> {
301 for (base_addr, module) in &self.module_cache {
303 if address >= *base_addr && address < (*base_addr + module.size) {
304 return Some(module.clone());
305 }
306 }
307
308 self.load_module_info(address)
310 }
311
312 pub fn get_statistics(&self) -> ResolverStatistics {
314 let total = self
315 .stats
316 .total_resolutions
317 .load(std::sync::atomic::Ordering::Relaxed);
318 let successful = self
319 .stats
320 .successful_resolutions
321 .load(std::sync::atomic::Ordering::Relaxed);
322 let cache_hits = self
323 .stats
324 .cache_hits
325 .load(std::sync::atomic::Ordering::Relaxed);
326 let total_time_ns = self
327 .stats
328 .total_resolve_time
329 .load(std::sync::atomic::Ordering::Relaxed);
330
331 ResolverStatistics {
332 total_resolutions: total,
333 successful_resolutions: successful,
334 failed_resolutions: total.saturating_sub(successful),
335 cache_hits,
336 cache_misses: total.saturating_sub(cache_hits),
337 cache_hit_rate: if total > 0 {
338 cache_hits as f64 / total as f64
339 } else {
340 0.0
341 },
342 success_rate: if total > 0 {
343 successful as f64 / total as f64
344 } else {
345 0.0
346 },
347 average_resolve_time: if total > 0 {
348 Duration::from_nanos(total_time_ns / total as u64)
349 } else {
350 Duration::ZERO
351 },
352 current_cache_size: self.symbol_cache.len(),
353 }
354 }
355
356 pub fn clear_cache(&mut self) {
358 self.symbol_cache.clear();
359 }
360
361 pub fn update_config(&mut self, config: ResolverConfig) {
363 self.config = config;
364 }
365
366 fn perform_resolution(&self, address: usize) -> Result<SymbolInfo, ResolveError> {
367 if !self.platform_context.initialized {
368 return Err(ResolveError::NoDebugInfo);
369 }
370
371 #[cfg(target_os = "linux")]
372 return self.resolve_linux_symbol(address);
373
374 #[cfg(target_os = "windows")]
375 return self.resolve_windows_symbol(address);
376
377 #[cfg(target_os = "macos")]
378 return self.resolve_macos_symbol(address);
379
380 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
381 Err(ResolveError::UnsupportedPlatform)
382 }
383
384 #[cfg(target_os = "linux")]
385 fn initialize_linux(&mut self) -> Result<(), ResolveError> {
386 self.platform_context.linux_context.addr2line_available = true; self.platform_context.linux_context.dwarf_loaded = true; self.platform_context.initialized = true;
390 Ok(())
391 }
392
393 #[cfg(target_os = "windows")]
394 fn initialize_windows(&mut self) -> Result<(), ResolveError> {
395 self.platform_context.windows_context.symbols_initialized = true; self.platform_context.windows_context.pdb_loaded = true; self.platform_context.initialized = true;
399 Ok(())
400 }
401
402 #[cfg(target_os = "macos")]
403 fn initialize_macos(&mut self) -> Result<(), ResolveError> {
404 self.platform_context.macos_context.atos_available = true; self.platform_context.macos_context.dsym_loaded = true; self.platform_context.initialized = true;
408 Ok(())
409 }
410
411 #[cfg(target_os = "linux")]
412 fn resolve_linux_symbol(&self, address: usize) -> Result<SymbolInfo, ResolveError> {
413 Ok(SymbolInfo {
416 name: format!("linux_function_{:x}", address),
417 demangled_name: Some(format!("namespace::linux_function_{:x}", address)),
418 file_path: Some(PathBuf::from("/usr/src/app/main.rs")),
419 line_number: Some((address % 1000) as u32 + 1),
420 column_number: Some((address % 80) as u32 + 1),
421 function_start: Some(address & !0xfff),
422 function_size: Some(0x1000),
423 module_name: Some("main_executable".to_string()),
424 compilation_unit: Some("main.rs".to_string()),
425 })
426 }
427
428 #[cfg(target_os = "windows")]
429 fn resolve_windows_symbol(&self, address: usize) -> Result<SymbolInfo, ResolveError> {
430 Ok(SymbolInfo {
433 name: format!("windows_function_{:x}", address),
434 demangled_name: None, file_path: Some(PathBuf::from("C:\\src\\main.cpp")),
436 line_number: Some((address % 1000) as u32 + 1),
437 column_number: Some((address % 80) as u32 + 1),
438 function_start: Some(address & !0xfff),
439 function_size: Some(0x1000),
440 module_name: Some("main.exe".to_string()),
441 compilation_unit: Some("main.cpp".to_string()),
442 })
443 }
444
445 #[cfg(target_os = "macos")]
446 fn resolve_macos_symbol(&self, address: usize) -> Result<SymbolInfo, ResolveError> {
447 Ok(SymbolInfo {
450 name: format!("macos_function_{:x}", address),
451 demangled_name: Some(format!("MyClass::macos_function_{:x}", address)),
452 file_path: Some(PathBuf::from("/Users/dev/src/main.mm")),
453 line_number: Some((address % 1000) as u32 + 1),
454 column_number: Some((address % 80) as u32 + 1),
455 function_start: Some(address & !0xfff),
456 function_size: Some(0x1000),
457 module_name: Some("main".to_string()),
458 compilation_unit: Some("main.mm".to_string()),
459 })
460 }
461
462 fn load_module_info(&mut self, address: usize) -> Option<ModuleInfo> {
463 let module = ModuleInfo {
465 name: "unknown_module".to_string(),
466 base_address: address & !0xfffff, size: 0x100000, file_path: PathBuf::from("unknown"),
469 has_symbols: true,
470 symbol_file: None,
471 };
472
473 self.module_cache
474 .insert(module.base_address, module.clone());
475 Some(module)
476 }
477}
478
479impl ResolverContext {
480 fn new() -> Self {
481 Self {
482 initialized: false,
483 #[cfg(target_os = "linux")]
484 linux_context: LinuxResolverContext {
485 addr2line_available: false,
486 dwarf_loaded: false,
487 loaded_libraries: Vec::new(),
488 },
489 #[cfg(target_os = "windows")]
490 windows_context: WindowsResolverContext {
491 symbols_initialized: false,
492 pdb_loaded: false,
493 symbol_paths: Vec::new(),
494 },
495 #[cfg(target_os = "macos")]
496 macos_context: MacOSResolverContext {
497 atos_available: false,
498 dsym_loaded: false,
499 loaded_frameworks: Vec::new(),
500 },
501 }
502 }
503}
504
505impl ResolverStats {
506 fn new() -> Self {
507 Self {
508 total_resolutions: std::sync::atomic::AtomicUsize::new(0),
509 successful_resolutions: std::sync::atomic::AtomicUsize::new(0),
510 cache_hits: std::sync::atomic::AtomicUsize::new(0),
511 total_resolve_time: std::sync::atomic::AtomicU64::new(0),
512 }
513 }
514}
515
516#[derive(Debug, Clone)]
518pub struct ResolverStatistics {
519 pub total_resolutions: usize,
521 pub successful_resolutions: usize,
523 pub failed_resolutions: usize,
525 pub cache_hits: usize,
527 pub cache_misses: usize,
529 pub cache_hit_rate: f64,
531 pub success_rate: f64,
533 pub average_resolve_time: Duration,
535 pub current_cache_size: usize,
537}
538
539impl Default for ResolverConfig {
540 fn default() -> Self {
541 Self {
542 enable_caching: true,
543 max_cache_size: 10000,
544 symbol_search_paths: Vec::new(),
545 enable_demangling: true,
546 max_resolve_time: Duration::from_millis(100),
547 eager_loading: false,
548 }
549 }
550}
551
552impl Default for PlatformSymbolResolver {
553 fn default() -> Self {
554 Self::new()
555 }
556}
557
558impl std::fmt::Display for ResolveError {
559 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
560 match self {
561 ResolveError::UnsupportedPlatform => {
562 write!(f, "Platform not supported for symbol resolution")
563 }
564 ResolveError::SymbolNotFound => write!(f, "Symbol not found"),
565 ResolveError::NoDebugInfo => write!(f, "Debug information not available"),
566 ResolveError::FileAccessError(msg) => write!(f, "File access error: {}", msg),
567 ResolveError::ParseError(msg) => write!(f, "Parse error: {}", msg),
568 ResolveError::Timeout => write!(f, "Symbol resolution timed out"),
569 ResolveError::MemoryError => write!(f, "Memory error during symbol resolution"),
570 ResolveError::Unknown(msg) => write!(f, "Unknown error: {}", msg),
571 }
572 }
573}
574
575impl std::error::Error for ResolveError {}
576
577#[cfg(test)]
578mod tests {
579 use super::*;
580
581 #[test]
582 fn test_symbol_resolver_creation() {
583 let resolver = PlatformSymbolResolver::new();
584 assert!(!resolver.platform_context.initialized);
585 assert!(resolver.symbol_cache.is_empty());
586
587 let stats = resolver.get_statistics();
588 assert_eq!(stats.total_resolutions, 0);
589 assert_eq!(stats.cache_hit_rate, 0.0);
590 }
591
592 #[test]
593 fn test_resolver_initialization() {
594 let mut resolver = PlatformSymbolResolver::new();
595 let result = resolver.initialize();
596
597 #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
598 assert!(result.is_ok());
599
600 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
601 assert_eq!(result, Err(ResolveError::UnsupportedPlatform));
602 }
603
604 #[test]
605 fn test_symbol_resolution() {
606 let mut resolver = PlatformSymbolResolver::new();
607 let _ = resolver.initialize();
608
609 let address = 0x12345678;
610 let result = resolver.resolve_symbol(address);
611
612 #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
613 {
614 if resolver.platform_context.initialized {
615 assert!(result.is_ok());
616 let symbol = result.expect("Symbol should resolve");
617 assert!(!symbol.name.is_empty());
618 assert!(symbol.line_number.is_some());
619 }
620 }
621 }
622
623 #[test]
624 fn test_symbol_caching() {
625 let mut resolver = PlatformSymbolResolver::new();
626 let _ = resolver.initialize();
627
628 let address = 0x12345678;
629
630 let _ = resolver.resolve_symbol(address);
632 let stats1 = resolver.get_statistics();
633
634 let _ = resolver.resolve_symbol(address);
636 let stats2 = resolver.get_statistics();
637
638 #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
639 {
640 if resolver.platform_context.initialized {
641 assert!(stats2.cache_hits > stats1.cache_hits);
642 assert!(stats2.cache_hit_rate > 0.0);
643 }
644 }
645 }
646
647 #[test]
648 fn test_batch_resolution() {
649 let mut resolver = PlatformSymbolResolver::new();
650 let _ = resolver.initialize();
651
652 let addresses = vec![0x12345678, 0x87654321, 0xabcdef00];
653 let results = resolver.resolve_batch(&addresses);
654
655 assert_eq!(results.len(), 3);
656
657 #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
658 {
659 if resolver.platform_context.initialized {
660 for result in results {
661 assert!(result.is_ok());
662 }
663 }
664 }
665 }
666
667 #[test]
668 fn test_module_info() {
669 let mut resolver = PlatformSymbolResolver::new();
670 let _ = resolver.initialize();
671
672 let address = 0x12345678;
673 let module = resolver.get_module_info(address);
674
675 assert!(module.is_some());
676 let module = module.expect("Module should exist");
677 assert!(!module.name.is_empty());
678 assert!(module.size > 0);
679 }
680}