altius_benchtools/profiler.rs
1//! A high-performance profiler for tracing RPC server execution with multi-threaded support.
2//!
3//! # Overview
4//!
5//! The profiler module provides a comprehensive solution for tracing and profiling RPC server execution,
6//! with support for multi-threaded environments, detailed event tracking, and flexible output formats.
7//!
8//! # Features
9//!
10//! - Task timing with precise start/end markers
11//! - Thread-safe profiling in concurrent environments
12//! - Rich event annotation system
13//! - Multiple output formats (JSON, ZIP)
14//! - Special handling for transaction and commit events
15//! - Global singleton instance with thread-safe access
16//!
17//! # Examples
18//!
19//! ```rust
20//! use altius_benchtools::profiler;
21//!
22//! // Start timing a task
23//! profiler::start("my_task");
24//!
25//! // Add notes to the current task
26//! profiler::note_str("my_task", "operation", "database_query");
27//! profiler::note_str("my_task", "query_type", "SELECT");
28//!
29//! // Record timing points
30//! profiler::note_time("my_task", "query_start");
31//!
32//! // End timing the task
33//! profiler::end("my_task");
34//!
35//! // Export results
36//! profiler::dump_json("profile_output.json");
37//! ```
38//!
39//! # Multi-threaded Usage
40//!
41//! The profiler is thread-safe and can be used across multiple threads:
42//!
43//! ```rust
44//! use std::thread;
45//! use altius_benchtools::profiler;
46//!
47//! let handle = thread::spawn(|| {
48//! profiler::start("worker_task");
49//! profiler::note_str("worker_task", "thread_info", "worker_1");
50//! // ... perform work ...
51//! profiler::end("worker_task");
52//! });
53//! ```
54//!
55//! # Output Format
56//!
57//! The profiler generates a structured JSON output containing:
58//!
59//! - Task timing information (start time, duration)
60//! - Thread identification
61//! - Custom annotations and notes
62//! - Special event types (transactions, commits)
63//!
64//! Example output structure:
65//! ```json
66//! {
67//! "title": {},
68//! "details": [
69//! [
70//! {
71//! "type": "transaction",
72//! "tx": "task_name",
73//! "runtime": 1234567,
74//! "start": 1000000,
75//! "end": 2234567,
76//! "status": "success",
77//! "detail": {
78//! "operation": "database_query",
79//! "query_type": "SELECT"
80//! }
81//! }
82//! ]
83//! ]
84//! }
85//! ```
86//!
87//! # Note on Thread Safety
88//!
89//! The profiler uses a global singleton instance protected by a mutex to ensure thread safety.
90//! All operations are atomic and can be safely performed from any thread.
91
92use once_cell::sync::Lazy;
93use serde_json::{json, Map, Value};
94use std::{
95 collections::HashMap,
96 fs::File,
97 io::{BufWriter, Write},
98 sync::Mutex,
99 thread::current,
100 time::Instant,
101};
102use zip::{write::FileOptions, CompressionMethod, ZipWriter};
103
104/// Global profiler instance initialized lazily
105static PROFILER: Lazy<Mutex<Profiler>> = Lazy::new(|| {
106 Mutex::new(Profiler {
107 genesis: Instant::now(),
108 thread_tasks: HashMap::new(),
109 global_tasks: HashMap::new(),
110 })
111});
112
113#[derive(Debug)]
114struct Profiler {
115 genesis: Instant,
116 thread_tasks: HashMap<
117 String, // thread id
118 HashMap<
119 String, // task name
120 Vec<(
121 u128, // start time
122 Option<u128>, // end time (optional)
123 Map<String, Value>, // other description
124 )>,
125 >,
126 >,
127 global_tasks: HashMap<
128 String, // task name
129 (u128, bool), // occurrence count & is ended
130 >,
131}
132
133impl Profiler {
134 /// Returns a reference to the global profiler instance
135 fn global() -> &'static Mutex<Profiler> {
136 &PROFILER
137 }
138
139 /// Gets the current thread's name as a string
140 fn get_current_thread_name() -> String {
141 let thread_id = current().id();
142 format!("{:?}", thread_id)
143 }
144
145 /// Inserts a new thread into the profiler if it doesn't exist
146 /// Returns true if the thread was newly inserted, false if it already existed
147 fn insert_thread(&mut self, thread: &str) -> bool {
148 let non_exist = !self.thread_tasks.contains_key(thread);
149 if non_exist {
150 self.thread_tasks.insert(thread.to_string(), HashMap::new());
151 }
152 non_exist
153 }
154
155 /// Inserts a new task for a specific thread if it doesn't exist
156 /// Returns true if the task was newly inserted, false if it already existed
157 fn insert_thread_task(&mut self, task: &str, thread: &str) -> bool {
158 self.insert_thread(thread);
159 let thread_tasks = self.thread_tasks.get_mut(thread).unwrap();
160 let non_exist = !thread_tasks.contains_key(task);
161 if non_exist {
162 thread_tasks.insert(task.to_string(), vec![]);
163 }
164 non_exist
165 }
166
167 /// Inserts a new task for the current thread if it doesn't exist
168 /// Returns true if the task was newly inserted, false if it already existed
169 fn insert_current_thread_task(&mut self, task: &str) -> bool {
170 self.insert_thread_task(task, &Profiler::get_current_thread_name())
171 }
172
173 /// Gets a reference to the events vector for a specific task and thread
174 /// Panics if either the thread or task don't exist
175 fn must_get(&self, task: &str, thread: &str) -> &Vec<(u128, Option<u128>, Map<String, Value>)> {
176 self.thread_tasks.get(thread).unwrap().get(task).unwrap()
177 }
178
179 /// Gets a reference to the events vector for a specific task in the current thread
180 /// Panics if either the thread or task don't exist
181 fn must_get_current(&self, task: &str) -> &Vec<(u128, Option<u128>, Map<String, Value>)> {
182 self.thread_tasks
183 .get(&Profiler::get_current_thread_name())
184 .unwrap()
185 .get(task)
186 .unwrap()
187 }
188
189 /// Gets a mutable reference to the events vector for a specific task and thread
190 /// Panics if either the thread or task don't exist
191 fn must_get_mut(
192 &mut self,
193 task: &str,
194 thread: &str,
195 ) -> &mut Vec<(u128, Option<u128>, Map<String, Value>)> {
196 self.thread_tasks
197 .get_mut(thread)
198 .unwrap()
199 .get_mut(task)
200 .unwrap()
201 }
202
203 /// Gets a mutable reference to the events vector for a specific task in the current thread
204 /// Panics if either the thread or task don't exist
205 fn must_get_mut_current(
206 &mut self,
207 task: &str,
208 ) -> &mut Vec<(u128, Option<u128>, Map<String, Value>)> {
209 self.thread_tasks
210 .get_mut(&Profiler::get_current_thread_name())
211 .unwrap()
212 .get_mut(task)
213 .unwrap()
214 }
215
216 /// Clears all profiling data from the profiler
217 fn clear(&mut self) {
218 self.thread_tasks.clear();
219 }
220}
221
222/// Returns the genesis time when the profiler was initialized.
223///
224/// This timestamp serves as the reference point for all timing measurements
225/// in the profiler. All durations are calculated relative to this time.
226///
227/// # Returns
228///
229/// * `Instant` - The initialization timestamp of the profiler
230pub fn get_genesis() -> Instant {
231 let profiler = Profiler::global().lock().unwrap();
232 profiler.genesis
233}
234
235/// Starts timing a new task in the current thread.
236///
237/// This function begins tracking a new task's execution time. Each task must be ended
238/// with a corresponding call to [`end()`]. Multiple tasks can be tracked simultaneously,
239/// but nested tasks of the same name are not supported.
240///
241/// # Arguments
242///
243/// * `task` - A string identifier for the task to be timed
244///
245/// # Panics
246///
247/// * Panics if the last event for this task name is not ended (i.e., if you try to start
248/// a task that was already started but not ended)
249///
250/// # Examples
251///
252/// ```rust
253/// use altius_benchtools::profiler;
254///
255/// profiler::start("database_query");
256/// // ... perform database operation ...
257/// profiler::end("database_query");
258/// ```
259pub fn start(task: &str) {
260 let mut profiler = Profiler::global().lock().unwrap();
261 let genesis = profiler.genesis;
262 match profiler.insert_current_thread_task(task) {
263 false => assert!(
264 profiler.must_get_current(task).last().unwrap().1.is_some(),
265 "the last event must be end"
266 ),
267 true => (),
268 };
269 profiler.must_get_mut_current(task).push((
270 Instant::now().duration_since(genesis).as_nanos(),
271 None,
272 Map::new(),
273 ));
274}
275
276/// Starts timing a new task that may be called multiple times with the same name.
277///
278/// This function is specifically designed for tasks that need to be executed multiple times
279/// with the same base name. It automatically appends an index to the task name to differentiate
280/// between multiple instances.
281///
282/// # Arguments
283///
284/// * `base_task` - The base name for the task. The actual task name will be `{base_task}-[{index}]`
285///
286/// # Panics
287///
288/// * Panics if the last event for this task name is not ended
289///
290/// # Thread Safety
291///
292/// * This function only works in the main thread
293///
294/// # Examples
295///
296/// ```rust
297/// use altius_benchtools::profiler;
298///
299/// // First instance
300/// profiler::start_multi("batch_process");
301/// // ... process batch 1 ...
302/// profiler::end_multi("batch_process");
303///
304/// // Second instance
305/// profiler::start_multi("batch_process");
306/// // ... process batch 2 ...
307/// profiler::end_multi("batch_process");
308/// ```
309pub fn start_multi(base_task: &str) {
310 let mut profiler = Profiler::global().lock().unwrap();
311 let genesis = profiler.genesis;
312 let count = match profiler.global_tasks.get_mut(base_task) {
313 None => {
314 profiler
315 .global_tasks
316 .insert(base_task.to_string(), (1, false));
317 0
318 }
319 Some((count, is_ended)) => {
320 assert!(*is_ended, "the last event must be end");
321 *count += 1;
322 *is_ended = false;
323 *count - 1
324 }
325 };
326 let task = &format!("{}-[{}]", base_task, count);
327 match profiler.insert_thread_task(task, "main") {
328 false => assert!(
329 profiler.must_get(task, "main").last().unwrap().1.is_some(),
330 "the last event must be end"
331 ),
332 true => (),
333 };
334 profiler.must_get_mut(task, "main").push((
335 Instant::now().duration_since(genesis).as_nanos(),
336 None,
337 Map::new(),
338 ));
339}
340
341/// Ends timing for a task in the current thread.
342///
343/// This function stops tracking a task's execution time and records its duration.
344/// It must be called after a corresponding [`start()`] call for the same task.
345///
346/// # Arguments
347///
348/// * `task` - The string identifier of the task to end
349///
350/// # Panics
351///
352/// * Panics if the last event for this task was not started (i.e., if you try to end
353/// a task that wasn't started)
354///
355/// # Examples
356///
357/// ```rust
358/// use altius_benchtools::profiler;
359///
360/// profiler::start("api_request");
361/// // ... perform API request ...
362/// profiler::end("api_request");
363/// ```
364pub fn end(task: &str) {
365 let mut profiler = Profiler::global().lock().unwrap();
366 assert!(
367 profiler.must_get_current(task).last().unwrap().1.is_none(),
368 "the last event must be start"
369 );
370 profiler.must_get_mut_current(task).last_mut().unwrap().1 =
371 Some(Instant::now().duration_since(profiler.genesis).as_nanos());
372}
373
374/// Ends timing for a task that was called multiple times.
375///
376/// This function ends timing for a task started with [`start_multi()`]. It must be called
377/// after a corresponding start_multi call with the same base task name.
378///
379/// # Arguments
380///
381/// * `base_task` - The base name of the task to end (same as used in start_multi)
382///
383/// # Panics
384///
385/// * Panics if the last event for this task was not started
386/// * Panics if called from a non-main thread
387///
388/// # Thread Safety
389///
390/// * This function only works in the main thread
391///
392/// # Examples
393///
394/// ```rust
395/// use altius_benchtools::profiler;
396///
397/// // First batch
398/// profiler::start_multi("batch_process");
399/// // ... process batch 1 ...
400/// profiler::end_multi("batch_process"); // Ends "batch_process-[0]"
401///
402/// // Second batch
403/// profiler::start_multi("batch_process");
404/// // ... process batch 2 ...
405/// profiler::end_multi("batch_process"); // Ends "batch_process-[1]"
406/// ```
407pub fn end_multi(base_task: &str) {
408 let mut profiler = Profiler::global().lock().unwrap();
409 let (count, is_ended) = profiler.global_tasks.get_mut(base_task).unwrap();
410 assert!(!*is_ended, "the last event must not be end");
411 *is_ended = true;
412 let task = &format!("{}-[{}]", base_task, *count - 1);
413 assert!(
414 profiler.must_get(task, "main").last().unwrap().1.is_none(),
415 "the last event must be start"
416 );
417 profiler.must_get_mut(task, "main").last_mut().unwrap().1 =
418 Some(Instant::now().duration_since(profiler.genesis).as_nanos());
419}
420
421/// Adds a key-value note to the last event of a task.
422///
423/// This function allows you to annotate a task with additional metadata in the form
424/// of key-value pairs. The value can be any valid JSON value.
425///
426/// # Arguments
427///
428/// * `task` - The string identifier of the task to annotate
429/// * `key` - The key for the metadata entry
430/// * `value` - The value to associate with the key (any valid JSON value)
431///
432/// # Panics
433///
434/// * Panics if the last event was not started (i.e., if you try to add a note to
435/// a task that wasn't started or was already ended)
436///
437/// # Examples
438///
439/// ```rust
440/// use altius_benchtools::profiler;
441/// use serde_json::json;
442///
443/// profiler::start("http_request");
444/// profiler::note("http_request", "method", json!("POST"));
445/// profiler::note("http_request", "headers", json!({
446/// "Content-Type": "application/json",
447/// "Authorization": "Bearer token"
448/// }));
449/// // ... perform request ...
450/// profiler::end("http_request");
451/// ```
452pub fn note(task: &str, key: &str, value: Value) {
453 let mut profiler = Profiler::global().lock().unwrap();
454 assert!(
455 profiler.must_get_current(task).last().unwrap().1.is_none(),
456 "the last event must be start"
457 );
458 profiler
459 .must_get_mut_current(task)
460 .last_mut()
461 .unwrap()
462 .2
463 .insert(key.to_string(), value);
464}
465
466/// Adds a string key-value note to the last event of a task.
467///
468/// This is a convenience wrapper around [`note()`] that automatically converts
469/// the string value to a JSON string value.
470///
471/// # Arguments
472///
473/// * `task` - The string identifier of the task to annotate
474/// * `key` - The key for the metadata entry
475/// * `value` - The string value to associate with the key
476///
477/// # Panics
478///
479/// * Panics if the last event was not started
480///
481/// # Examples
482///
483/// ```rust
484/// use altius_benchtools::profiler;
485///
486/// profiler::start("request");
487/// profiler::note_str("request", "endpoint", "/api/v1/users");
488/// profiler::note_str("request", "method", "GET");
489/// // ... perform request ...
490/// profiler::end("request");
491/// ```
492pub fn note_str(task: &str, key: &str, value: &str) {
493 note(task, key, Value::String(value.to_string()));
494}
495
496/// Adds multiple key-value notes to the last event of a task.
497///
498/// This function allows you to add multiple annotations at once by providing
499/// a map of key-value pairs.
500///
501/// # Arguments
502///
503/// * `task` - The string identifier of the task to annotate
504/// * `description` - A mutable map containing the key-value pairs to add
505///
506/// # Panics
507///
508/// * Panics if the last event was not started
509///
510/// # Examples
511///
512/// ```rust
513/// use altius_benchtools::profiler;
514/// use serde_json::{Map, Value, json};
515///
516/// profiler::start("query");
517/// let mut desc = Map::new();
518/// desc.insert("table".to_string(), json!("users"));
519/// desc.insert("type".to_string(), json!("SELECT"));
520/// desc.insert("filter".to_string(), json!({"age": ">= 18"}));
521/// profiler::notes("query", &mut desc);
522/// // ... perform query ...
523/// profiler::end("query");
524/// ```
525pub fn notes(task: &str, description: &mut Map<String, Value>) {
526 let mut profiler = Profiler::global().lock().unwrap();
527 profiler
528 .must_get_mut_current(task)
529 .last_mut()
530 .unwrap()
531 .2
532 .append(description);
533}
534
535/// Adds the current time as a value for a key in the last event of a task.
536///
537/// This function records the current timestamp relative to the profiler's genesis time
538/// and adds it as a note to the task.
539///
540/// # Arguments
541///
542/// * `task` - The string identifier of the task to annotate
543/// * `key` - The key under which to store the timestamp
544///
545/// # Panics
546///
547/// * Panics if the last event was not started
548///
549/// # Examples
550///
551/// ```rust
552/// use altius_benchtools::profiler;
553///
554/// profiler::start("long_operation");
555/// // ... initial setup ...
556/// profiler::note_time("long_operation", "setup_complete");
557/// // ... main work ...
558/// profiler::note_time("long_operation", "work_complete");
559/// // ... cleanup ...
560/// profiler::end("long_operation");
561/// ```
562pub fn note_time(task: &str, key: &str) {
563 let mut profiler = Profiler::global().lock().unwrap();
564 let genesis = profiler.genesis;
565 assert!(
566 profiler.must_get_current(task).last().unwrap().1.is_none(),
567 "the last event must be start"
568 );
569 profiler
570 .must_get_mut_current(task)
571 .last_mut()
572 .unwrap()
573 .2
574 .insert(
575 key.to_string(),
576 (Instant::now().duration_since(genesis).as_nanos() as u64).into(),
577 );
578}
579
580/// Adds a string key-value note to the last event of a task that was called multiple times.
581///
582/// This function adds a string note to a task created with [`start_multi()`]. It must be used
583/// with the base task name (without the index suffix).
584///
585/// # Arguments
586///
587/// * `base_task` - The base name of the task (same as used in start_multi)
588/// * `key` - The key for the metadata entry
589/// * `value` - The string value to associate with the key
590///
591/// # Panics
592///
593/// * Panics if the last event was not started
594/// * Panics if called from a non-main thread
595///
596/// # Thread Safety
597///
598/// * This function only works in the main thread
599///
600/// # Examples
601///
602/// ```rust
603/// use altius_benchtools::profiler;
604///
605/// profiler::start_multi("batch_job");
606/// profiler::note_str_multi("batch_job", "status", "processing");
607/// // ... process batch ...
608/// profiler::note_str_multi("batch_job", "status", "completed");
609/// profiler::end_multi("batch_job");
610/// ```
611pub fn note_str_multi(base_task: &str, key: &str, value: &str) {
612 let mut profiler = Profiler::global().lock().unwrap();
613 let (count, is_ended) = profiler.global_tasks.get_mut(base_task).unwrap();
614 assert!(!*is_ended, "the last event must not be end");
615 let task = &format!("{}-[{}]", base_task, *count - 1);
616 assert!(
617 profiler.must_get(task, "main").last().unwrap().1.is_none(),
618 "the last event must be start"
619 );
620 profiler
621 .must_get_mut(task, "main")
622 .last_mut()
623 .unwrap()
624 .2
625 .insert(key.to_string(), Value::String(value.to_string()));
626}
627
628/// Adds a string key-value note to a task without the usual safety checks.
629///
630/// This is an unchecked version of [`note_str()`] that will create a new task entry
631/// if one doesn't exist. Use with caution as it bypasses the normal start/end task flow.
632///
633/// # Arguments
634///
635/// * `task` - The string identifier of the task to annotate
636/// * `key` - The key for the metadata entry
637/// * `value` - The string value to associate with the key
638///
639/// # Safety
640///
641/// This function is marked as unchecked because it:
642/// - Does not verify if the task exists
643/// - Creates a new task entry if none exists
644/// - Does not enforce the normal start/end task flow
645///
646/// # Examples
647///
648/// ```rust
649/// use altius_benchtools::profiler;
650///
651/// // Note: This bypasses normal task flow - use with caution
652/// profiler::note_str_unchecked("background_task", "status", "running");
653/// ```
654pub fn note_str_unchecked(task: &str, key: &str, value: &str) {
655 let mut profiler = Profiler::global().lock().unwrap();
656 let genesis = profiler.genesis;
657 match profiler.insert_thread_task(task, "main") {
658 true => profiler.must_get_mut(task, "main").push((
659 Instant::now().duration_since(genesis).as_nanos(),
660 None,
661 Map::new(),
662 )),
663 false => (),
664 };
665 profiler
666 .must_get_mut(task, "main")
667 .last_mut()
668 .unwrap()
669 .2
670 .insert(key.to_string(), Value::String(value.to_string()));
671}
672
673/// Clears all profiling data from the profiler.
674///
675/// This function removes all recorded tasks, events, and their associated metadata
676/// from the profiler. The genesis time is preserved.
677///
678/// # Examples
679///
680/// ```rust
681/// use altius_benchtools::profiler;
682///
683/// // After some profiling...
684/// profiler::clear(); // Reset profiler state
685/// ```
686pub fn clear() {
687 let mut profiler = Profiler::global().lock().unwrap();
688 profiler.clear();
689}
690
691/// Dumps the profiler data as a JSON string.
692///
693/// This function exports all profiling data in a structured JSON format. The output
694/// includes timing information, thread identification, custom annotations, and special
695/// event types (transactions, commits).
696///
697/// # Returns
698///
699/// * `String` - A pretty-printed JSON string containing all profiling data
700///
701/// # Examples
702///
703/// ```rust
704/// use altius_benchtools::profiler;
705///
706/// // ... perform profiling ...
707///
708/// let json_data = profiler::dump();
709/// println!("Profile data: {}", json_data);
710/// ```
711///
712/// # Output Format
713///
714/// The output JSON has the following structure:
715/// ```json
716/// {
717/// "title": {},
718/// "details": [
719/// [
720/// {
721/// "type": "transaction",
722/// "tx": "task_name",
723/// "runtime": 1234567,
724/// "start": 1000000,
725/// "end": 2234567,
726/// "status": "success",
727/// "detail": { ... }
728/// }
729/// ]
730/// ]
731/// }
732/// ```
733pub fn dump() -> String {
734 let profiler = Profiler::global().lock().unwrap();
735 let now = Instant::now().duration_since(profiler.genesis).as_nanos();
736
737 let mut output_frontend = Value::Array(vec![]);
738
739 for (_thread_name, thread_events) in &profiler.thread_tasks {
740 let mut detail = vec![];
741 for (name, thread_tasks) in thread_events {
742 for event in thread_tasks {
743 let (start, end_opt, description) = event;
744 let duration = end_opt.unwrap_or(now) - start;
745
746 match description.get("type") {
747 Some(Value::String(type_str)) => match type_str.as_str() {
748 "transaction" => detail.push(json!({
749 "type": "transaction",
750 "tx": name,
751 "runtime": duration,
752 "start": start,
753 "end": end_opt,
754 // "status": description.get("status").unwrap().as_str().unwrap_or("unknown"),
755 "status": match description.get("status") {
756 Some(value) => value.as_str().unwrap_or("unknown"),
757 None => "unknown",
758 },
759 "detail": description,
760 })),
761 "commit" => detail.push(json!({
762 "type": "commit",
763 "tx": match description.get("tx") {
764 Some(value) => value.as_str().unwrap_or("unknown"),
765 None => "unknown",
766 },
767 "runtime": duration,
768 "start": start,
769 "end": end_opt,
770 "detail": description,
771 })),
772 other_type => detail.push(json!({
773 "type": other_type,
774 "name": name,
775 "runtime": duration,
776 "start": start,
777 "end": end_opt,
778 "detail": description,
779 })),
780 },
781 _ => detail.push(json!({
782 "type": "other",
783 "name": name,
784 "runtime": duration,
785 "start": start,
786 "end": end_opt,
787 "detail": description,
788 })),
789 }
790 }
791 }
792 output_frontend
793 .as_array_mut()
794 .unwrap()
795 .push(Value::Array(detail));
796 }
797
798 serde_json::to_string_pretty(&output_frontend).unwrap()
799}
800
801/// Dumps the profiler data to a JSON file at the specified path.
802///
803/// This function writes all profiling data to a file in a pretty-printed JSON format.
804/// It's a convenience wrapper around [`dump()`] that handles file I/O.
805///
806/// # Arguments
807///
808/// * `output_path` - The path where the JSON file should be written
809///
810/// # Panics
811///
812/// * Panics if the file cannot be created or written to
813///
814/// # Examples
815///
816/// ```rust
817/// use altius_benchtools::profiler;
818///
819/// // After some profiling...
820/// profiler::dump_json("profile_results.json");
821/// ```
822pub fn dump_json(output_path: &str) {
823 let result_json = dump();
824 let mut file = File::create(output_path).unwrap();
825 file.write_all(result_json.as_bytes()).unwrap();
826}
827
828/// Dumps the profiler data to a ZIP file containing a JSON file.
829///
830/// This function exports all profiling data to a compressed ZIP file containing
831/// a JSON file. The ZIP file will contain a single JSON file with the same base name.
832/// This is useful for storing large profiling datasets efficiently.
833///
834/// # Arguments
835///
836/// * `output_name` - The base name for the output files (without extension)
837///
838/// # Panics
839///
840/// * Panics if the ZIP file cannot be created or written to
841///
842/// # Examples
843///
844/// ```rust
845/// use altius_benchtools::profiler;
846///
847/// // After some profiling...
848/// profiler::dump_zip("profile_results");
849/// // Creates profile_results.zip containing profile_results.json
850/// ```
851pub fn dump_zip(output_name: &str) {
852 let result_json = dump();
853 let file = File::create(output_name.to_string() + ".zip").unwrap();
854 let mut zip = ZipWriter::new(BufWriter::new(file));
855 let options = FileOptions::<()>::default().compression_method(CompressionMethod::Deflated);
856 zip.start_file(output_name.to_string() + ".json", options)
857 .unwrap();
858 zip.write_all(result_json.as_bytes()).unwrap();
859 zip.finish().unwrap();
860}
861
862/// Prints the current state of the profiler for debugging purposes.
863///
864/// This function prints a debug representation of the entire profiler state
865/// to stdout. This is primarily intended for development and debugging use.
866///
867/// # Examples
868///
869/// ```rust
870/// use altius_benchtools::profiler;
871///
872/// // After some profiling...
873/// profiler::debug_print(); // Prints internal profiler state
874/// ```
875pub fn debug_print() {
876 println!("Profiler: {:?}", PROFILER);
877}