1use core::sync::atomic::{compiler_fence, Ordering};
16
17use crate::aggregator;
18use crate::alloc::{snapshot_alloc_counters, ReentrancyGuard};
19use crate::children;
20use crate::cpu_clock::cpu_now_ns;
21use crate::session::ProfileSession;
22use crate::time::read;
23use std::marker::PhantomData;
24
25pub struct Guard {
32 session: Option<&'static ProfileSession>,
34 saved_children_ns: u64,
35 name_id: u32,
36 cpu_time_enabled: bool,
37 cpu_start_ns: u64,
38 start_ns: u64,
39 alloc_count_start: u64,
40 alloc_bytes_start: u64,
41 free_count_start: u64,
42 free_bytes_start: u64,
43 _not_send: PhantomData<*const ()>,
44}
45
46#[inline(always)]
54pub fn enter(name_id: u32) -> Guard {
55 let session = match ProfileSession::get() {
56 Some(s) => s,
57 None => return Guard::inactive(),
58 };
59 let mut guard = Guard::create(session, name_id);
60 guard.stamp();
61 guard
62}
63
64impl Guard {
65 fn inactive() -> Self {
67 Self {
68 session: None,
69 saved_children_ns: 0,
70 name_id: 0,
71 cpu_time_enabled: false,
72 cpu_start_ns: 0,
73 start_ns: 0,
74 alloc_count_start: 0,
75 alloc_bytes_start: 0,
76 free_count_start: 0,
77 free_bytes_start: 0,
78 _not_send: PhantomData,
79 }
80 }
81
82 #[inline(never)]
88 fn create(session: &'static ProfileSession, name_id: u32) -> Self {
89 let _reentrancy = ReentrancyGuard::enter();
90 let saved_children_ns = children::save_and_zero();
91 let snap = snapshot_alloc_counters();
92 let cpu_start_ns = if session.cpu_time_enabled {
93 cpu_now_ns()
94 } else {
95 0
96 };
97 drop(_reentrancy);
98
99 Self {
100 session: Some(session),
101 saved_children_ns,
102 name_id,
103 cpu_time_enabled: session.cpu_time_enabled,
104 cpu_start_ns,
105 start_ns: 0,
106 alloc_count_start: snap.alloc_count,
107 alloc_bytes_start: snap.alloc_bytes,
108 free_count_start: snap.free_count,
109 free_bytes_start: snap.free_bytes,
110 _not_send: PhantomData,
111 }
112 }
113
114 #[inline(always)]
116 pub fn stamp(&mut self) {
117 compiler_fence(Ordering::SeqCst);
118 self.start_ns = read();
119 }
120}
121
122impl Drop for Guard {
123 #[inline(always)]
124 fn drop(&mut self) {
125 let end_ticks = read();
126 compiler_fence(Ordering::SeqCst);
127
128 let session = match self.session {
129 Some(s) => s,
130 None => return,
131 };
132 let _reentrancy = ReentrancyGuard::enter();
133 let cpu_end_ns = if self.cpu_time_enabled {
134 cpu_now_ns()
135 } else {
136 0
137 };
138 let snap_end = snapshot_alloc_counters();
139
140 let start_ns = session.calibration.now_ns(self.start_ns);
141 let end_ns = session.calibration.now_ns(end_ticks);
142 let inclusive_ns = end_ns.saturating_sub(start_ns);
143
144 let my_children_ns = children::current_children_ns();
145 let self_ns = inclusive_ns.saturating_sub(my_children_ns);
146 let cpu_self_ns = cpu_end_ns.saturating_sub(self.cpu_start_ns);
147
148 aggregator::aggregate(
149 self.name_id,
150 self_ns,
151 inclusive_ns,
152 cpu_self_ns,
153 snap_end.alloc_count.saturating_sub(self.alloc_count_start),
154 snap_end.alloc_bytes.saturating_sub(self.alloc_bytes_start),
155 snap_end.free_count.saturating_sub(self.free_count_start),
156 snap_end.free_bytes.saturating_sub(self.free_bytes_start),
157 &session.agg_registry,
158 );
159
160 children::restore_and_report(self.saved_children_ns, inclusive_ns);
162 }
163}