#![allow(warnings, unused)]
use std::collections::HashMap;
use std::path::PathBuf;
use std::time::{Duration, Instant};
pub struct PlatformSymbolResolver {
config: ResolverConfig,
symbol_cache: HashMap<usize, CachedSymbol>,
module_cache: HashMap<usize, ModuleInfo>,
stats: ResolverStats,
platform_context: ResolverContext,
}
#[derive(Debug, Clone)]
pub struct ResolverConfig {
pub enable_caching: bool,
pub max_cache_size: usize,
pub symbol_search_paths: Vec<PathBuf>,
pub enable_demangling: bool,
pub max_resolve_time: Duration,
pub eager_loading: bool,
}
#[derive(Debug)]
struct ResolverContext {
initialized: bool,
#[cfg(target_os = "linux")]
linux_context: LinuxResolverContext,
#[cfg(target_os = "windows")]
windows_context: WindowsResolverContext,
#[cfg(target_os = "macos")]
macos_context: MacOSResolverContext,
}
#[cfg(target_os = "linux")]
#[derive(Debug)]
struct LinuxResolverContext {
addr2line_available: bool,
dwarf_loaded: bool,
}
#[cfg(target_os = "windows")]
#[derive(Debug)]
struct WindowsResolverContext {
symbols_initialized: bool,
pdb_loaded: bool,
symbol_paths: Vec<PathBuf>,
}
#[cfg(target_os = "macos")]
#[derive(Debug)]
struct MacOSResolverContext {
atos_available: bool,
dsym_loaded: bool,
}
#[derive(Debug, Clone)]
struct CachedSymbol {
symbol: SymbolInfo,
access_count: usize,
}
#[derive(Debug, Clone)]
pub struct SymbolInfo {
pub name: String,
pub demangled_name: Option<String>,
pub file_path: Option<PathBuf>,
pub line_number: Option<u32>,
pub column_number: Option<u32>,
pub function_start: Option<usize>,
pub function_size: Option<usize>,
pub module_name: Option<String>,
pub compilation_unit: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ModuleInfo {
pub name: String,
pub base_address: usize,
pub size: usize,
pub file_path: PathBuf,
pub has_symbols: bool,
pub symbol_file: Option<PathBuf>,
}
#[derive(Debug)]
struct ResolverStats {
total_resolutions: std::sync::atomic::AtomicUsize,
successful_resolutions: std::sync::atomic::AtomicUsize,
cache_hits: std::sync::atomic::AtomicUsize,
total_resolve_time: std::sync::atomic::AtomicU64,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ResolveError {
UnsupportedPlatform,
SymbolNotFound,
NoDebugInfo,
FileAccessError(String),
ParseError(String),
Timeout,
MemoryError,
Unknown(String),
}
impl PlatformSymbolResolver {
pub fn new() -> Self {
Self {
config: ResolverConfig::default(),
symbol_cache: HashMap::new(),
module_cache: HashMap::new(),
stats: ResolverStats::new(),
platform_context: ResolverContext::new(),
}
}
pub fn initialize(&mut self) -> Result<(), ResolveError> {
#[cfg(target_os = "linux")]
{
self.initialize_linux()
}
#[cfg(target_os = "windows")]
{
self.initialize_windows()
}
#[cfg(target_os = "macos")]
{
self.initialize_macos()
}
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
{
Err(ResolveError::UnsupportedPlatform)
}
}
pub fn resolve_symbol(&mut self, address: usize) -> Result<SymbolInfo, ResolveError> {
let start_time = Instant::now();
if self.config.enable_caching {
if let Some(cached) = self.symbol_cache.get_mut(&address) {
cached.access_count += 1;
self.stats
.cache_hits
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
return Ok(cached.symbol.clone());
}
}
self.stats
.total_resolutions
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let result = self.perform_resolution(address);
let resolve_time = start_time.elapsed();
self.stats.total_resolve_time.fetch_add(
resolve_time.as_nanos() as u64,
std::sync::atomic::Ordering::Relaxed,
);
if let Ok(symbol) = &result {
self.stats
.successful_resolutions
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if self.config.enable_caching && self.symbol_cache.len() < self.config.max_cache_size {
self.symbol_cache.insert(
address,
CachedSymbol {
symbol: symbol.clone(),
access_count: 1,
},
);
}
}
result
}
pub fn resolve_batch(&mut self, addresses: &[usize]) -> Vec<Result<SymbolInfo, ResolveError>> {
addresses
.iter()
.map(|&addr| self.resolve_symbol(addr))
.collect()
}
pub fn get_module_info(&mut self, address: usize) -> Option<ModuleInfo> {
for (base_addr, module) in &self.module_cache {
if address >= *base_addr && address < (*base_addr + module.size) {
return Some(module.clone());
}
}
self.load_module_info(address)
}
pub fn get_statistics(&self) -> ResolverStatistics {
let total = self
.stats
.total_resolutions
.load(std::sync::atomic::Ordering::Relaxed);
let successful = self
.stats
.successful_resolutions
.load(std::sync::atomic::Ordering::Relaxed);
let cache_hits = self
.stats
.cache_hits
.load(std::sync::atomic::Ordering::Relaxed);
let total_time_ns = self
.stats
.total_resolve_time
.load(std::sync::atomic::Ordering::Relaxed);
ResolverStatistics {
total_resolutions: total,
successful_resolutions: successful,
failed_resolutions: total.saturating_sub(successful),
cache_hits,
cache_misses: total.saturating_sub(cache_hits),
cache_hit_rate: if total > 0 {
cache_hits as f64 / total as f64
} else {
0.0
},
success_rate: if total > 0 {
successful as f64 / total as f64
} else {
0.0
},
average_resolve_time: if total > 0 {
Duration::from_nanos(total_time_ns / total as u64)
} else {
Duration::ZERO
},
current_cache_size: self.symbol_cache.len(),
}
}
pub fn clear_cache(&mut self) {
self.symbol_cache.clear();
}
pub fn update_config(&mut self, config: ResolverConfig) {
self.config = config;
}
fn perform_resolution(&self, address: usize) -> Result<SymbolInfo, ResolveError> {
if !self.platform_context.initialized {
return Err(ResolveError::NoDebugInfo);
}
#[cfg(target_os = "linux")]
return self.resolve_linux_symbol(address);
#[cfg(target_os = "windows")]
return self.resolve_windows_symbol(address);
#[cfg(target_os = "macos")]
return self.resolve_macos_symbol(address);
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
Err(ResolveError::UnsupportedPlatform)
}
#[cfg(target_os = "linux")]
fn initialize_linux(&mut self) -> Result<(), ResolveError> {
self.platform_context.linux_context.addr2line_available = true; self.platform_context.linux_context.dwarf_loaded = true; self.platform_context.initialized = true;
Ok(())
}
#[cfg(target_os = "windows")]
fn initialize_windows(&mut self) -> Result<(), ResolveError> {
self.platform_context.windows_context.symbols_initialized = true; self.platform_context.windows_context.pdb_loaded = true; self.platform_context.initialized = true;
Ok(())
}
#[cfg(target_os = "macos")]
fn initialize_macos(&mut self) -> Result<(), ResolveError> {
self.platform_context.macos_context.atos_available = true; self.platform_context.macos_context.dsym_loaded = true; self.platform_context.initialized = true;
Ok(())
}
#[cfg(target_os = "linux")]
fn resolve_linux_symbol(&self, address: usize) -> Result<SymbolInfo, ResolveError> {
use std::ffi::CStr;
let addr = address as *const libc::c_void;
unsafe {
let mut info: libc::Dl_info = std::mem::zeroed();
if libc::dladdr(addr, &mut info) == 0 {
return Err(ResolveError::SymbolNotFound);
}
let name = if !info.dli_sname.is_null() {
CStr::from_ptr(info.dli_sname).to_string_lossy().to_string()
} else {
format!("unknown_0x{:x}", address)
};
let file_path = if !info.dli_fname.is_null() {
Some(PathBuf::from(
CStr::from_ptr(info.dli_fname).to_string_lossy().to_string(),
))
} else {
None
};
let base_address = info.dli_fbase as usize;
let _offset = address.saturating_sub(base_address);
let module_name = file_path
.as_ref()
.and_then(|p| p.file_name().map(|n| n.to_string_lossy().to_string()));
Ok(SymbolInfo {
name: name.clone(),
demangled_name: Some(name), file_path,
line_number: None, column_number: None,
function_start: Some(base_address),
function_size: None, module_name,
compilation_unit: None, })
}
}
#[cfg(target_os = "windows")]
fn resolve_windows_symbol(&self, address: usize) -> Result<SymbolInfo, ResolveError> {
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::System::Diagnostics::Debug::{
SymCleanup, SymFromAddrW, SymGetModuleBase64, SymInitializeW, SymSetContext,
SYMBOL_INFO,
};
use windows_sys::Win32::System::ProcessStatus::EnumProcessModules;
let process: windows_sys::Win32::Foundation::HANDLE = -1isize as _;
unsafe {
if SymInitializeW(process, std::ptr::null(), 1) == 0 {
return Err(ResolveError::Unknown(format!(
"SymInitializeW failed: {}",
GetLastError()
)));
}
let mut symbol_info: SYMBOL_INFO = std::mem::zeroed();
symbol_info.SizeOfStruct = std::mem::size_of::<SYMBOL_INFO>() as u32;
symbol_info.MaxNameLen = 256;
let mut displacement = 0u64;
let result = if SymFromAddrW(
process,
address as u64,
&mut displacement,
&mut symbol_info as *mut SYMBOL_INFO as *mut _,
) == 0
{
Err(ResolveError::SymbolNotFound)
} else {
let name = if symbol_info.Name[0] != 0 {
let name_len = symbol_info.NameLen as usize;
let name_bytes = &symbol_info.Name[..name_len.min(symbol_info.Name.len())];
let name_u16: Vec<u16> = name_bytes.iter().map(|&b| b as u16).collect();
String::from_utf16_lossy(&name_u16)
} else {
format!("unknown_0x{:x}", address)
};
let module_base = SymGetModuleBase64(process, address as u64);
Ok(SymbolInfo {
name: name.clone(),
demangled_name: Some(name),
file_path: None,
line_number: None,
column_number: None,
function_start: Some(symbol_info.Address as usize),
function_size: None,
module_name: if module_base != 0 {
Some(format!("module_0x{:x}", module_base))
} else {
None
},
compilation_unit: None,
})
};
SymCleanup(process);
result
}
}
#[cfg(target_os = "macos")]
fn resolve_macos_symbol(&self, address: usize) -> Result<SymbolInfo, ResolveError> {
use std::ffi::CStr;
let addr = address as *const libc::c_void;
unsafe {
let mut info: libc::Dl_info = std::mem::zeroed();
if libc::dladdr(addr, &mut info) == 0 {
return Err(ResolveError::SymbolNotFound);
}
let name = if !info.dli_sname.is_null() {
CStr::from_ptr(info.dli_sname).to_string_lossy().to_string()
} else {
format!("unknown_0x{:x}", address)
};
let file_path = if !info.dli_fname.is_null() {
Some(PathBuf::from(
CStr::from_ptr(info.dli_fname).to_string_lossy().to_string(),
))
} else {
None
};
let base_address = info.dli_fbase as usize;
let _offset = address.saturating_sub(base_address);
let module_name = file_path
.as_ref()
.and_then(|p| p.file_name().map(|n| n.to_string_lossy().to_string()));
Ok(SymbolInfo {
name: name.clone(),
demangled_name: Some(name), file_path: file_path.clone(),
line_number: None, column_number: None,
function_start: Some(base_address),
function_size: None, module_name,
compilation_unit: None, })
}
}
fn load_module_info(&mut self, _address: usize) -> Option<ModuleInfo> {
None
}
}
impl ResolverContext {
fn new() -> Self {
Self {
initialized: false,
#[cfg(target_os = "linux")]
linux_context: LinuxResolverContext {
addr2line_available: false,
dwarf_loaded: false,
},
#[cfg(target_os = "windows")]
windows_context: WindowsResolverContext {
symbols_initialized: false,
pdb_loaded: false,
symbol_paths: Vec::new(),
},
#[cfg(target_os = "macos")]
macos_context: MacOSResolverContext {
atos_available: false,
dsym_loaded: false,
},
}
}
}
impl ResolverStats {
fn new() -> Self {
Self {
total_resolutions: std::sync::atomic::AtomicUsize::new(0),
successful_resolutions: std::sync::atomic::AtomicUsize::new(0),
cache_hits: std::sync::atomic::AtomicUsize::new(0),
total_resolve_time: std::sync::atomic::AtomicU64::new(0),
}
}
}
#[derive(Debug, Clone)]
pub struct ResolverStatistics {
pub total_resolutions: usize,
pub successful_resolutions: usize,
pub failed_resolutions: usize,
pub cache_hits: usize,
pub cache_misses: usize,
pub cache_hit_rate: f64,
pub success_rate: f64,
pub average_resolve_time: Duration,
pub current_cache_size: usize,
}
impl Default for ResolverConfig {
fn default() -> Self {
Self {
enable_caching: true,
max_cache_size: 10000,
symbol_search_paths: Vec::new(),
enable_demangling: true,
max_resolve_time: Duration::from_millis(100),
eager_loading: false,
}
}
}
impl Default for PlatformSymbolResolver {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for ResolveError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ResolveError::UnsupportedPlatform => {
write!(f, "Platform not supported for symbol resolution")
}
ResolveError::SymbolNotFound => write!(f, "Symbol not found"),
ResolveError::NoDebugInfo => write!(f, "Debug information not available"),
ResolveError::FileAccessError(msg) => write!(f, "File access error: {}", msg),
ResolveError::ParseError(msg) => write!(f, "Parse error: {}", msg),
ResolveError::Timeout => write!(f, "Symbol resolution timed out"),
ResolveError::MemoryError => write!(f, "Memory error during symbol resolution"),
ResolveError::Unknown(msg) => write!(f, "Unknown error: {}", msg),
}
}
}
impl std::error::Error for ResolveError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_symbol_resolver_creation() {
let resolver = PlatformSymbolResolver::new();
assert!(!resolver.platform_context.initialized);
assert!(resolver.symbol_cache.is_empty());
let stats = resolver.get_statistics();
assert_eq!(stats.total_resolutions, 0);
assert_eq!(stats.cache_hit_rate, 0.0);
}
#[test]
fn test_resolver_initialization() {
let mut resolver = PlatformSymbolResolver::new();
let result = resolver.initialize();
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
assert!(result.is_ok());
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
assert_eq!(result, Err(ResolveError::UnsupportedPlatform));
}
#[test]
fn test_symbol_resolution() {
let mut resolver = PlatformSymbolResolver::new();
let _ = resolver.initialize();
let address = test_symbol_resolution as *const () as usize;
let result = resolver.resolve_symbol(address);
#[cfg(target_os = "linux")]
{
if resolver.platform_context.initialized {
match result {
Ok(symbol) => {
assert!(!symbol.name.is_empty());
}
Err(_) => {
}
}
}
}
}
#[test]
fn test_symbol_caching() {
let mut resolver = PlatformSymbolResolver::new();
let _ = resolver.initialize();
let address = test_symbol_caching as *const () as usize;
let _ = resolver.resolve_symbol(address);
let stats1 = resolver.get_statistics();
let _ = resolver.resolve_symbol(address);
let stats2 = resolver.get_statistics();
#[cfg(target_os = "linux")]
{
if resolver.platform_context.initialized {
assert!(stats2.cache_hits >= stats1.cache_hits);
}
}
}
#[test]
fn test_batch_resolution() {
let mut resolver = PlatformSymbolResolver::new();
let _ = resolver.initialize();
let addresses = vec![
test_batch_resolution as *const () as usize,
test_symbol_caching as *const () as usize,
test_module_info as *const () as usize,
];
let results = resolver.resolve_batch(&addresses);
assert_eq!(results.len(), 3);
#[cfg(target_os = "linux")]
{
if resolver.platform_context.initialized {
let success_count = results.iter().filter(|r| r.is_ok()).count();
assert!(success_count > 0, "At least one symbol should resolve");
}
}
}
#[test]
fn test_module_info() {
let mut resolver = PlatformSymbolResolver::new();
let _ = resolver.initialize();
let address = test_module_info as *const () as usize;
let module = resolver.get_module_info(address);
#[cfg(target_os = "linux")]
{
if resolver.platform_context.initialized {
if let Some(module) = module {
assert!(!module.name.is_empty());
assert!(module.size > 0);
}
}
}
}
}