1use std::{
2 cmp::max,
3 collections::VecDeque,
4 fmt::{Debug, Formatter},
5 vec,
6};
7
8use hashbrown::HashMap;
9use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
10use rustc_hash::FxHashSet;
11use turbo_rcstr::{RcStr, rcstr};
12
13use crate::{
14 FxIndexMap,
15 bottom_up::build_bottom_up_graph,
16 span::{
17 Span, SpanEvent, SpanEventSelfTime, SpanExtra, SpanGraphEvent, SpanIndex, SpanName,
18 SpanNames, SpanTimeData, SpanTotals,
19 },
20 span_bottom_up_ref::SpanBottomUpRef,
21 span_graph_ref::{SpanGraphEventRef, SpanGraphRef, event_map_to_list},
22 store::{SpanId, Store},
23 timestamp::Timestamp,
24};
25
26pub type GroupNameToDirectAndRecusiveSpans<'l> =
27 FxIndexMap<(&'l RcStr, &'l RcStr), (Vec<SpanIndex>, Vec<SpanIndex>)>;
28
29#[derive(Copy, Clone)]
30pub struct SpanRef<'a> {
31 pub(crate) span: &'a Span,
32 pub(crate) store: &'a Store,
33 pub(crate) index: usize,
34}
35
36impl<'a> SpanRef<'a> {
37 pub fn id(&self) -> SpanId {
38 unsafe { SpanId::new_unchecked(self.index << 1) }
39 }
40
41 pub fn index(&self) -> SpanIndex {
42 SpanIndex::new(self.index).unwrap()
43 }
44
45 pub fn parent(&self) -> Option<SpanRef<'a>> {
46 self.span.parent.map(|index| SpanRef {
47 span: &self.store.spans[index.get()],
48 store: self.store,
49 index: index.get(),
50 })
51 }
52
53 pub fn start(&self) -> Timestamp {
54 self.span.start
55 }
56
57 pub fn time_data(&self) -> &'a SpanTimeData {
58 &self.span.time_data
59 }
60
61 pub fn extra(&self) -> &'a SpanExtra {
62 self.span.extra()
63 }
64
65 pub fn names(&self) -> &'a SpanNames {
66 self.span.names()
67 }
68
69 pub fn end(&self) -> Timestamp {
70 let time_data = self.time_data();
71 *time_data.end.get_or_init(|| {
72 max(
73 time_data.self_end,
74 self.children()
75 .map(|child| child.end())
76 .max()
77 .unwrap_or_default(),
78 )
79 })
80 }
81
82 pub fn is_complete(&self) -> bool {
83 self.span.is_complete
84 }
85
86 pub fn is_root(&self) -> bool {
87 self.index == 0
88 }
89
90 pub fn nice_name(&self) -> (&'a RcStr, &'a RcStr) {
91 let SpanName { category, title } = &self.names().nice_name;
92 (category, title)
93 }
94
95 pub fn group_name(&self) -> (&'a RcStr, &'a RcStr) {
96 let SpanName { category, title } = &self.names().group_name;
97 (category, title)
98 }
99
100 pub fn args(&self) -> impl Iterator<Item = (&RcStr, &RcStr)> {
101 self.span.args.iter().map(|(k, v)| (k, v))
102 }
103
104 pub fn self_time(&self) -> Timestamp {
105 self.time_data().self_time
106 }
107
108 pub fn self_allocations(&self) -> u64 {
109 self.span.self_allocations.saturating_sub(32)
111 }
112
113 pub fn self_deallocations(&self) -> u64 {
114 self.span.self_deallocations
115 }
116
117 pub fn self_persistent_allocations(&self) -> u64 {
118 self.self_allocations()
119 .saturating_sub(self.span.self_deallocations)
120 }
121
122 pub fn self_allocation_count(&self) -> u64 {
123 self.span.self_allocation_count.saturating_sub(4)
125 }
126
127 pub fn self_span_count(&self) -> u64 {
128 1
129 }
130
131 pub fn events(&self) -> impl DoubleEndedIterator<Item = SpanEventRef<'a>> {
133 self.span
134 .events
135 .iter()
136 .map(|event: &'a SpanEvent| match event {
137 SpanEvent::SelfTime(self_time) => SpanEventRef::SelfTime {
138 self_time: SpanEventSelfTimeRef {
139 store: self.store,
140 self_time,
141 },
142 },
143 SpanEvent::Child { index, .. } => SpanEventRef::Child {
144 span: SpanRef {
145 span: &self.store.spans[index.get()],
146 store: self.store,
147 index: index.get(),
148 },
149 },
150 })
151 }
152
153 pub fn children(&self) -> impl DoubleEndedIterator<Item = SpanRef<'a>> + 'a + use<'a> {
155 self.span.events.iter().filter_map(|event| match event {
156 SpanEvent::SelfTime { .. } => None,
157 SpanEvent::Child { index, .. } => Some(SpanRef {
158 span: &self.store.spans[index.get()],
159 store: self.store,
160 index: index.get(),
161 }),
162 })
163 }
164
165 pub fn children_par(&self) -> impl ParallelIterator<Item = SpanRef<'a>> + 'a {
167 self.span.events.par_iter().filter_map(|event| match event {
168 SpanEvent::SelfTime { .. } => None,
169 SpanEvent::Child { index, .. } => Some(SpanRef {
170 span: &self.store.spans[index.get()],
171 store: self.store,
172 index: index.get(),
173 }),
174 })
175 }
176
177 pub fn total_time(&self) -> Timestamp {
178 *self.time_data().total_time.get_or_init(|| {
179 self.children()
180 .map(|child| child.total_time())
181 .reduce(|a, b| a + b)
182 .unwrap_or_default()
183 + self.self_time()
184 })
185 }
186
187 fn totals(&self) -> &'a SpanTotals {
193 self.span.totals.get_or_init(|| {
194 let mut t = SpanTotals {
195 max_depth: 0,
196 allocations: self.self_allocations(),
197 deallocations: self.self_deallocations(),
198 persistent_allocations: self.self_persistent_allocations(),
199 allocation_count: self.self_allocation_count(),
200 span_count: 1,
201 };
202 for child in self.children() {
203 let c = child.totals();
204 t.max_depth = max(t.max_depth, c.max_depth + 1);
205 t.allocations += c.allocations;
206 t.deallocations += c.deallocations;
207 t.persistent_allocations += c.persistent_allocations;
208 t.allocation_count += c.allocation_count;
209 t.span_count += c.span_count;
210 }
211 t
212 })
213 }
214
215 pub fn total_allocations(&self) -> u64 {
216 self.totals().allocations
217 }
218
219 pub fn total_deallocations(&self) -> u64 {
220 self.totals().deallocations
221 }
222
223 pub fn total_persistent_allocations(&self) -> u64 {
224 self.totals().persistent_allocations
225 }
226
227 pub fn total_allocation_count(&self) -> u64 {
228 self.totals().allocation_count
229 }
230
231 pub fn total_span_count(&self) -> u64 {
232 self.totals().span_count
233 }
234
235 pub fn corrected_self_time(&self) -> Timestamp {
236 let store = self.store;
237 *self.time_data().corrected_self_time.get_or_init(|| {
238 let mut self_time = self
239 .span
240 .events
241 .par_iter()
242 .filter_map(|event: &'a SpanEvent| {
243 if let SpanEvent::SelfTime(self_time) = event {
244 return Some(
245 SpanEventSelfTimeRef { store, self_time }.corrected_self_time(),
246 );
247 }
248 None
249 })
250 .sum();
251 if self.children().next().is_none() {
252 self_time = max(self_time, Timestamp::from_value(1));
253 }
254 self_time
255 })
256 }
257
258 pub fn corrected_total_time(&self) -> Timestamp {
259 *self.time_data().corrected_total_time.get_or_init(|| {
260 self.children_par()
261 .map(|child| child.corrected_total_time())
262 .sum::<Timestamp>()
263 + self.corrected_self_time()
264 })
265 }
266
267 pub fn max_depth(&self) -> u32 {
268 self.totals().max_depth
269 }
270
271 pub fn graph(&self) -> impl Iterator<Item = SpanGraphEventRef<'a>> + '_ {
272 self.extra()
273 .graph
274 .get_or_init(|| {
275 struct Entry<'a> {
276 span: SpanRef<'a>,
277 recursive: Vec<SpanIndex>,
278 }
279 let entries = self
280 .children_par()
281 .map(|span| {
282 let name = span.group_name();
283 let mut recursive = Vec::new();
284 let mut queue = VecDeque::with_capacity(0);
285 for nested_child in span.children() {
286 let nested_name = nested_child.group_name();
287 if name == nested_name {
288 recursive.push(nested_child.index());
289 queue.push_back(nested_child);
290 }
291 }
292 while let Some(child) = queue.pop_front() {
293 for nested_child in child.children() {
294 let nested_name = nested_child.group_name();
295 if name == nested_name {
296 recursive.push(nested_child.index());
297 queue.push_back(nested_child);
298 }
299 }
300 }
301 Entry { span, recursive }
302 })
303 .collect_vec_list();
304 let mut map: GroupNameToDirectAndRecusiveSpans = FxIndexMap::default();
305 for Entry {
306 span,
307 mut recursive,
308 } in entries.into_iter().flatten()
309 {
310 let name = span.group_name();
311 let (list, recursive_list) = map.entry(name).or_default();
312 list.push(span.index());
313 recursive_list.append(&mut recursive);
314 }
315 event_map_to_list(map)
316 })
317 .iter()
318 .map(|event| match event {
319 SpanGraphEvent::SelfTime { duration } => SpanGraphEventRef::SelfTime {
320 duration: *duration,
321 },
322 SpanGraphEvent::Child { child } => SpanGraphEventRef::Child {
323 graph: SpanGraphRef {
324 graph: child.clone(),
325 store: self.store,
326 },
327 },
328 })
329 }
330
331 pub fn bottom_up(self) -> impl Iterator<Item = SpanBottomUpRef<'a>> {
332 self.extra()
333 .bottom_up
334 .get_or_init(|| build_bottom_up_graph([self].into_iter()))
335 .iter()
336 .map(move |bottom_up| SpanBottomUpRef {
337 bottom_up: bottom_up.clone(),
338 store: self.store,
339 })
340 }
341
342 pub fn search(&self, query: &str) -> impl Iterator<Item = SpanRef<'a>> {
343 let mut query_items = query.split(",").map(str::trim);
344 let index = self.search_index();
345 let mut result = FxHashSet::default();
346 let query = query_items.next().unwrap();
347 for (key, spans) in index {
348 if key.contains(query) {
349 result.extend(spans.iter().copied());
350 }
351 }
352 for query in query_items {
353 let mut and_result = FxHashSet::default();
354 for (key, spans) in index {
355 if key.contains(query) {
356 and_result.extend(spans.iter().copied());
357 }
358 }
359 result.retain(|index| and_result.contains(index));
360 }
361 let store = self.store;
362 result.into_iter().map(move |index| SpanRef {
363 span: &store.spans[index.get()],
364 store,
365 index: index.get(),
366 })
367 }
368
369 fn search_index(&self) -> &HashMap<RcStr, Vec<SpanIndex>> {
370 self.extra().search_index.get_or_init(|| {
371 let mut all_spans = Vec::new();
372 all_spans.push(self.index);
373 let mut i = 0;
374 while i < all_spans.len() {
375 let index = all_spans[i];
376 let span = SpanRef {
377 span: &self.store.spans[index],
378 store: self.store,
379 index,
380 };
381 for child in span.children() {
382 all_spans.push(child.index);
383 }
384 i += 1;
385 }
386
387 enum SpanOrMap<'a> {
388 Span(SpanRef<'a>),
389 Map(HashMap<RcStr, Vec<SpanIndex>>),
390 }
391
392 fn push_to_index(
398 index: &mut HashMap<RcStr, Vec<SpanIndex>>,
399 lookup: &str,
400 make_key: impl FnOnce() -> RcStr,
401 span_index: SpanIndex,
402 ) {
403 index
404 .raw_entry_mut()
405 .from_key(lookup)
406 .and_modify(|_, v| v.push(span_index))
407 .or_insert_with(|| (make_key(), vec![span_index]));
408 }
409
410 fn add_span_to_map<'a>(index: &mut HashMap<RcStr, Vec<SpanIndex>>, span: SpanRef<'a>) {
411 if span.is_root() {
412 return;
413 }
414 let (cat, name) = span.nice_name();
415 if !cat.is_empty() {
416 push_to_index(index, cat, || cat.clone(), span.index());
417 }
418 if !name.is_empty() {
419 push_to_index(
420 index,
421 name,
422 || RcStr::from(format!("name={name}")),
423 span.index(),
424 );
425 }
426 for (k, v) in span.span.args.iter() {
427 push_to_index(
428 index,
429 v.as_str(),
430 || RcStr::from(format!("{k}={v}")),
431 span.index(),
432 );
433 }
434 if !span.is_complete() && span.span.name != "thread" {
435 push_to_index(
436 index,
437 "incomplete_span",
438 || rcstr!("incomplete_span"),
439 span.index(),
440 );
441 }
442 }
443
444 let result = all_spans
445 .into_par_iter()
446 .map(|index| {
447 SpanOrMap::Span(SpanRef {
448 span: &self.store.spans[index],
449 store: self.store,
450 index,
451 })
452 })
453 .reduce(
454 || SpanOrMap::Map(HashMap::default()),
455 |a, b| {
456 let mut map = match a {
457 SpanOrMap::Span(span) => {
458 let mut map = HashMap::default();
459 add_span_to_map(&mut map, span);
460 map
461 }
462 SpanOrMap::Map(map) => map,
463 };
464 match b {
465 SpanOrMap::Span(span) => {
466 add_span_to_map(&mut map, span);
467 }
468 SpanOrMap::Map(other_map) => {
469 for (name, value) in other_map {
470 map.entry(name).or_default().extend(value);
471 }
472 }
473 }
474 SpanOrMap::Map(map)
475 },
476 );
477 match result {
478 SpanOrMap::Span(span) => {
479 let mut map = HashMap::default();
480 add_span_to_map(&mut map, span);
481 map
482 }
483 SpanOrMap::Map(map) => map,
484 }
485 })
486 }
487}
488
489impl Debug for SpanRef<'_> {
490 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
491 f.debug_struct("SpanRef")
492 .field("id", &self.id())
493 .field("name", &self.nice_name())
494 .field("start", &self.start())
495 .field("end", &self.end())
496 .field("is_complete", &self.is_complete())
497 .field("self_time", &self.self_time())
498 .field("total_time", &self.total_time())
499 .field("max_depth", &self.max_depth())
500 .finish()
501 }
502}
503
504pub struct SpanEventSelfTimeRef<'a> {
505 store: &'a Store,
506 self_time: &'a SpanEventSelfTime,
507}
508
509impl<'a> SpanEventSelfTimeRef<'a> {
510 pub fn start(&self) -> Timestamp {
511 self.self_time.start
512 }
513
514 pub fn end(&self) -> Timestamp {
515 self.self_time.end()
516 }
517
518 pub fn corrected_self_time(&self) -> Timestamp {
519 *self.self_time.corrected_self_time.get_or_init(|| {
520 let end = self.self_time.end();
523 let duration = Timestamp::from_value(self.self_time.duration.get());
524 self.store.set_max_self_time_lookup(end);
525 self.store.self_time_tree.as_ref().map_or(duration, |tree| {
526 tree.lookup_range_corrected_time(self.self_time.start, end)
527 })
528 })
529 }
530}
531
532pub enum SpanEventRef<'a> {
533 SelfTime { self_time: SpanEventSelfTimeRef<'a> },
534 Child { span: SpanRef<'a> },
535}
536
537impl SpanEventRef<'_> {
538 pub fn total_time(&self) -> Timestamp {
539 match self {
540 SpanEventRef::SelfTime {
541 self_time: event, ..
542 } => event.end().saturating_sub(event.start()),
543 SpanEventRef::Child { span } => span.total_time(),
544 }
545 }
546
547 pub fn corrected_self_time(&self) -> Timestamp {
548 match self {
549 SpanEventRef::SelfTime { self_time: event } => event.corrected_self_time(),
550 SpanEventRef::Child { span } => span.corrected_self_time(),
551 }
552 }
553}
554
555#[cfg(test)]
556mod tests {
557 use rustc_hash::FxHashSet;
558 use turbo_rcstr::RcStr;
559
560 use crate::{span::SpanArgs, span_ref::SpanRef, store::Store, timestamp::Timestamp};
561
562 fn span_ref<'a>(store: &'a Store, idx: crate::span::SpanIndex) -> SpanRef<'a> {
563 SpanRef {
564 span: &store.spans[idx.get()],
565 store,
566 index: idx.get(),
567 }
568 }
569
570 #[test]
571 fn totals_aggregate_subtree() {
572 let mut store = Store::new();
573 let mut outdated = FxHashSet::default();
574
575 let a = store.add_span(
578 None,
579 Timestamp::from_micros(0),
580 RcStr::default(),
581 RcStr::from("a"),
582 SpanArgs::new(),
583 &mut outdated,
584 );
585 let b = store.add_span(
586 Some(a),
587 Timestamp::from_micros(1),
588 RcStr::default(),
589 RcStr::from("b"),
590 SpanArgs::new(),
591 &mut outdated,
592 );
593 let c = store.add_span(
594 None,
595 Timestamp::from_micros(2),
596 RcStr::default(),
597 RcStr::from("c"),
598 SpanArgs::new(),
599 &mut outdated,
600 );
601
602 store.add_allocation(a, 1000, 10, &mut outdated);
606 store.add_allocation(b, 500, 5, &mut outdated);
607 store.add_allocation(c, 200, 2, &mut outdated);
608
609 let a_ref = span_ref(&store, a);
610 let b_ref = span_ref(&store, b);
611 let c_ref = span_ref(&store, c);
612
613 assert_eq!(a_ref.self_allocations(), 1000 - 32);
615 assert_eq!(b_ref.self_allocations(), 500 - 32);
616 assert_eq!(c_ref.self_allocations(), 200 - 32);
617
618 assert_eq!(
620 a_ref.total_allocations(),
621 a_ref.self_allocations() + b_ref.self_allocations()
622 );
623 assert_eq!(b_ref.total_allocations(), b_ref.self_allocations());
624 assert_eq!(c_ref.total_allocations(), c_ref.self_allocations());
625
626 assert_eq!(a_ref.total_span_count(), 2);
628 assert_eq!(b_ref.total_span_count(), 1);
629 assert_eq!(c_ref.total_span_count(), 1);
630
631 assert_eq!(
633 a_ref.total_allocation_count(),
634 a_ref.self_allocation_count() + b_ref.self_allocation_count()
635 );
636 }
637
638 #[test]
639 fn totals_invalidate_and_recompute() {
640 let mut store = Store::new();
641 let mut outdated = FxHashSet::default();
642 let s = store.add_span(
643 None,
644 Timestamp::from_micros(0),
645 RcStr::default(),
646 RcStr::from("s"),
647 SpanArgs::new(),
648 &mut outdated,
649 );
650 store.add_allocation(s, 1000, 10, &mut outdated);
651
652 let before = span_ref(&store, s).total_allocations();
654 assert_eq!(before, 1000 - 32);
655
656 let mut outdated = FxHashSet::default();
658 store.add_allocation(s, 200, 2, &mut outdated);
659 store.invalidate_outdated_spans(&outdated);
660
661 let after = span_ref(&store, s).total_allocations();
663 assert_eq!(after, 1200 - 32);
664 }
665}