1use crate::cdk::api::performance_counter;
2use canic_cdk::candid::CandidType;
3use serde::{Deserialize, Serialize};
4use std::{cell::RefCell, collections::HashMap};
5
6thread_local! {
7 pub static PERF_LAST: RefCell<u64> = RefCell::new(performance_counter(1));
8 static PERF_TABLE: RefCell<HashMap<String, PerfSlot>> = RefCell::new(HashMap::new());
9}
10
11#[derive(Default)]
16struct PerfSlot {
17 count: u64,
18 total_instructions: u64,
19}
20
21impl PerfSlot {
22 const fn increment(&mut self, delta: u64) {
23 self.count = self.count.saturating_add(1);
24 self.total_instructions = self.total_instructions.saturating_add(delta);
25 }
26}
27
28#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
34pub struct PerfEntry {
35 pub label: String,
36 pub count: u64,
37 pub total_instructions: u64,
38}
39
40#[must_use]
42#[allow(clippy::missing_const_for_fn)]
43pub fn perf_counter() -> u64 {
44 #[cfg(target_arch = "wasm32")]
45 {
46 performance_counter(1)
47 }
48 #[cfg(not(target_arch = "wasm32"))]
49 {
50 0
51 }
52}
53
54pub fn record(label: &str, delta: u64) {
56 PERF_TABLE.with_borrow_mut(|table| {
57 table.entry(label.to_string()).or_default().increment(delta);
58 });
59}
60
61#[must_use]
63pub fn entries() -> Vec<PerfEntry> {
64 PERF_TABLE.with_borrow(|table| {
65 let mut entries: Vec<PerfEntry> = table
66 .iter()
67 .map(|(label, slot)| PerfEntry {
68 label: label.clone(),
69 count: slot.count,
70 total_instructions: slot.total_instructions,
71 })
72 .collect();
73
74 entries.sort_by(|a, b| a.label.cmp(&b.label));
75 entries
76 })
77}
78
79#[cfg(test)]
80pub fn reset() {
81 PERF_TABLE.with_borrow_mut(HashMap::clear);
82 PERF_LAST.with_borrow_mut(|last| *last = 0);
83}