use std::collections::BTreeMap;
use std::collections::HashMap;
use std::collections::VecDeque;
use std::time::SystemTime;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct StackFrame {
pub function_name: String,
pub file: Option<String>,
pub line: Option<u32>,
pub column: Option<u32>,
pub execution_time: Duration,
pub memory_used: usize,
pub depth: usize,
pub timestamp: u64, }
#[derive(Debug, Clone)]
pub struct CallStackAnalysis {
pub hot_path: Vec<String>,
pub bottlenecks: Vec<Bottleneck>,
pub recursion: Vec<RecursionInfo>,
pub depth_stats: DepthStats,
}
#[derive(Debug, Clone)]
pub struct Bottleneck {
pub function: String,
pub bottleneck_type: BottleneckType,
pub description: String,
pub impact: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BottleneckType {
SlowExecution,
HighMemoryUsage,
HighCallFrequency,
DeepNesting,
}
#[derive(Debug, Clone)]
pub struct RecursionInfo {
pub function: String,
pub recursion_depth: usize,
pub recursion_count: usize,
pub is_direct_recursion: bool,
}
#[derive(Debug, Clone)]
pub struct DepthStats {
pub max_depth: usize,
pub avg_depth: f64,
pub depth_distribution: HashMap<usize, usize>,
}
#[derive(Debug)]
pub struct CallStackAnalyzer {
max_depth: usize,
current_stack: VecDeque<StackFrame>,
call_history: Vec<StackFrame>,
function_call_counts: HashMap<String, usize>,
recursion_tracker: RecursionTracker,
stats: AnalyzerStats,
}
#[derive(Debug)]
struct RecursionTracker {
current_recursion_stack: Vec<String>,
recursion_info: HashMap<String, RecursionInfo>,
}
#[derive(Debug, Clone, Default)]
pub struct AnalyzerStats {
pub total_calls: u64,
pub max_call_depth: usize,
pub bottlenecks_detected: u64,
pub recursion_detected: u64,
pub analysis_count: u64,
}
impl CallStackAnalyzer {
pub fn new(max_depth: usize) -> Self {
Self {
max_depth,
current_stack: VecDeque::new(),
call_history: Vec::new(),
function_call_counts: HashMap::new(),
recursion_tracker: RecursionTracker {
current_recursion_stack: Vec::new(),
recursion_info: HashMap::new(),
},
stats: AnalyzerStats::default(),
}
}
pub fn enter_function(
&mut self,
function_name: &str,
file: Option<String>,
line: Option<u32>,
column: Option<u32>,
timestamp: u64,
) {
let depth: _ = self.current_stack.len();
if depth >= self.max_depth {
eprintln!("Warning: Maximum call depth {} reached", self.max_depth);
}
let frame: _ = StackFrame {
function_name: function_name.to_string(),
file,
line,
column,
execution_time: Duration::from_nanos(0),
memory_used: 0,
depth,
timestamp,
};
self.current_stack.push_back(frame);
self.stats.total_calls += 1;
if depth > self.stats.max_call_depth {
self.stats.max_call_depth = depth;
}
*self
.function_call_counts
.entry(function_name.to_string())
.or_insert(0) += 1;
self.check_recursion(function_name);
}
pub fn exit_function(
&mut self,
function_name: &str,
end_timestamp: u64,
memory_used: usize,
) -> Option<StackFrame> {
if let Some(frame) = self.current_stack.pop_back() {
if frame.function_name == function_name {
let execution_time: _ = Duration::from_nanos(end_timestamp - frame.timestamp);
let mut completed_frame = frame;
completed_frame.execution_time = execution_time;
completed_frame.memory_used = memory_used;
self.call_history.push(completed_frame.clone());
if let Some(pos) = self
.recursion_tracker
.current_recursion_stack
.iter()
.position(|f| f == function_name)
{
self.recursion_tracker.current_recursion_stack.remove(pos);
}
return Some(completed_frame);
} else {
self.current_stack.push_back(frame);
}
}
None
}
fn check_recursion(&mut self, function_name: &str) {
if self
.recursion_tracker
.current_recursion_stack
.iter()
.any(|s| s == function_name)
{
let depth: _ = self
.recursion_tracker
.current_recursion_stack
.iter()
.rev()
.position(|f| f == function_name)
.map(|pos| self.recursion_tracker.current_recursion_stack.len() - pos)
.unwrap_or(1);
let recursion_info: _ = self
.recursion_tracker
.recursion_info
.entry(function_name.to_string())
.or_insert_with(|| RecursionInfo {
function: function_name.to_string(),
recursion_depth: 0,
recursion_count: 0,
is_direct_recursion: true,
});
recursion_info.recursion_depth = recursion_info.recursion_depth.max(depth);
recursion_info.recursion_count += 1;
self.stats.recursion_detected += 1;
} else {
self.recursion_tracker
.current_recursion_stack
.push(function_name.to_string());
}
}
pub fn analyze_stack(&mut self) -> CallStackAnalysis {
let hot_path: _ = self.identify_hot_path();
let bottlenecks: _ = self.identify_bottlenecks();
let recursion: _ = self.get_recursion_info();
let depth_stats: _ = self.calculate_depth_stats();
CallStackAnalysis {
hot_path,
bottlenecks,
recursion,
depth_stats,
}
}
fn identify_hot_path(&self) -> Vec<String> {
let mut function_counts: Vec<(&String, &usize)> =
self.function_call_counts.iter().collect();
function_counts.sort_by(|a, b| b.1.cmp(a.1));
function_counts
.into_iter()
.take(5)
.map(|(name, _)| name.clone())
.collect()
}
fn identify_bottlenecks(&mut self) -> Vec<Bottleneck> {
let mut bottlenecks = Vec::new();
for (function_name, call_count) in &self.function_call_counts {
let total_execution_time: Duration = self
.call_history
.iter()
.filter(|frame| &frame.function_name == function_name)
.map(|frame| frame.execution_time)
.sum();
let avg_execution_time: _ = if *call_count > 0 {
Duration::from_nanos(total_execution_time.as_nanos() as u64 / *call_count as u64)
} else {
Duration::from_nanos(0)
};
if avg_execution_time > Duration::from_millis(10) {
bottlenecks.push(Bottleneck {
function: function_name.clone(),
bottleneck_type: BottleneckType::SlowExecution,
description: format!("Average execution time: {:?}", avg_execution_time),
impact: 0.8,
});
}
if *call_count > 1000 {
bottlenecks.push(Bottleneck {
function: function_name.clone(),
bottleneck_type: BottleneckType::HighCallFrequency,
description: format!("Call count: {}", call_count),
impact: 0.6,
});
}
}
if self.stats.max_call_depth > 10 {
bottlenecks.push(Bottleneck {
function: "Deep Nesting".to_string(),
bottleneck_type: BottleneckType::DeepNesting,
description: format!("Maximum call depth: {}", self.stats.max_call_depth),
impact: 0.7,
});
}
self.stats.bottlenecks_detected += bottlenecks.len() as u64;
bottlenecks
}
fn get_recursion_info(&self) -> Vec<RecursionInfo> {
self.recursion_tracker
.recursion_info
.values()
.cloned()
.collect()
}
fn calculate_depth_stats(&self) -> DepthStats {
let mut depth_distribution: HashMap<usize, usize> = HashMap::new();
for frame in &self.call_history {
*depth_distribution.entry(frame.depth).or_insert(0) += 1;
}
let total_calls: _ = self.call_history.len() as f64;
let avg_depth: _ = if total_calls > 0.0 {
self.call_history
.iter()
.map(|frame| frame.depth as f64)
.sum::<f64>()
/ total_calls
} else {
0.0
};
DepthStats {
max_depth: self.stats.max_call_depth,
avg_depth,
depth_distribution,
}
}
pub fn get_current_stack(&self) -> Vec<&StackFrame> {
self.current_stack.iter().collect()
}
pub fn get_function_call_counts(&self) -> HashMap<String, usize> {
self.function_call_counts.clone()
}
pub fn get_stats(&self) -> &AnalyzerStats {
&self.stats
}
pub fn clear(&mut self) {
self.current_stack.clear();
self.call_history.clear();
self.function_call_counts.clear();
self.recursion_tracker.current_recursion_stack.clear();
self.recursion_tracker.recursion_info.clear();
self.stats = AnalyzerStats::default();
}
pub fn get_call_history(&self) -> &[StackFrame] {
&self.call_history
}
pub fn is_recursive_call(&self, function_name: &str) -> bool {
self.recursion_tracker
.current_recursion_stack
.iter()
.any(|s| s == function_name)
}
pub fn get_current_recursion_depth(&self) -> usize {
self.recursion_tracker.current_recursion_stack.len()
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_call_stack_analyzer_creation() {
let analyzer: _ = CallStackAnalyzer::new(100);
assert_eq!(analyzer.get_stats().total_calls, 0);
}
#[test]
fn test_enter_and_exit_function() {
let mut analyzer = CallStackAnalyzer::new(10);
let start_time: _ = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
analyzer.enter_function("test_func", None, None, None, start_time);
std::thread::sleep(Duration::from_millis(10));
let end_time: _ = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
let frame: _ = analyzer.exit_function("test_func", end_time, 1024);
assert!(frame.is_some());
let frame_data: _ = frame.unwrap();
assert_eq!(frame_data.function_name, "test_func");
assert!(frame_data.execution_time >= Duration::from_millis(10));
}
#[test]
fn test_recursion_detection() {
let mut analyzer = CallStackAnalyzer::new(10);
let start_time: _ = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
analyzer.enter_function("recursive_func", None, None, None, start_time);
assert!(!analyzer.is_recursive_call("recursive_func"));
analyzer.enter_function("recursive_func", None, None, None, start_time);
assert!(analyzer.is_recursive_call("recursive_func"));
let end_time: _ = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
analyzer.exit_function("recursive_func", end_time, 0);
assert!(!analyzer.is_recursive_call("recursive_func"));
}
#[test]
fn test_analyze_stack() {
let mut analyzer = CallStackAnalyzer::new(10);
let start_time: _ = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
analyzer.enter_function("func_a", None, None, None, start_time);
analyzer.enter_function("func_b", None, None, None, start_time);
let end_time: _ = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
analyzer.exit_function("func_b", end_time, 0);
analyzer.exit_function("func_a", end_time, 0);
let analysis: _ = analyzer.analyze_stack();
assert!(!analysis.hot_path.is_empty());
assert!(analysis.depth_stats.max_depth > 0);
}
#[test]
fn test_bottleneck_detection() {
let mut analyzer = CallStackAnalyzer::new(10);
for _ in 0..1100 {
let start_time: _ = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
analyzer.enter_function("frequent_func", None, None, None, start_time);
let end_time: _ = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
analyzer.exit_function("frequent_func", end_time, 0);
}
let analysis: _ = analyzer.analyze_stack();
let has_frequency_bottleneck: _ = analysis.bottlenecks.iter().any(|b| {
b.function == "frequent_func" && b.bottleneck_type == BottleneckType::HighCallFrequency
});
assert!(has_frequency_bottleneck);
}
#[test]
fn test_max_depth_limit() {
let mut analyzer = CallStackAnalyzer::new(3);
let start_time: _ = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
for i in 0..5 {
analyzer.enter_function(&format!("func_{}", i), None, None, None, start_time);
}
let stats: _ = analyzer.get_stats();
assert_eq!(stats.max_call_depth, 4); }
#[test]
fn test_clear() {
let mut analyzer = CallStackAnalyzer::new(10);
let start_time: _ = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
analyzer.enter_function("test_func", None, None, None, start_time);
let end_time: _ = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
analyzer.exit_function("test_func", end_time, 0);
analyzer.clear();
assert_eq!(analyzer.get_stats().total_calls, 0);
assert_eq!(analyzer.get_call_history().len(), 0);
}
#[test]
fn test_depth_statistics() {
let mut analyzer = CallStackAnalyzer::new(10);
let start_time: _ = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
analyzer.enter_function("depth_0", None, None, None, start_time);
analyzer.enter_function("depth_1", None, None, None, start_time);
let end_time: _ = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
analyzer.exit_function("depth_1", end_time, 0);
analyzer.exit_function("depth_0", end_time, 0);
let analysis: _ = analyzer.analyze_stack();
assert!(analysis.depth_stats.avg_depth >= 0.0);
assert!(analysis.depth_stats.max_depth > 0);
}
}