1use super::op_driver::OpDriver;
3use super::op_driver::OpInflightStats;
4use super::ContextState;
5use crate::OpId;
6use crate::OpState;
7use crate::PromiseId;
8use crate::ResourceId;
9use bit_set::BitSet;
10use serde::Serialize;
11use serde::Serializer;
12use std::cell::Cell;
13use std::cell::RefCell;
14use std::collections::BTreeMap;
15use std::fmt::Display;
16use std::ops::Deref;
17use std::rc::Rc;
18
19type ActivityId = usize;
20
21const NO_TRACES: [BTreeMap<ActivityId, Rc<str>>;
23 RuntimeActivityType::MAX_TYPE as usize] = [
24 BTreeMap::new(),
25 BTreeMap::new(),
26 BTreeMap::new(),
27 BTreeMap::new(),
28];
29
30#[derive(Default)]
31pub struct RuntimeActivityTraces {
32 enabled: Cell<bool>,
33 traces: RefCell<
34 [BTreeMap<ActivityId, Rc<str>>; RuntimeActivityType::MAX_TYPE as usize],
35 >,
36}
37
38impl RuntimeActivityTraces {
39 pub(crate) fn set_enabled(&self, enabled: bool) {
40 self.enabled.set(enabled);
41 if !enabled {
42 *self.traces.borrow_mut() = Default::default();
43 }
44 }
45
46 pub(crate) fn submit(
47 &self,
48 activity_type: RuntimeActivityType,
49 id: ActivityId,
50 trace: &str,
51 ) {
52 debug_assert_ne!(
53 activity_type,
54 RuntimeActivityType::Interval,
55 "Use Timer for for timers and intervals"
56 );
57 self.traces.borrow_mut()[activity_type as usize].insert(id, trace.into());
58 }
59
60 pub(crate) fn complete(
61 &self,
62 activity_type: RuntimeActivityType,
63 id: ActivityId,
64 ) {
65 self.traces.borrow_mut()[activity_type as usize].remove(&id);
66 }
67
68 pub fn is_enabled(&self) -> bool {
69 self.enabled.get()
70 }
71
72 pub fn count(&self) -> usize {
73 self.traces.borrow().len()
74 }
75
76 pub fn get_all(
77 &self,
78 mut f: impl FnMut(RuntimeActivityType, ActivityId, &str),
79 ) {
80 let traces = self.traces.borrow();
81 for i in 0..RuntimeActivityType::MAX_TYPE {
82 for (key, value) in traces[i as usize].iter() {
83 f(RuntimeActivityType::from_u8(i), *key, value.as_ref())
84 }
85 }
86 }
87
88 pub fn capture(
89 &self,
90 ) -> [BTreeMap<ActivityId, Rc<str>>; RuntimeActivityType::MAX_TYPE as usize]
91 {
92 if self.is_enabled() {
93 self.traces.borrow().clone()
94 } else {
95 NO_TRACES
96 }
97 }
98
99 pub fn get<T>(
100 &self,
101 activity_type: RuntimeActivityType,
102 id: ActivityId,
103 f: impl FnOnce(Option<&str>) -> T,
104 ) -> T {
105 f(self.traces.borrow()[activity_type as u8 as usize]
106 .get(&id)
107 .map(|x| x.as_ref()))
108 }
109}
110
111#[derive(Clone)]
112pub struct RuntimeActivityStatsFactory {
113 pub(super) context_state: Rc<ContextState>,
114 pub(super) op_state: Rc<RefCell<OpState>>,
115}
116
117#[derive(Clone, Default, PartialEq, Eq)]
119pub struct RuntimeActivityStatsFilter {
120 include_timers: bool,
121 include_ops: bool,
122 include_resources: bool,
123 op_filter: BitSet,
124}
125
126impl RuntimeActivityStatsFilter {
127 pub fn all() -> Self {
128 RuntimeActivityStatsFilter {
129 include_ops: true,
130 include_resources: true,
131 include_timers: true,
132 op_filter: BitSet::default(),
133 }
134 }
135
136 pub fn with_ops(mut self) -> Self {
137 self.include_ops = true;
138 self
139 }
140
141 pub fn with_resources(mut self) -> Self {
142 self.include_resources = true;
143 self
144 }
145
146 pub fn with_timers(mut self) -> Self {
147 self.include_timers = true;
148 self
149 }
150
151 pub fn omit_op(mut self, op: OpId) -> Self {
152 self.op_filter.insert(op as _);
153 self
154 }
155
156 pub fn is_empty(&self) -> bool {
157 let Self {
159 include_ops,
160 include_resources,
161 include_timers,
162 op_filter: _,
163 } = self;
164 !(*include_ops) && !(*include_resources) && !(*include_timers)
165 }
166}
167
168impl RuntimeActivityStatsFactory {
169 pub fn capture(
171 self,
172 filter: &RuntimeActivityStatsFilter,
173 ) -> RuntimeActivityStats {
174 let resources = if filter.include_resources {
175 let res = &self.op_state.borrow().resource_table;
176 let mut resources = ResourceOpenStats {
177 resources: Vec::with_capacity(res.len()),
178 };
179 for resource in res.names() {
180 resources
181 .resources
182 .push((resource.0, resource.1.to_string()))
183 }
184 resources
185 } else {
186 ResourceOpenStats::default()
187 };
188
189 let timers = if filter.include_timers {
190 let timer_count = self.context_state.timers.len();
191 let mut timers = TimerStats {
192 timers: Vec::with_capacity(timer_count),
193 repeats: BitSet::with_capacity(timer_count),
194 };
195 for (timer_id, repeats) in &self.context_state.timers.iter() {
196 if repeats {
197 timers.repeats.insert(timers.timers.len());
198 }
199 timers.timers.push(timer_id as usize);
200 }
201 timers
202 } else {
203 TimerStats::default()
204 };
205
206 let (ops, activity_traces) = if filter.include_ops {
207 let ops = self.context_state.pending_ops.stats(&filter.op_filter);
208 let activity_traces = self.context_state.activity_traces.capture();
209 (ops, activity_traces)
210 } else {
211 (Default::default(), Default::default())
212 };
213
214 RuntimeActivityStats {
215 context_state: self.context_state.clone(),
216 ops,
217 activity_traces,
218 resources,
219 timers,
220 }
221 }
222}
223
224#[derive(Default)]
225pub struct ResourceOpenStats {
226 pub(super) resources: Vec<(u32, String)>,
227}
228
229#[derive(Default)]
230pub struct TimerStats {
231 pub(super) timers: Vec<usize>,
232 pub(super) repeats: BitSet,
235}
236
237pub struct RuntimeActivityStats {
240 context_state: Rc<ContextState>,
241 pub(super) ops: OpInflightStats,
242 pub(super) activity_traces: [BTreeMap<ActivityId, Rc<str>>; 4],
243 pub(super) resources: ResourceOpenStats,
244 pub(super) timers: TimerStats,
245}
246
247#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
249#[repr(transparent)]
250pub struct RuntimeActivityTrace(Rc<str>);
251
252impl Serialize for RuntimeActivityTrace {
253 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
254 where
255 S: Serializer,
256 {
257 self.0.as_ref().serialize(serializer)
258 }
259}
260
261impl Display for RuntimeActivityTrace {
262 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263 f.write_str(self.0.as_ref())
264 }
265}
266
267impl Deref for RuntimeActivityTrace {
268 type Target = str;
269 fn deref(&self) -> &Self::Target {
270 self.0.as_ref()
271 }
272}
273
274impl From<&Rc<str>> for RuntimeActivityTrace {
275 fn from(value: &Rc<str>) -> Self {
276 Self(value.clone())
277 }
278}
279
280#[derive(Debug, Serialize)]
282pub enum RuntimeActivity {
283 AsyncOp(PromiseId, Option<RuntimeActivityTrace>, &'static str),
285 Resource(ResourceId, Option<RuntimeActivityTrace>, String),
287 Timer(usize, Option<RuntimeActivityTrace>),
289 Interval(usize, Option<RuntimeActivityTrace>),
291}
292
293impl RuntimeActivity {
294 pub fn activity(&self) -> RuntimeActivityType {
295 match self {
296 Self::AsyncOp(..) => RuntimeActivityType::AsyncOp,
297 Self::Resource(..) => RuntimeActivityType::Resource,
298 Self::Timer(..) => RuntimeActivityType::Timer,
299 Self::Interval(..) => RuntimeActivityType::Interval,
300 }
301 }
302}
303
304#[derive(
306 Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
307)]
308#[repr(u8)]
309pub enum RuntimeActivityType {
310 AsyncOp,
311 Resource,
312 Timer,
313 Interval,
314}
315
316impl RuntimeActivityType {
317 const MAX_TYPE: u8 = 4;
318
319 pub(crate) fn from_u8(value: u8) -> Self {
320 match value {
321 0 => Self::AsyncOp,
322 1 => Self::Resource,
323 2 => Self::Timer,
324 3 => Self::Interval,
325 _ => unreachable!(),
326 }
327 }
328}
329
330impl RuntimeActivityStats {
331 fn trace_for(
332 &self,
333 activity_type: RuntimeActivityType,
334 id: ActivityId,
335 ) -> Option<RuntimeActivityTrace> {
336 debug_assert_ne!(
337 activity_type,
338 RuntimeActivityType::Interval,
339 "Use Timer for for timers and intervals"
340 );
341 self.activity_traces[activity_type as u8 as usize]
342 .get(&id)
343 .map(|x| x.into())
344 }
345
346 pub fn dump(&self) -> RuntimeActivitySnapshot {
349 let has_traces = !self.activity_traces.is_empty();
350 let mut v = Vec::with_capacity(
351 self.ops.ops.len()
352 + self.resources.resources.len()
353 + self.timers.timers.len(),
354 );
355 let ops = &self.context_state.op_ctxs;
356 if has_traces {
357 for op in self.ops.ops.iter() {
358 v.push(RuntimeActivity::AsyncOp(
359 op.0,
360 self.trace_for(RuntimeActivityType::AsyncOp, op.0 as _),
361 ops[op.1 as usize].decl.name,
362 ));
363 }
364 } else {
365 for op in self.ops.ops.iter() {
366 v.push(RuntimeActivity::AsyncOp(
367 op.0,
368 None,
369 ops[op.1 as usize].decl.name,
370 ));
371 }
372 }
373 for resource in self.resources.resources.iter() {
374 v.push(RuntimeActivity::Resource(
375 resource.0,
376 None,
377 resource.1.clone(),
378 ))
379 }
380 if has_traces {
381 for i in 0..self.timers.timers.len() {
382 let id = self.timers.timers[i];
383 if self.timers.repeats.contains(i) {
384 v.push(RuntimeActivity::Interval(
385 id,
386 self.trace_for(RuntimeActivityType::Timer, id),
387 ));
388 } else {
389 v.push(RuntimeActivity::Timer(
390 id,
391 self.trace_for(RuntimeActivityType::Timer, id),
392 ));
393 }
394 }
395 } else {
396 for i in 0..self.timers.timers.len() {
397 if self.timers.repeats.contains(i) {
398 v.push(RuntimeActivity::Interval(self.timers.timers[i], None));
399 } else {
400 v.push(RuntimeActivity::Timer(self.timers.timers[i], None));
401 }
402 }
403 }
404 RuntimeActivitySnapshot { active: v }
405 }
406
407 pub fn diff(before: &Self, after: &Self) -> RuntimeActivityDiff {
408 let mut appeared = vec![];
409 let mut disappeared = vec![];
410 let ops = &before.context_state.op_ctxs;
411
412 let mut a = BitSet::new();
413 for op in after.ops.ops.iter() {
414 a.insert(op.0 as usize);
415 }
416 for op in before.ops.ops.iter() {
417 if a.remove(op.0 as usize) {
418 } else {
420 disappeared.push(RuntimeActivity::AsyncOp(
422 op.0,
423 before.trace_for(RuntimeActivityType::AsyncOp, op.0 as _),
424 ops[op.1 as usize].decl.name,
425 ));
426 }
427 }
428 for op in after.ops.ops.iter() {
429 if a.contains(op.0 as usize) {
430 appeared.push(RuntimeActivity::AsyncOp(
432 op.0,
433 after.trace_for(RuntimeActivityType::AsyncOp, op.0 as _),
434 ops[op.1 as usize].decl.name,
435 ));
436 }
437 }
438
439 let mut a = BitSet::new();
440 for op in after.resources.resources.iter() {
441 a.insert(op.0 as usize);
442 }
443 for op in before.resources.resources.iter() {
444 if a.remove(op.0 as usize) {
445 } else {
447 disappeared.push(RuntimeActivity::Resource(op.0, None, op.1.clone()));
449 }
450 }
451 for op in after.resources.resources.iter() {
452 if a.contains(op.0 as usize) {
453 appeared.push(RuntimeActivity::Resource(op.0, None, op.1.clone()));
455 }
456 }
457
458 let mut a = BitSet::new();
459 for timer in after.timers.timers.iter() {
460 a.insert(*timer);
461 }
462 for index in 0..before.timers.timers.len() {
463 let timer = before.timers.timers[index];
464 if a.remove(timer) {
465 } else {
467 if before.timers.repeats.contains(index) {
469 disappeared.push(RuntimeActivity::Interval(
470 timer,
471 before.trace_for(RuntimeActivityType::Timer, timer),
472 ));
473 } else {
474 disappeared.push(RuntimeActivity::Timer(
475 timer,
476 before.trace_for(RuntimeActivityType::Timer, timer),
477 ));
478 }
479 }
480 }
481 for index in 0..after.timers.timers.len() {
482 let timer = after.timers.timers[index];
483 if a.contains(timer) {
484 if after.timers.repeats.contains(index) {
486 appeared.push(RuntimeActivity::Interval(
487 timer,
488 after.trace_for(RuntimeActivityType::Timer, timer),
489 ));
490 } else {
491 appeared.push(RuntimeActivity::Timer(
492 timer,
493 after.trace_for(RuntimeActivityType::Timer, timer),
494 ));
495 }
496 }
497 }
498
499 RuntimeActivityDiff {
500 appeared,
501 disappeared,
502 }
503 }
504}
505
506#[derive(Debug, Serialize)]
507pub struct RuntimeActivityDiff {
508 pub appeared: Vec<RuntimeActivity>,
509 pub disappeared: Vec<RuntimeActivity>,
510}
511
512impl RuntimeActivityDiff {
513 pub fn is_empty(&self) -> bool {
514 self.appeared.is_empty() && self.disappeared.is_empty()
515 }
516}
517
518#[derive(Debug, Serialize)]
519pub struct RuntimeActivitySnapshot {
520 pub active: Vec<RuntimeActivity>,
521}