1use rustc_hash::FxHashMap;
8
9#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
11pub enum WorkPhase {
12 Build,
14 Measure,
16 IntrinsicMeasure,
18 Place,
20 Transform,
22 Paint,
24}
25
26#[derive(Clone, Debug)]
28pub struct WorkExecution {
29 pub instance: u64,
31 pub phase: WorkPhase,
33 pub target: &'static str,
35}
36
37#[derive(Clone, Debug, Default)]
39pub struct FrameWork {
40 pub session: Option<crate::RuntimeSessionId>,
42 executions: Vec<WorkExecution>,
43}
44
45impl FrameWork {
46 pub fn executions(&self) -> &[WorkExecution] {
48 &self.executions
49 }
50
51 pub fn phase(&self, phase: WorkPhase) -> impl Iterator<Item = &WorkExecution> {
53 self.executions
54 .iter()
55 .filter(move |entry| entry.phase == phase)
56 }
57
58 pub fn count(&self, target: &str, phase: WorkPhase) -> usize {
60 self.phase(phase)
61 .filter(|entry| entry.target == target)
62 .count()
63 }
64
65 pub fn executed<T: 'static>(&self, phase: WorkPhase) -> usize {
67 self.count(std::any::type_name::<T>(), phase)
68 }
69
70 pub fn for_instance(&self, instance: u64, phase: WorkPhase) -> usize {
72 self.phase(phase)
73 .filter(|entry| entry.instance == instance)
74 .count()
75 }
76}
77
78#[derive(Clone, Debug, Default)]
80pub struct WorkTotals {
81 pub session: Option<crate::RuntimeSessionId>,
83 counts: FxHashMap<&'static str, [usize; 6]>,
84}
85
86impl WorkTotals {
87 pub fn count(&self, target: &str, phase: WorkPhase) -> usize {
89 self.counts
90 .get(target)
91 .map_or(0, |counts| counts[phase as usize])
92 }
93
94 #[cfg(any(
95 test,
96 feature = "testing",
97 all(feature = "headless", not(target_family = "wasm"))
98 ))]
99 pub(crate) fn record(&mut self, frame: &FrameWork) {
100 self.session = frame.session;
101 for entry in frame.executions() {
102 let count = &mut self.counts.entry(entry.target).or_default()[entry.phase as usize];
103 *count = count.checked_add(1).expect("work count exhausted");
104 }
105 }
106}
107
108#[cfg(any(
109 test,
110 feature = "testing",
111 all(feature = "headless", not(target_family = "wasm"))
112))]
113mod collector {
114 use std::cell::RefCell;
115
116 use super::{FrameWork, WorkExecution, WorkPhase};
117
118 #[derive(Default)]
119 struct Observer(RefCell<Option<FrameWork>>);
120
121 pub(crate) fn begin() {
122 crate::local_runtime_resource::<Observer>()
123 .0
124 .replace(Some(FrameWork {
125 session: crate::RuntimeSession::current().map(|session| session.identity()),
126 ..Default::default()
127 }));
128 }
129
130 pub(crate) fn finish() -> FrameWork {
131 crate::local_runtime_resource::<Observer>()
132 .0
133 .take()
134 .unwrap_or_default()
135 }
136
137 pub(crate) fn record(instance: u64, phase: WorkPhase, target: &'static str) {
138 if crate::RuntimeSession::current().is_none() {
139 return;
140 }
141 if let Some(frame) = crate::local_runtime_resource::<Observer>()
142 .0
143 .borrow_mut()
144 .as_mut()
145 {
146 frame.executions.push(WorkExecution {
147 instance,
148 phase,
149 target,
150 });
151 }
152 }
153
154 pub(crate) fn record_policy<T>(phase: WorkPhase) {
155 if crate::RuntimeSession::current().is_none() {
156 return;
157 }
158 let instance = crate::execution_context::with_execution_context(|context| {
159 context.instance_logic_id_stack.last().copied().unwrap_or(0)
160 });
161 record(instance, phase, std::any::type_name::<T>());
162 }
163}
164
165#[cfg(any(
166 test,
167 feature = "testing",
168 all(feature = "headless", not(target_family = "wasm"))
169))]
170pub(crate) use collector::{begin, finish, record, record_policy};
171
172#[cfg(not(any(
173 test,
174 feature = "testing",
175 all(feature = "headless", not(target_family = "wasm"))
176)))]
177pub(crate) fn record(_instance: u64, _phase: WorkPhase, _target: &'static str) {}
178
179#[cfg(not(any(
180 test,
181 feature = "testing",
182 all(feature = "headless", not(target_family = "wasm"))
183)))]
184pub(crate) fn record_policy<T>(_phase: WorkPhase) {}