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