1#[cfg(feature = "full")]
56pub mod category;
57#[cfg(feature = "full")]
58pub mod collector;
59#[cfg(feature = "full")]
62pub mod operation;
63#[cfg(feature = "full")]
64pub mod reporter;
65#[cfg(feature = "full")]
66pub mod timer;
67
68#[cfg(not(feature = "full"))]
72pub mod category {
73 pub trait Category: Send + Sync {
74 fn get_name(&self) -> &str;
75 fn get_description(&self) -> &str;
76 fn color_hint(&self) -> Option<&str> {
77 None
78 }
79 fn priority(&self) -> i32 {
80 0
81 }
82 }
83
84 #[derive(Debug)]
85 pub struct NoCategory;
86
87 impl Category for NoCategory {
88 fn get_name(&self) -> &str {
89 "NoCategory"
90 }
91 fn get_description(&self) -> &str {
92 "Default category when none is specified"
93 }
94 }
95}
96
97#[cfg(not(feature = "full"))]
98pub mod operation {
99 use crate::category::{Category, NoCategory};
100 use std::fmt::Debug;
101
102 pub trait Operation: Debug + Send + Sync {
103 fn get_category(&self) -> &dyn Category {
104 &NoCategory
105 }
106
107 fn to_str(&self) -> String {
108 format!("{:?}", self)
109 }
110 }
111
112 #[derive(Debug)]
113 pub struct SimpleOperation {
114 pub name: String,
115 }
116
117 impl SimpleOperation {
118 pub fn new(name: impl Into<String>) -> Self {
119 Self { name: name.into() }
120 }
121 }
122
123 impl Operation for SimpleOperation {
124 fn to_str(&self) -> String {
125 self.name.clone()
126 }
127 }
128}
129
130#[cfg(not(feature = "full"))]
131pub mod collector {
132 use std::collections::HashMap;
133 use std::time::Duration;
134
135 #[derive(Debug, Clone, Default)]
136 pub struct OperationStats {
137 pub count: usize,
138 pub total: Duration,
139 }
140
141 impl OperationStats {
142 pub fn mean(&self) -> Duration {
143 if self.count == 0 {
144 Duration::ZERO
145 } else {
146 self.total / (self.count as u32)
147 }
148 }
149 }
150
151 pub struct ProfileCollector;
152
153 impl ProfileCollector {
154 pub fn record(_key: &str, _duration_micros: u64) {}
155 pub fn get_stats(_key: &str) -> Option<OperationStats> {
156 None
157 }
158 pub fn get_all_stats() -> HashMap<String, OperationStats> {
159 HashMap::new()
160 }
161 pub fn clear_all() {}
162 pub fn reset_all() {}
163 pub fn reset_operation(_key: &str) {}
164 pub fn has_data() -> bool {
165 false
166 }
167 pub fn total_operations() -> u64 {
168 1 }
170 pub fn get_summary() -> SummaryStats {
171 SummaryStats::default()
172 }
173 pub fn report_stats() {}
174
175 pub fn pause() {}
176
177 pub fn unpause() {}
178
179 pub fn is_paused() -> bool {
180 false
181 }
182
183 pub fn reset_pause_state() {}
184 }
185
186 #[derive(Debug, Default)]
187 pub struct SummaryStats {
188 pub total_operations: u64,
189 pub unique_operations: usize,
190 pub total_time_micros: u64,
191 }
192
193 #[derive(Debug, Clone, Copy)]
194 pub enum TimeFormat {
195 Microseconds,
196 Milliseconds,
197 Seconds,
198 Auto,
199 }
200
201 #[derive(Debug, Clone, Copy)]
202 pub enum SortMetric {
203 TotalTime,
204 MeanTime,
205 MaxTime,
206 CallCount,
207 }
208
209 #[derive(Debug, Clone, Copy)]
210 pub struct Percentile {
211 pub p50: u64,
212 pub p95: u64,
213 pub p99: u64,
214 pub p999: u64,
215 }
216
217 #[derive(Debug)]
218 pub struct ReportConfig {
219 pub include_percentiles: bool,
220 pub group_by_category: bool,
221 pub time_format: TimeFormat,
222 pub sort_by: SortMetric,
223 pub sort_by_time: bool,
224 pub min_samples: u64,
225 }
226
227 impl Default for ReportConfig {
228 fn default() -> Self {
229 Self {
230 include_percentiles: false,
231 group_by_category: false,
232 time_format: TimeFormat::Auto,
233 sort_by: SortMetric::TotalTime,
234 sort_by_time: false,
235 min_samples: 0,
236 }
237 }
238 }
239
240 pub struct ProfileReport {
241 pub stats: HashMap<String, OperationStats>,
242 pub config: ReportConfig,
243 }
244
245 impl ProfileReport {
246 pub fn generate() -> Self {
247 Self {
248 stats: HashMap::new(),
249 config: ReportConfig::default(),
250 }
251 }
252
253 pub fn generate_with_config(_config: ReportConfig) -> Self {
254 Self {
255 stats: HashMap::new(),
256 config: ReportConfig::default(),
257 }
258 }
259
260 pub fn quick_summary(&self) -> String {
261 String::new()
262 }
263
264 pub fn summary_stats(&self) -> SummaryStats {
265 SummaryStats::default()
266 }
267
268 pub fn to_string(&self) -> String {
269 String::new()
270 }
271
272 pub fn top_operations_by(
273 &self,
274 _metric: SortMetric,
275 _limit: usize,
276 ) -> Vec<(String, OperationStats)> {
277 Vec::new()
278 }
279 }
280
281 impl std::fmt::Debug for ProfileReport {
282 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
283 write!(f, "")
284 }
285 }
286
287 pub struct ReportBuilder {
288 _phantom: std::marker::PhantomData<()>,
289 }
290
291 impl Default for ReportBuilder {
292 fn default() -> Self {
293 Self::new()
294 }
295 }
296
297 impl ReportBuilder {
298 pub fn new() -> Self {
299 Self {
300 _phantom: std::marker::PhantomData,
301 }
302 }
303
304 pub fn group_by_category(self, _enabled: bool) -> Self {
305 self
306 }
307 pub fn include_percentiles(self, _enabled: bool) -> Self {
308 self
309 }
310 pub fn time_format(self, _format: TimeFormat) -> Self {
311 self
312 }
313 pub fn sort_by_time(self, _enabled: bool) -> Self {
314 self
315 }
316 pub fn build(self) -> ProfileReport {
317 ProfileReport::generate()
318 }
319 }
320}
321
322#[cfg(not(feature = "full"))]
323pub mod timer {
324 use crate::operation::Operation;
325
326 pub struct ProfileTimer<'a> {
327 _operation: &'a dyn Operation,
328 }
329
330 impl<'a> ProfileTimer<'a> {
331 pub fn new(operation: &'a dyn Operation) -> Self {
332 Self {
333 _operation: operation,
334 }
335 }
336 }
337
338 impl<'a> Drop for ProfileTimer<'a> {
339 fn drop(&mut self) {
340 }
342 }
343
344 pub struct ProfileTimerAsync<'a> {
345 _operation: &'a dyn Operation,
346 }
347
348 impl<'a> ProfileTimerAsync<'a> {
349 pub fn new(operation: &'a dyn Operation) -> Self {
350 Self {
351 _operation: operation,
352 }
353 }
354
355 pub async fn run<F, R>(self, fut: F) -> R
356 where
357 F: std::future::Future<Output = R>,
358 {
359 fut.await
361 }
362 }
363
364 pub struct PausableTimer<'a> {
365 _operation: &'a dyn Operation,
366 }
367
368 impl<'a> PausableTimer<'a> {
369 pub fn new(operation: &'a dyn Operation) -> Self {
370 Self {
371 _operation: operation,
372 }
373 }
374
375 pub fn new_paused(operation: &'a dyn Operation) -> Self {
376 Self {
377 _operation: operation,
378 }
379 }
380
381 pub fn pause(&mut self) {}
382
383 pub fn resume(&mut self) {}
384
385 pub fn total_elapsed(&self) -> std::time::Duration {
386 std::time::Duration::ZERO
387 }
388
389 pub fn total_elapsed_micros(&self) -> u64 {
390 0
391 }
392
393 pub fn total_elapsed_millis(&self) -> u64 {
394 0
395 }
396
397 pub fn is_running(&self) -> bool {
398 false
399 }
400
401 pub fn operation(&self) -> &dyn Operation {
402 self._operation
403 }
404
405 pub fn record(&mut self) {}
406
407 pub fn stop(self) -> std::time::Duration {
408 std::time::Duration::ZERO
409 }
410
411 pub fn stop_and_record(self) -> std::time::Duration {
412 std::time::Duration::ZERO
413 }
414
415 pub fn reset(&mut self) {}
416
417 pub fn reset_paused(&mut self) {}
418 }
419
420 impl<'a> Drop for PausableTimer<'a> {
421 fn drop(&mut self) {}
422 }
423}
424
425#[doc(inline)]
427pub use category::{Category, NoCategory};
428#[doc(inline)]
429pub use collector::{OperationStats, ProfileCollector, SummaryStats};
430#[doc(inline)]
431pub use operation::Operation;
432#[doc(inline)]
433pub use timer::{PausableTimer, ProfileTimer, ProfileTimerAsync};
434
435#[cfg(feature = "full")]
437#[doc(inline)]
438pub use category::DefaultCategory;
439#[cfg(feature = "full")]
440#[doc(inline)]
441pub use reporter::{
442 Percentile, ProfileReport, ReportBuilder, ReportConfig, SortMetric, TimeFormat,
443};
444
445pub use quantum_pulse_macros::Operation as ProfileOp;
448
449#[macro_export]
473macro_rules! profile {
474 ($operation:expr, $code:block) => {{
475 let _timer = $crate::ProfileTimer::new(&$operation);
476 $code
477 }};
478}
479
480#[macro_export]
511#[doc(alias = "await")]
512macro_rules! profile_async {
513 ($operation:expr, $code:expr) => {
514 $crate::ProfileTimerAsync::new(&$operation).run($code)
517 };
518}
519
520#[macro_export]
543macro_rules! scoped_timer {
544 ($operation:expr) => {
545 let _timer = $crate::ProfileTimer::new(&$operation);
546 };
547}
548
549#[macro_export]
594macro_rules! pause {
595 () => {
596 $crate::ProfileCollector::pause();
597 };
598}
599
600#[macro_export]
637macro_rules! unpause {
638 () => {
639 $crate::ProfileCollector::unpause();
640 };
641}
642
643#[cfg(test)]
644mod tests {
645 use super::*;
646
647 #[test]
648 #[cfg(feature = "full")]
649 fn test_basic_profiling() {
650 #[derive(Debug)]
651 enum TestOperation {
652 Test,
653 }
654
655 impl Operation for TestOperation {
656 fn to_str(&self) -> String {
657 "test_operation".to_string()
658 }
659 }
660
661 ProfileCollector::clear_all();
662
663 let op = TestOperation::Test;
664 let result = profile!(op, {
665 std::thread::sleep(std::time::Duration::from_millis(1));
666 42
667 });
668
669 assert_eq!(result, 42);
670 assert!(ProfileCollector::has_data());
671
672 let stats = ProfileCollector::get_stats("::test_operation");
673 assert!(stats.is_some());
674 assert_eq!(stats.unwrap().count, 1);
675 }
676
677 #[test]
678 fn test_custom_category() {
679 #[derive(Debug)]
680 struct TestCategory;
681
682 impl Category for TestCategory {
683 fn get_name(&self) -> &str {
684 "Test"
685 }
686 fn get_description(&self) -> &str {
687 "Test category"
688 }
689 }
690
691 #[derive(Debug)]
692 struct TestOp;
693
694 impl Operation for TestOp {
695 fn get_category(&self) -> &dyn Category {
696 &TestCategory
697 }
698 }
699
700 ProfileCollector::clear_all();
701
702 let op = TestOp;
703 profile!(op, {
704 std::thread::sleep(std::time::Duration::from_millis(1));
705 });
706
707 assert!(ProfileCollector::total_operations() > 0);
709 }
710
711 #[tokio::test]
712 #[cfg(feature = "full")]
713 async fn test_async_profiling() {
714 #[derive(Debug)]
715 enum TestOperation {
716 AsyncTest,
717 }
718
719 impl Operation for TestOperation {
720 fn to_str(&self) -> String {
721 "async_test".to_string()
722 }
723 }
724
725 ProfileCollector::clear_all();
726
727 let op = TestOperation::AsyncTest;
728 let result = profile_async!(op, async {
729 tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
730 "async_result"
731 })
732 .await;
733
734 assert_eq!(result, "async_result");
735 assert!(ProfileCollector::has_data());
736 }
737
738 #[test]
739 #[cfg(feature = "full")]
740 fn test_profile_macro() {
741 #[derive(Debug)]
742 enum TestOperation {
743 MacroTest,
744 }
745
746 impl Operation for TestOperation {
747 fn to_str(&self) -> String {
748 "macro_test".to_string()
749 }
750 }
751
752 ProfileCollector::clear_all();
753
754 let op = TestOperation::MacroTest;
755 let result = profile!(op, {
756 std::thread::sleep(std::time::Duration::from_millis(1));
757 100
758 });
759
760 assert_eq!(result, 100);
761 assert!(ProfileCollector::get_stats("::macro_test").is_some());
762 }
763
764 #[test]
765 #[cfg(feature = "full")]
766 fn test_scoped_timer() {
767 ProfileCollector::clear_all();
768
769 #[derive(Debug)]
770 enum TestOperation {
771 ScopedTest,
772 }
773
774 impl Operation for TestOperation {
775 fn to_str(&self) -> String {
776 "scoped_test".to_string()
777 }
778 }
779
780 let op = TestOperation::ScopedTest;
781 {
782 scoped_timer!(op);
783 std::thread::sleep(std::time::Duration::from_millis(1));
784 }
785
786 assert!(ProfileCollector::has_data());
787 let stats = ProfileCollector::get_stats("::scoped_test");
788 assert!(stats.is_some());
789 }
790
791 #[test]
792 #[cfg(feature = "full")]
793 fn test_pause_unpause() {
794 ProfileCollector::clear_all();
795
796 #[derive(Debug)]
797 enum TestOperation {
798 PauseTest,
799 }
800
801 impl Operation for TestOperation {
802 fn to_str(&self) -> String {
803 "pause_test".to_string()
804 }
805 }
806
807 let op = TestOperation::PauseTest;
808
809 unpause!();
811 assert!(!ProfileCollector::is_paused());
812
813 profile!(op, {
815 std::thread::sleep(std::time::Duration::from_millis(1));
816 });
817
818 let stats_before_pause = ProfileCollector::get_stats("::pause_test");
819 assert!(stats_before_pause.is_some());
820 let count_before = stats_before_pause.unwrap().count;
821
822 pause!();
824 assert!(ProfileCollector::is_paused());
825
826 profile!(op, {
828 std::thread::sleep(std::time::Duration::from_millis(1));
829 });
830
831 let stats_during_pause = ProfileCollector::get_stats("::pause_test");
832 assert!(stats_during_pause.is_some());
833 assert_eq!(stats_during_pause.unwrap().count, count_before);
834
835 unpause!();
837 assert!(!ProfileCollector::is_paused());
838
839 profile!(op, {
841 std::thread::sleep(std::time::Duration::from_millis(1));
842 });
843
844 let stats_after_unpause = ProfileCollector::get_stats("::pause_test");
845 assert!(stats_after_unpause.is_some());
846 assert_eq!(stats_after_unpause.unwrap().count, count_before + 1);
847 }
848}