1#[cfg(feature = "mem-probe")]
11use embassy_time::Instant;
12#[cfg(feature = "mem-probe")]
13use portable_atomic::{AtomicU64, Ordering};
14
15#[macro_export]
17macro_rules! bench_emit {
18 ($($arg:tt)*) => {
19 ::log::info!("@BENCH {}", ::core::format_args!($($arg)*))
20 };
21}
22
23#[derive(Clone, Copy)]
25pub enum Checkpoint {
26 Boot,
28 PeripheralsReady,
30 WifiUp,
32 TcpListening,
34 TcpAccept,
36 KexComplete,
38 AuthSuccess,
40 ChannelOpen,
42}
43
44#[cfg(feature = "mem-probe")]
45impl Checkpoint {
46 const fn name(self) -> &'static str {
49 match self {
50 Self::Boot => "bench_boot",
51 Self::PeripheralsReady => "bench_peripherals_ready",
52 Self::WifiUp => "bench_wifi_up",
53 Self::TcpListening => "bench_tcp_listening",
54 Self::TcpAccept => "bench_tcp_accept",
55 Self::KexComplete => "bench_kex_complete",
56 Self::AuthSuccess => "bench_auth_success",
57 Self::ChannelOpen => "bench_channel_open",
58 }
59 }
60}
61
62#[cfg(feature = "mem-probe")]
64const ALL: [Checkpoint; 8] = [
65 Checkpoint::Boot,
66 Checkpoint::PeripheralsReady,
67 Checkpoint::WifiUp,
68 Checkpoint::TcpListening,
69 Checkpoint::TcpAccept,
70 Checkpoint::KexComplete,
71 Checkpoint::AuthSuccess,
72 Checkpoint::ChannelOpen,
73];
74
75#[cfg(feature = "mem-probe")]
78static T_US: [AtomicU64; ALL.len()] = [const { AtomicU64::new(0) }; ALL.len()];
79
80#[cfg(feature = "mem-probe")]
82pub fn checkpoint(c: Checkpoint) {
83 let t_us = Instant::now().as_micros();
84 bench_emit!("checkpoint={} t_us={t_us}", c.name());
85 T_US[c as usize].store(t_us, Ordering::Relaxed);
86}
87
88#[cfg(not(feature = "mem-probe"))]
89pub fn checkpoint(_c: Checkpoint) {}
90
91#[cfg(feature = "mem-probe")]
95pub fn replay_checkpoints() {
96 for c in ALL {
97 if let t_us @ 1.. = T_US[c as usize].load(Ordering::Relaxed) {
99 crate::bench_emit!("checkpoint={} t_us={t_us}", c.name());
100 }
101 }
102}
103
104#[cfg(not(feature = "mem-probe"))]
105pub fn replay_checkpoints() {}
106
107#[cfg(feature = "mem-probe")]
108static KEX_START_TICKS: AtomicU64 = AtomicU64::new(0);
109
110#[cfg(feature = "mem-probe")]
112pub fn mark_kex_start() {
113 KEX_START_TICKS.store(Instant::now().as_ticks(), Ordering::Relaxed);
114}
115
116#[cfg(not(feature = "mem-probe"))]
117pub fn mark_kex_start() {}
118
119#[cfg(feature = "mem-probe")]
121pub fn log_kex_elapsed(label: &str) {
122 let start_ticks = KEX_START_TICKS.swap(0, Ordering::Relaxed);
123 if start_ticks == 0 {
124 return;
125 }
126 let elapsed = Instant::from_ticks(start_ticks).elapsed();
127 bench_emit!("kex={label} elapsed_us={}", elapsed.as_micros());
128}
129
130#[cfg(not(feature = "mem-probe"))]
131pub fn log_kex_elapsed(_label: &str) {}