1use std::collections::{BTreeMap, HashMap, VecDeque};
9use std::fmt;
10use std::sync::{Arc, Mutex, RwLock};
11use std::thread;
12use std::time::{Duration, Instant, SystemTime};
13
14#[derive(Debug)]
16pub struct PerformanceProfiler {
18 cpu_profiler: Arc<Mutex<CpuProfiler>>,
20 memory_profiler: Arc<Mutex<MemoryProfiler>>,
22 gpu_profiler: Arc<Mutex<GpuProfiler>>,
24 network_profiler: Arc<Mutex<NetworkProfiler>>,
26 custom_profiler: Arc<Mutex<CustomProfiler>>,
28 config: ProfilingConfig,
30 session: Arc<RwLock<Option<ProfilingSession>>>,
32}
33
34#[derive(Debug, Clone)]
36pub struct ProfilingConfig {
38 pub cpu_profiling: bool,
40 pub memory_profiling: bool,
42 pub gpu_profiling: bool,
44 pub network_profiling: bool,
46 pub cpu_sample_rate: u32,
48 pub memory_threshold: usize,
50 pub max_data_points: usize,
52 pub collect_stack_traces: bool,
54 pub output_directory: String,
56}
57
58impl Default for ProfilingConfig {
59 fn default() -> Self {
60 Self {
61 cpu_profiling: true,
62 memory_profiling: true,
63 gpu_profiling: true,
64 network_profiling: true,
65 cpu_sample_rate: 100, memory_threshold: 1024, max_data_points: 100000,
68 collect_stack_traces: true,
69 output_directory: "/tmp/voirs_profiling".to_string(),
70 }
71 }
72}
73
74#[derive(Debug, Clone)]
76pub struct ProfilingSession {
78 pub id: String,
80 pub start_time: SystemTime,
82 pub end_time: Option<SystemTime>,
84 pub description: String,
86 pub tags: HashMap<String, String>,
88}
89
90impl PerformanceProfiler {
91 #[must_use]
93 pub fn new(config: ProfilingConfig) -> Self {
94 Self {
95 cpu_profiler: Arc::new(Mutex::new(CpuProfiler::new(config.clone()))),
96 memory_profiler: Arc::new(Mutex::new(MemoryProfiler::new(config.clone()))),
97 gpu_profiler: Arc::new(Mutex::new(GpuProfiler::new(config.clone()))),
98 network_profiler: Arc::new(Mutex::new(NetworkProfiler::new(config.clone()))),
99 custom_profiler: Arc::new(Mutex::new(CustomProfiler::new(config.clone()))),
100 config,
101 session: Arc::new(RwLock::new(None)),
102 }
103 }
104
105 #[must_use]
107 pub fn start_session(&self, description: String, tags: HashMap<String, String>) -> String {
108 let session_id = uuid::Uuid::new_v4().to_string();
109 let session = ProfilingSession {
110 id: session_id.clone(),
111 start_time: SystemTime::now(),
112 end_time: None,
113 description,
114 tags,
115 };
116
117 *self.session.write().unwrap() = Some(session);
118
119 if self.config.cpu_profiling {
121 self.cpu_profiler.lock().unwrap().start();
122 }
123 if self.config.memory_profiling {
124 self.memory_profiler.lock().unwrap().start();
125 }
126 if self.config.gpu_profiling {
127 self.gpu_profiler.lock().unwrap().start();
128 }
129 if self.config.network_profiling {
130 self.network_profiler.lock().unwrap().start();
131 }
132
133 session_id
134 }
135
136 #[must_use]
138 pub fn stop_session(&self) -> Option<ProfilingReport> {
139 let mut session_guard = self.session.write().unwrap();
140 if let Some(ref mut session) = *session_guard {
141 session.end_time = Some(SystemTime::now());
142
143 let cpu_report = if self.config.cpu_profiling {
145 self.cpu_profiler.lock().unwrap().stop()
146 } else {
147 CpuProfilingReport::default()
148 };
149
150 let memory_report = if self.config.memory_profiling {
151 self.memory_profiler.lock().unwrap().stop()
152 } else {
153 MemoryProfilingReport::default()
154 };
155
156 let gpu_report = if self.config.gpu_profiling {
157 self.gpu_profiler.lock().unwrap().stop()
158 } else {
159 GpuProfilingReport::default()
160 };
161
162 let network_report = if self.config.network_profiling {
163 self.network_profiler.lock().unwrap().stop()
164 } else {
165 NetworkProfilingReport::default()
166 };
167
168 let custom_report = self.custom_profiler.lock().unwrap().get_report();
169
170 let report = ProfilingReport {
171 session: session.clone(),
172 cpu_report,
173 memory_report,
174 gpu_report,
175 network_report,
176 custom_report,
177 };
178
179 *session_guard = None;
180 Some(report)
181 } else {
182 None
183 }
184 }
185
186 pub fn profile_function<F, R>(&self, name: &str, func: F) -> (R, FunctionProfile)
188 where
189 F: FnOnce() -> R,
190 {
191 let start_time = Instant::now();
192 let start_memory = self.get_current_memory_usage();
193
194 self.custom_profiler
196 .lock()
197 .unwrap()
198 .mark_function_entry(name);
199
200 let result = func();
202
203 self.custom_profiler
205 .lock()
206 .unwrap()
207 .mark_function_exit(name);
208
209 let end_time = Instant::now();
210 let end_memory = self.get_current_memory_usage();
211
212 let profile = FunctionProfile {
213 name: name.to_string(),
214 duration: end_time - start_time,
215 memory_delta: end_memory as i64 - start_memory as i64,
216 cpu_usage: self.get_current_cpu_usage(),
217 };
218
219 (result, profile)
220 }
221
222 fn get_current_memory_usage(&self) -> usize {
224 1024 * 1024 }
228
229 fn get_current_cpu_usage(&self) -> f64 {
231 25.0 }
235
236 pub fn add_custom_event(&self, name: String, data: CustomEventData) {
238 self.custom_profiler.lock().unwrap().add_event(name, data);
239 }
240
241 #[must_use]
243 pub fn get_current_session(&self) -> Option<ProfilingSession> {
244 self.session.read().unwrap().clone()
245 }
246}
247
248#[derive(Debug, Clone)]
250pub struct FunctionProfile {
252 pub name: String,
254 pub duration: Duration,
256 pub memory_delta: i64,
258 pub cpu_usage: f64,
260}
261
262#[derive(Debug, Clone)]
264pub struct ProfilingReport {
266 pub session: ProfilingSession,
268 pub cpu_report: CpuProfilingReport,
270 pub memory_report: MemoryProfilingReport,
272 pub gpu_report: GpuProfilingReport,
274 pub network_report: NetworkProfilingReport,
276 pub custom_report: CustomProfilingReport,
278}
279
280#[derive(Debug)]
282pub struct CpuProfiler {
284 config: ProfilingConfig,
286 active: bool,
288 samples: VecDeque<CpuSample>,
290 call_stack: Vec<FunctionCall>,
292 hot_spots: HashMap<String, Duration>,
294}
295
296#[derive(Debug, Clone)]
297pub struct CpuSample {
299 pub timestamp: Instant,
301 pub cpu_usage: f64,
303 pub call_stack: Vec<String>,
305 pub thread_id: u64,
307}
308
309#[derive(Debug, Clone)]
310pub struct FunctionCall {
312 pub name: String,
314 pub start_time: Instant,
316 pub depth: usize,
318}
319
320#[derive(Debug, Clone, Default)]
321pub struct CpuProfilingReport {
323 pub total_duration: Duration,
325 pub average_cpu_usage: f64,
327 pub peak_cpu_usage: f64,
329 pub hot_spots: HashMap<String, u64>,
331 pub call_graph: CallGraph,
333 pub cpu_timeline: Vec<CpuTimelinePoint>,
335}
336
337#[derive(Debug, Clone, Default)]
338pub struct CallGraph {
340 pub nodes: HashMap<String, CallGraphNode>,
342 pub edges: Vec<CallGraphEdge>,
344}
345
346#[derive(Debug, Clone)]
347pub struct CallGraphNode {
349 pub name: String,
351 pub total_time: Duration,
353 pub call_count: u64,
355 pub average_time: Duration,
357}
358
359#[derive(Debug, Clone)]
360pub struct CallGraphEdge {
362 pub from: String,
364 pub to: String,
366 pub call_count: u64,
368}
369
370#[derive(Debug, Clone)]
371pub struct CpuTimelinePoint {
373 pub timestamp: Instant,
375 pub cpu_usage: f64,
377}
378
379impl CpuProfiler {
380 fn new(config: ProfilingConfig) -> Self {
381 Self {
382 config,
383 active: false,
384 samples: VecDeque::new(),
385 call_stack: Vec::new(),
386 hot_spots: HashMap::new(),
387 }
388 }
389
390 fn start(&mut self) {
391 self.active = true;
392 self.samples.clear();
393 self.call_stack.clear();
394 self.hot_spots.clear();
395 }
396
397 fn stop(&mut self) -> CpuProfilingReport {
398 self.active = false;
399
400 let total_duration =
401 if let (Some(first), Some(last)) = (self.samples.front(), self.samples.back()) {
402 last.timestamp - first.timestamp
403 } else {
404 Duration::from_secs(0)
405 };
406
407 let average_cpu_usage = if self.samples.is_empty() {
408 0.0
409 } else {
410 self.samples.iter().map(|s| s.cpu_usage).sum::<f64>() / self.samples.len() as f64
411 };
412
413 let peak_cpu_usage = self.samples.iter().map(|s| s.cpu_usage).fold(0.0, f64::max);
414
415 let hot_spots = self
416 .hot_spots
417 .iter()
418 .map(|(name, duration)| (name.clone(), duration.as_millis() as u64))
419 .collect();
420
421 let call_graph = self.build_call_graph();
422
423 let cpu_timeline = self
424 .samples
425 .iter()
426 .map(|s| CpuTimelinePoint {
427 timestamp: s.timestamp,
428 cpu_usage: s.cpu_usage,
429 })
430 .collect();
431
432 CpuProfilingReport {
433 total_duration,
434 average_cpu_usage,
435 peak_cpu_usage,
436 hot_spots,
437 call_graph,
438 cpu_timeline,
439 }
440 }
441
442 fn build_call_graph(&self) -> CallGraph {
443 let mut nodes = HashMap::new();
444 let mut edges = Vec::new();
445
446 for (function_name, total_time) in &self.hot_spots {
448 let node = CallGraphNode {
449 name: function_name.clone(),
450 total_time: *total_time,
451 call_count: 1, average_time: *total_time,
453 };
454 nodes.insert(function_name.clone(), node);
455 }
456
457 CallGraph { nodes, edges }
458 }
459
460 pub fn add_sample(&mut self, cpu_usage: f64) {
462 if !self.active {
463 return;
464 }
465
466 let sample = CpuSample {
467 timestamp: Instant::now(),
468 cpu_usage,
469 call_stack: self.call_stack.iter().map(|c| c.name.clone()).collect(),
470 thread_id: 0, };
472
473 self.samples.push_back(sample);
474
475 while self.samples.len() > self.config.max_data_points {
477 self.samples.pop_front();
478 }
479 }
480
481 pub fn enter_function(&mut self, name: &str) {
483 if !self.active {
484 return;
485 }
486
487 let call = FunctionCall {
488 name: name.to_string(),
489 start_time: Instant::now(),
490 depth: self.call_stack.len(),
491 };
492
493 self.call_stack.push(call);
494 }
495
496 pub fn exit_function(&mut self, name: &str) {
498 if !self.active {
499 return;
500 }
501
502 if let Some(call) = self.call_stack.pop() {
503 if call.name == name {
504 let duration = call.start_time.elapsed();
505 *self
506 .hot_spots
507 .entry(name.to_string())
508 .or_insert(Duration::from_secs(0)) += duration;
509 }
510 }
511 }
512}
513
514#[derive(Debug)]
516pub struct MemoryProfiler {
518 config: ProfilingConfig,
520 active: bool,
522 allocations: HashMap<usize, AllocationInfo>,
524 timeline: VecDeque<MemoryTimelinePoint>,
526 leaks: Vec<MemoryLeak>,
528}
529
530#[derive(Debug, Clone)]
531pub struct AllocationInfo {
533 pub size: usize,
535 pub timestamp: Instant,
537 pub stack_trace: Vec<String>,
539 pub tag: String,
541}
542
543#[derive(Debug, Clone)]
544pub struct MemoryTimelinePoint {
546 pub timestamp: Instant,
548 pub total_allocated: usize,
550 pub allocation_count: usize,
552 pub usage_percentage: f64,
554}
555
556#[derive(Debug, Clone)]
557pub struct MemoryLeak {
559 pub size: usize,
561 pub allocation_time: Instant,
563 pub stack_trace: Vec<String>,
565 pub confidence: f64,
567}
568
569#[derive(Debug, Clone, Default)]
570pub struct MemoryProfilingReport {
572 pub total_allocated: usize,
574 pub peak_memory_usage: usize,
576 pub average_memory_usage: usize,
578 pub allocation_count: usize,
580 pub deallocation_count: usize,
582 pub memory_leaks: Vec<MemoryLeak>,
584 pub allocation_hot_spots: HashMap<String, usize>,
586 pub memory_timeline: Vec<MemoryTimelinePoint>,
588}
589
590impl MemoryProfiler {
591 fn new(config: ProfilingConfig) -> Self {
592 Self {
593 config,
594 active: false,
595 allocations: HashMap::new(),
596 timeline: VecDeque::new(),
597 leaks: Vec::new(),
598 }
599 }
600
601 fn start(&mut self) {
602 self.active = true;
603 self.allocations.clear();
604 self.timeline.clear();
605 self.leaks.clear();
606 }
607
608 fn stop(&mut self) -> MemoryProfilingReport {
609 self.active = false;
610
611 self.detect_memory_leaks();
613
614 let total_allocated = self.allocations.values().map(|a| a.size).sum();
615 let allocation_count = self.allocations.len();
616
617 let peak_memory_usage = self
618 .timeline
619 .iter()
620 .map(|p| p.total_allocated)
621 .max()
622 .unwrap_or(0);
623
624 let average_memory_usage = if self.timeline.is_empty() {
625 0
626 } else {
627 self.timeline
628 .iter()
629 .map(|p| p.total_allocated)
630 .sum::<usize>()
631 / self.timeline.len()
632 };
633
634 let allocation_hot_spots = self.calculate_allocation_hot_spots();
635
636 MemoryProfilingReport {
637 total_allocated,
638 peak_memory_usage,
639 average_memory_usage,
640 allocation_count,
641 deallocation_count: 0, memory_leaks: self.leaks.clone(),
643 allocation_hot_spots,
644 memory_timeline: self.timeline.iter().cloned().collect(),
645 }
646 }
647
648 fn detect_memory_leaks(&mut self) {
649 let now = Instant::now();
650 let leak_threshold = Duration::from_secs(300); for allocation in self.allocations.values() {
653 if now.duration_since(allocation.timestamp) > leak_threshold {
654 let leak = MemoryLeak {
655 size: allocation.size,
656 allocation_time: allocation.timestamp,
657 stack_trace: allocation.stack_trace.clone(),
658 confidence: 0.8, };
660 self.leaks.push(leak);
661 }
662 }
663 }
664
665 fn calculate_allocation_hot_spots(&self) -> HashMap<String, usize> {
666 let mut hot_spots = HashMap::new();
667
668 for allocation in self.allocations.values() {
669 *hot_spots.entry(allocation.tag.clone()).or_insert(0) += allocation.size;
670 }
671
672 hot_spots
673 }
674
675 pub fn track_allocation(&mut self, ptr: usize, size: usize, tag: String) {
677 if !self.active || size < self.config.memory_threshold {
678 return;
679 }
680
681 let allocation = AllocationInfo {
682 size,
683 timestamp: Instant::now(),
684 stack_trace: if self.config.collect_stack_traces {
685 vec!["placeholder_stack_trace".to_string()]
686 } else {
687 Vec::new()
688 },
689 tag,
690 };
691
692 self.allocations.insert(ptr, allocation);
693
694 self.update_memory_timeline();
696 }
697
698 pub fn track_deallocation(&mut self, ptr: usize) {
700 if !self.active {
701 return;
702 }
703
704 self.allocations.remove(&ptr);
705 self.update_memory_timeline();
706 }
707
708 fn update_memory_timeline(&mut self) {
709 let total_allocated = self.allocations.values().map(|a| a.size).sum();
710 let allocation_count = self.allocations.len();
711
712 let point = MemoryTimelinePoint {
713 timestamp: Instant::now(),
714 total_allocated,
715 allocation_count,
716 usage_percentage: (total_allocated as f64 / (8u64 * 1024 * 1024 * 1024) as f64) * 100.0, };
718
719 self.timeline.push_back(point);
720
721 while self.timeline.len() > self.config.max_data_points {
723 self.timeline.pop_front();
724 }
725 }
726}
727
728#[derive(Debug)]
730pub struct GpuProfiler {
732 config: ProfilingConfig,
734 active: bool,
736 utilization_samples: VecDeque<GpuUtilizationSample>,
738 memory_transfers: Vec<GpuMemoryTransfer>,
740 kernel_executions: Vec<GpuKernelExecution>,
742}
743
744#[derive(Debug, Clone)]
745pub struct GpuUtilizationSample {
747 pub timestamp: Instant,
749 pub gpu_utilization: f64,
751 pub memory_utilization: f64,
753 pub temperature: f64,
755 pub power_consumption: f64,
757}
758
759#[derive(Debug, Clone)]
760pub struct GpuMemoryTransfer {
762 pub start_time: Instant,
764 pub duration: Duration,
766 pub size: usize,
768 pub direction: GpuMemoryDirection,
770 pub bandwidth: f64,
772}
773
774#[derive(Debug, Clone)]
775pub enum GpuMemoryDirection {
777 HostToDevice,
779 DeviceToHost,
781 DeviceToDevice,
783}
784
785#[derive(Debug, Clone)]
786pub struct GpuKernelExecution {
788 pub name: String,
790 pub start_time: Instant,
792 pub duration: Duration,
794 pub grid_size: (u32, u32, u32),
796 pub block_size: (u32, u32, u32),
798 pub shared_memory: usize,
800 pub registers_per_thread: u32,
802}
803
804#[derive(Debug, Clone, Default)]
805pub struct GpuProfilingReport {
807 pub average_gpu_utilization: f64,
809 pub peak_gpu_utilization: f64,
811 pub average_memory_utilization: f64,
813 pub total_memory_transfers: usize,
815 pub total_transfer_volume: usize,
817 pub kernel_stats: HashMap<String, KernelStatistics>,
819 pub utilization_timeline: Vec<GpuUtilizationSample>,
821}
822
823#[derive(Debug, Clone)]
824pub struct KernelStatistics {
826 pub execution_count: u64,
828 pub total_time: Duration,
830 pub average_time: Duration,
832 pub min_time: Duration,
834 pub max_time: Duration,
836}
837
838impl GpuProfiler {
839 fn new(config: ProfilingConfig) -> Self {
840 Self {
841 config,
842 active: false,
843 utilization_samples: VecDeque::new(),
844 memory_transfers: Vec::new(),
845 kernel_executions: Vec::new(),
846 }
847 }
848
849 fn start(&mut self) {
850 self.active = true;
851 self.utilization_samples.clear();
852 self.memory_transfers.clear();
853 self.kernel_executions.clear();
854 }
855
856 fn stop(&mut self) -> GpuProfilingReport {
857 self.active = false;
858
859 let average_gpu_utilization = if self.utilization_samples.is_empty() {
860 0.0
861 } else {
862 self.utilization_samples
863 .iter()
864 .map(|s| s.gpu_utilization)
865 .sum::<f64>()
866 / self.utilization_samples.len() as f64
867 };
868
869 let peak_gpu_utilization = self
870 .utilization_samples
871 .iter()
872 .map(|s| s.gpu_utilization)
873 .fold(0.0, f64::max);
874
875 let average_memory_utilization = if self.utilization_samples.is_empty() {
876 0.0
877 } else {
878 self.utilization_samples
879 .iter()
880 .map(|s| s.memory_utilization)
881 .sum::<f64>()
882 / self.utilization_samples.len() as f64
883 };
884
885 let total_memory_transfers = self.memory_transfers.len();
886 let total_transfer_volume = self.memory_transfers.iter().map(|t| t.size).sum();
887
888 let kernel_stats = self.calculate_kernel_statistics();
889
890 GpuProfilingReport {
891 average_gpu_utilization,
892 peak_gpu_utilization,
893 average_memory_utilization,
894 total_memory_transfers,
895 total_transfer_volume,
896 kernel_stats,
897 utilization_timeline: self.utilization_samples.iter().cloned().collect(),
898 }
899 }
900
901 fn calculate_kernel_statistics(&self) -> HashMap<String, KernelStatistics> {
902 let mut stats = HashMap::new();
903
904 for execution in &self.kernel_executions {
905 let entry = stats
906 .entry(execution.name.clone())
907 .or_insert(KernelStatistics {
908 execution_count: 0,
909 total_time: Duration::from_secs(0),
910 average_time: Duration::from_secs(0),
911 min_time: Duration::from_secs(u64::MAX),
912 max_time: Duration::from_secs(0),
913 });
914
915 entry.execution_count += 1;
916 entry.total_time += execution.duration;
917 entry.min_time = entry.min_time.min(execution.duration);
918 entry.max_time = entry.max_time.max(execution.duration);
919 }
920
921 for stat in stats.values_mut() {
923 if stat.execution_count > 0 {
924 stat.average_time = stat.total_time / stat.execution_count as u32;
925 }
926 }
927
928 stats
929 }
930
931 pub fn add_utilization_sample(
933 &mut self,
934 gpu_util: f64,
935 memory_util: f64,
936 temperature: f64,
937 power: f64,
938 ) {
939 if !self.active {
940 return;
941 }
942
943 let sample = GpuUtilizationSample {
944 timestamp: Instant::now(),
945 gpu_utilization: gpu_util,
946 memory_utilization: memory_util,
947 temperature,
948 power_consumption: power,
949 };
950
951 self.utilization_samples.push_back(sample);
952
953 while self.utilization_samples.len() > self.config.max_data_points {
954 self.utilization_samples.pop_front();
955 }
956 }
957
958 pub fn track_memory_transfer(
960 &mut self,
961 size: usize,
962 direction: GpuMemoryDirection,
963 duration: Duration,
964 ) {
965 if !self.active {
966 return;
967 }
968
969 let bandwidth = if duration.as_secs_f64() > 0.0 {
970 (size as f64) / duration.as_secs_f64() / (1024.0 * 1024.0 * 1024.0) } else {
972 0.0
973 };
974
975 let transfer = GpuMemoryTransfer {
976 start_time: Instant::now(),
977 duration,
978 size,
979 direction,
980 bandwidth,
981 };
982
983 self.memory_transfers.push(transfer);
984 }
985
986 pub fn track_kernel_execution(
988 &mut self,
989 name: String,
990 duration: Duration,
991 grid_size: (u32, u32, u32),
992 block_size: (u32, u32, u32),
993 ) {
994 if !self.active {
995 return;
996 }
997
998 let execution = GpuKernelExecution {
999 name,
1000 start_time: Instant::now(),
1001 duration,
1002 grid_size,
1003 block_size,
1004 shared_memory: 0, registers_per_thread: 32, };
1007
1008 self.kernel_executions.push(execution);
1009 }
1010}
1011
1012#[derive(Debug)]
1014pub struct NetworkProfiler {
1016 config: ProfilingConfig,
1018 active: bool,
1020 requests: Vec<NetworkRequest>,
1022 bandwidth_samples: VecDeque<BandwidthSample>,
1024}
1025
1026#[derive(Debug, Clone)]
1027pub struct NetworkRequest {
1029 pub id: String,
1031 pub url: String,
1033 pub method: String,
1035 pub start_time: Instant,
1037 pub duration: Duration,
1039 pub request_size: usize,
1041 pub response_size: usize,
1043 pub status_code: u16,
1045 pub connection_reused: bool,
1047}
1048
1049#[derive(Debug, Clone)]
1050pub struct BandwidthSample {
1052 pub timestamp: Instant,
1054 pub upload_bandwidth: f64,
1056 pub download_bandwidth: f64,
1058 pub latency: f64,
1060}
1061
1062#[derive(Debug, Clone, Default)]
1063pub struct NetworkProfilingReport {
1065 pub total_requests: usize,
1067 pub average_request_duration: Duration,
1069 pub total_bytes_sent: usize,
1071 pub total_bytes_received: usize,
1073 pub average_bandwidth: f64,
1075 pub endpoint_stats: HashMap<String, EndpointStatistics>,
1077 pub bandwidth_timeline: Vec<BandwidthSample>,
1079}
1080
1081#[derive(Debug, Clone)]
1082pub struct EndpointStatistics {
1084 pub request_count: usize,
1086 pub average_response_time: Duration,
1088 pub success_rate: f64,
1090 pub error_count: usize,
1092}
1093
1094impl NetworkProfiler {
1095 fn new(config: ProfilingConfig) -> Self {
1096 Self {
1097 config,
1098 active: false,
1099 requests: Vec::new(),
1100 bandwidth_samples: VecDeque::new(),
1101 }
1102 }
1103
1104 fn start(&mut self) {
1105 self.active = true;
1106 self.requests.clear();
1107 self.bandwidth_samples.clear();
1108 }
1109
1110 fn stop(&mut self) -> NetworkProfilingReport {
1111 self.active = false;
1112
1113 let total_requests = self.requests.len();
1114
1115 let average_request_duration = if self.requests.is_empty() {
1116 Duration::from_secs(0)
1117 } else {
1118 let total_duration: Duration = self.requests.iter().map(|r| r.duration).sum();
1119 total_duration / self.requests.len() as u32
1120 };
1121
1122 let total_bytes_sent = self.requests.iter().map(|r| r.request_size).sum();
1123 let total_bytes_received = self.requests.iter().map(|r| r.response_size).sum();
1124
1125 let average_bandwidth = if self.bandwidth_samples.is_empty() {
1126 0.0
1127 } else {
1128 self.bandwidth_samples
1129 .iter()
1130 .map(|s| s.download_bandwidth)
1131 .sum::<f64>()
1132 / self.bandwidth_samples.len() as f64
1133 };
1134
1135 let endpoint_stats = self.calculate_endpoint_statistics();
1136
1137 NetworkProfilingReport {
1138 total_requests,
1139 average_request_duration,
1140 total_bytes_sent,
1141 total_bytes_received,
1142 average_bandwidth,
1143 endpoint_stats,
1144 bandwidth_timeline: self.bandwidth_samples.iter().cloned().collect(),
1145 }
1146 }
1147
1148 fn calculate_endpoint_statistics(&self) -> HashMap<String, EndpointStatistics> {
1149 let mut stats = HashMap::new();
1150
1151 for request in &self.requests {
1152 let entry = stats
1153 .entry(request.url.clone())
1154 .or_insert(EndpointStatistics {
1155 request_count: 0,
1156 average_response_time: Duration::from_secs(0),
1157 success_rate: 0.0,
1158 error_count: 0,
1159 });
1160
1161 entry.request_count += 1;
1162
1163 if request.status_code >= 400 {
1164 entry.error_count += 1;
1165 }
1166 }
1167
1168 for (url, stat) in &mut stats {
1170 let url_requests: Vec<&NetworkRequest> =
1171 self.requests.iter().filter(|r| r.url == *url).collect();
1172
1173 if !url_requests.is_empty() {
1174 let total_duration: Duration = url_requests.iter().map(|r| r.duration).sum();
1175 stat.average_response_time = total_duration / url_requests.len() as u32;
1176
1177 let success_count = url_requests.iter().filter(|r| r.status_code < 400).count();
1178 stat.success_rate = success_count as f64 / url_requests.len() as f64;
1179 }
1180 }
1181
1182 stats
1183 }
1184
1185 pub fn track_request(&mut self, request: NetworkRequest) {
1187 if !self.active {
1188 return;
1189 }
1190
1191 self.requests.push(request);
1192 }
1193
1194 pub fn add_bandwidth_sample(&mut self, upload: f64, download: f64, latency: f64) {
1196 if !self.active {
1197 return;
1198 }
1199
1200 let sample = BandwidthSample {
1201 timestamp: Instant::now(),
1202 upload_bandwidth: upload,
1203 download_bandwidth: download,
1204 latency,
1205 };
1206
1207 self.bandwidth_samples.push_back(sample);
1208
1209 while self.bandwidth_samples.len() > self.config.max_data_points {
1210 self.bandwidth_samples.pop_front();
1211 }
1212 }
1213}
1214
1215#[derive(Debug)]
1217pub struct CustomProfiler {
1219 config: ProfilingConfig,
1221 events: Vec<CustomEvent>,
1223 function_stack: Vec<String>,
1225 function_timings: HashMap<String, Vec<Duration>>,
1227}
1228
1229#[derive(Debug, Clone)]
1230pub struct CustomEvent {
1232 pub name: String,
1234 pub timestamp: Instant,
1236 pub data: CustomEventData,
1238 pub tags: HashMap<String, String>,
1240}
1241
1242#[derive(Debug, Clone)]
1243pub enum CustomEventData {
1245 Counter(u64),
1247 Gauge(f64),
1249 Duration(Duration),
1251 Message(String),
1253 Structured(HashMap<String, String>),
1255}
1256
1257#[derive(Debug, Clone, Default)]
1258pub struct CustomProfilingReport {
1260 pub events: Vec<CustomEvent>,
1262 pub function_timings: HashMap<String, FunctionTimingStats>,
1264 pub event_frequency: HashMap<String, usize>,
1266}
1267
1268#[derive(Debug, Clone)]
1269pub struct FunctionTimingStats {
1271 pub call_count: usize,
1273 pub total_time: Duration,
1275 pub average_time: Duration,
1277 pub min_time: Duration,
1279 pub max_time: Duration,
1281 pub p95_time: Duration,
1283}
1284
1285impl CustomProfiler {
1286 fn new(config: ProfilingConfig) -> Self {
1287 Self {
1288 config,
1289 events: Vec::new(),
1290 function_stack: Vec::new(),
1291 function_timings: HashMap::new(),
1292 }
1293 }
1294
1295 pub fn add_event(&mut self, name: String, data: CustomEventData) {
1297 let event = CustomEvent {
1298 name,
1299 timestamp: Instant::now(),
1300 data,
1301 tags: HashMap::new(),
1302 };
1303
1304 self.events.push(event);
1305 }
1306
1307 pub fn mark_function_entry(&mut self, name: &str) {
1309 self.function_stack.push(name.to_string());
1310 }
1311
1312 pub fn mark_function_exit(&mut self, name: &str) {
1314 if let Some(current) = self.function_stack.last() {
1315 if current == name {
1316 self.function_stack.pop();
1317 }
1318 }
1319 }
1320
1321 #[must_use]
1323 pub fn get_report(&self) -> CustomProfilingReport {
1324 let mut event_frequency = HashMap::new();
1325 for event in &self.events {
1326 *event_frequency.entry(event.name.clone()).or_insert(0) += 1;
1327 }
1328
1329 let function_timings = self.calculate_function_timing_stats();
1330
1331 CustomProfilingReport {
1332 events: self.events.clone(),
1333 function_timings,
1334 event_frequency,
1335 }
1336 }
1337
1338 fn calculate_function_timing_stats(&self) -> HashMap<String, FunctionTimingStats> {
1339 let mut stats = HashMap::new();
1340
1341 for (function_name, timings) in &self.function_timings {
1342 if timings.is_empty() {
1343 continue;
1344 }
1345
1346 let mut sorted_timings = timings.clone();
1347 sorted_timings.sort();
1348
1349 let call_count = timings.len();
1350 let total_time = timings.iter().sum();
1351 let average_time = total_time / call_count as u32;
1352 let min_time = sorted_timings[0];
1353 let max_time = sorted_timings[call_count - 1];
1354 let p95_index = (call_count as f64 * 0.95) as usize;
1355 let p95_time = sorted_timings[p95_index.min(call_count - 1)];
1356
1357 stats.insert(
1358 function_name.clone(),
1359 FunctionTimingStats {
1360 call_count,
1361 total_time,
1362 average_time,
1363 min_time,
1364 max_time,
1365 p95_time,
1366 },
1367 );
1368 }
1369
1370 stats
1371 }
1372}
1373
1374#[macro_export]
1376macro_rules! profile_function {
1377 ($profiler:expr, $name:expr, $body:expr) => {{
1378 let (result, profile) = $profiler.profile_function($name, || $body);
1379 println!(
1380 "Function '{}' took {}ms",
1381 profile.name,
1382 profile.duration.as_millis()
1383 );
1384 result
1385 }};
1386}
1387
1388#[macro_export]
1389macro_rules! profile_block {
1391 ($profiler:expr, $name:expr, $block:block) => {{
1392 let start = std::time::Instant::now();
1393 let result = $block;
1394 let duration = start.elapsed();
1395 $profiler.add_custom_event(
1396 format!("block_{}", $name),
1397 $crate::monitoring::performance_profiling::CustomEventData::Duration(duration),
1398 );
1399 result
1400 }};
1401}
1402
1403#[cfg(test)]
1404mod tests {
1405 use super::*;
1406
1407 #[test]
1408 fn test_performance_profiler_creation() {
1409 let config = ProfilingConfig::default();
1410 let profiler = PerformanceProfiler::new(config);
1411
1412 assert!(profiler.get_current_session().is_none());
1413 }
1414
1415 #[test]
1416 fn test_profiling_session() {
1417 let config = ProfilingConfig::default();
1418 let profiler = PerformanceProfiler::new(config);
1419
1420 let session_id = profiler.start_session("test_session".to_string(), HashMap::new());
1421
1422 assert!(!session_id.is_empty());
1423 assert!(profiler.get_current_session().is_some());
1424
1425 let report = profiler.stop_session();
1426 assert!(report.is_some());
1427 assert!(profiler.get_current_session().is_none());
1428 }
1429
1430 #[test]
1431 fn test_function_profiling() {
1432 let config = ProfilingConfig::default();
1433 let profiler = PerformanceProfiler::new(config);
1434
1435 let (result, profile) = profiler.profile_function("test_function", || {
1436 thread::sleep(Duration::from_millis(10));
1437 42
1438 });
1439
1440 assert_eq!(result, 42);
1441 assert_eq!(profile.name, "test_function");
1442 assert!(profile.duration >= Duration::from_millis(10));
1443 }
1444
1445 #[test]
1446 fn test_cpu_profiler() {
1447 let config = ProfilingConfig::default();
1448 let mut cpu_profiler = CpuProfiler::new(config);
1449
1450 cpu_profiler.start();
1451 cpu_profiler.enter_function("test_function");
1452 cpu_profiler.add_sample(50.0);
1453 thread::sleep(Duration::from_millis(1));
1454 cpu_profiler.exit_function("test_function");
1455
1456 let report = cpu_profiler.stop();
1457 assert!(report.hot_spots.contains_key("test_function"));
1458 assert_eq!(report.cpu_timeline.len(), 1);
1459 assert_eq!(report.cpu_timeline[0].cpu_usage, 50.0);
1460 }
1461
1462 #[test]
1463 fn test_memory_profiler() {
1464 let config = ProfilingConfig::default();
1465 let mut memory_profiler = MemoryProfiler::new(config);
1466
1467 memory_profiler.start();
1468 memory_profiler.track_allocation(0x1000, 2048, "test_allocation".to_string());
1469 let report = memory_profiler.stop();
1472 assert_eq!(report.allocation_count, 1); assert!(report.allocation_hot_spots.contains_key("test_allocation"));
1474 }
1475
1476 #[test]
1477 fn test_custom_profiler() {
1478 let config = ProfilingConfig::default();
1479 let mut custom_profiler = CustomProfiler::new(config);
1480
1481 custom_profiler.add_event("test_event".to_string(), CustomEventData::Counter(42));
1482
1483 custom_profiler.mark_function_entry("test_function");
1484 custom_profiler.mark_function_exit("test_function");
1485
1486 let report = custom_profiler.get_report();
1487 assert_eq!(report.events.len(), 1);
1488 assert_eq!(report.event_frequency.get("test_event"), Some(&1));
1489 }
1490}