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 Iterator<Item = SpanEventRef<'a>> {
188 self.span.events.iter().map(|event| match event {
189 &SpanEvent::SelfTime { start, end } => SpanEventRef::SelfTime { start, end },
190 SpanEvent::Child { index } => SpanEventRef::Child {
191 span: SpanRef {
192 span: &self.store.spans[index.get()],
193 store: self.store,
194 index: index.get(),
195 },
196 },
197 })
198 }
199
200 pub fn children(&self) -> impl DoubleEndedIterator<Item = SpanRef<'a>> + 'a + use<'a> {
201 self.span.events.iter().filter_map(|event| match event {
202 SpanEvent::SelfTime { .. } => None,
203 SpanEvent::Child { index } => Some(SpanRef {
204 span: &self.store.spans[index.get()],
205 store: self.store,
206 index: index.get(),
207 }),
208 })
209 }
210
211 pub fn children_par(&self) -> impl ParallelIterator<Item = SpanRef<'a>> + 'a {
212 self.span.events.par_iter().filter_map(|event| match event {
213 SpanEvent::SelfTime { .. } => None,
214 SpanEvent::Child { index } => Some(SpanRef {
215 span: &self.store.spans[index.get()],
216 store: self.store,
217 index: index.get(),
218 }),
219 })
220 }
221
222 pub fn total_time(&self) -> Timestamp {
223 *self.time_data().total_time.get_or_init(|| {
224 self.children()
225 .map(|child| child.total_time())
226 .reduce(|a, b| a + b)
227 .unwrap_or_default()
228 + self.self_time()
229 })
230 }
231
232 pub fn total_allocations(&self) -> u64 {
233 *self.span.total_allocations.get_or_init(|| {
234 self.children()
235 .map(|child| child.total_allocations())
236 .reduce(|a, b| a + b)
237 .unwrap_or_default()
238 + self.self_allocations()
239 })
240 }
241
242 pub fn total_deallocations(&self) -> u64 {
243 *self.span.total_deallocations.get_or_init(|| {
244 self.children()
245 .map(|child| child.total_deallocations())
246 .reduce(|a, b| a + b)
247 .unwrap_or_default()
248 + self.self_deallocations()
249 })
250 }
251
252 pub fn total_persistent_allocations(&self) -> u64 {
253 *self.span.total_persistent_allocations.get_or_init(|| {
254 self.children()
255 .map(|child| child.total_persistent_allocations())
256 .reduce(|a, b| a + b)
257 .unwrap_or_default()
258 + self.self_persistent_allocations()
259 })
260 }
261
262 pub fn total_allocation_count(&self) -> u64 {
263 *self.span.total_allocation_count.get_or_init(|| {
264 self.children()
265 .map(|child| child.total_allocation_count())
266 .reduce(|a, b| a + b)
267 .unwrap_or_default()
268 + self.self_allocation_count()
269 })
270 }
271
272 pub fn total_span_count(&self) -> u64 {
273 *self.span.total_span_count.get_or_init(|| {
274 self.children()
275 .map(|child| child.total_span_count())
276 .reduce(|a, b| a + b)
277 .unwrap_or_default()
278 + 1
279 })
280 }
281
282 pub fn corrected_self_time(&self) -> Timestamp {
283 let store = self.store;
284 *self.time_data().corrected_self_time.get_or_init(|| {
285 let mut self_time = self
286 .span
287 .events
288 .par_iter()
289 .filter_map(|event| {
290 if let SpanEvent::SelfTime { start, end } = event {
291 let duration = *end - *start;
292 if !duration.is_zero() {
293 store.set_max_self_time_lookup(*end);
294 let corrected_time =
295 store.self_time_tree.as_ref().map_or(duration, |tree| {
296 tree.lookup_range_corrected_time(*start, *end)
297 });
298 return Some(corrected_time);
299 }
300 }
301 None
302 })
303 .sum();
304 if self.children().next().is_none() {
305 self_time = max(self_time, Timestamp::from_value(1));
306 }
307 self_time
308 })
309 }
310
311 pub fn corrected_total_time(&self) -> Timestamp {
312 *self.time_data().corrected_total_time.get_or_init(|| {
313 self.children_par()
314 .map(|child| child.corrected_total_time())
315 .sum::<Timestamp>()
316 + self.corrected_self_time()
317 })
318 }
319
320 pub fn max_depth(&self) -> u32 {
321 *self.span.max_depth.get_or_init(|| {
322 self.children()
323 .map(|child| child.max_depth() + 1)
324 .max()
325 .unwrap_or_default()
326 })
327 }
328
329 pub fn graph(&self) -> impl Iterator<Item = SpanGraphEventRef<'a>> + '_ {
330 self.extra()
331 .graph
332 .get_or_init(|| {
333 struct Entry<'a> {
334 span: SpanRef<'a>,
335 recursive: Vec<SpanIndex>,
336 }
337 let entries = self
338 .children_par()
339 .map(|span| {
340 let name = span.group_name();
341 let mut recursive = Vec::new();
342 let mut queue = VecDeque::with_capacity(0);
343 for nested_child in span.children() {
344 let nested_name = nested_child.group_name();
345 if name == nested_name {
346 recursive.push(nested_child.index());
347 queue.push_back(nested_child);
348 }
349 }
350 while let Some(child) = queue.pop_front() {
351 for nested_child in child.children() {
352 let nested_name = nested_child.group_name();
353 if name == nested_name {
354 recursive.push(nested_child.index());
355 queue.push_back(nested_child);
356 }
357 }
358 }
359 Entry { span, recursive }
360 })
361 .collect_vec_list();
362 let mut map: GroupNameToDirectAndRecusiveSpans = FxIndexMap::default();
363 for Entry {
364 span,
365 mut recursive,
366 } in entries.into_iter().flatten()
367 {
368 let name = span.group_name();
369 let (list, recursive_list) = map.entry(name).or_default();
370 list.push(span.index());
371 recursive_list.append(&mut recursive);
372 }
373 event_map_to_list(map)
374 })
375 .iter()
376 .map(|event| match event {
377 SpanGraphEvent::SelfTime { duration } => SpanGraphEventRef::SelfTime {
378 duration: *duration,
379 },
380 SpanGraphEvent::Child { child } => SpanGraphEventRef::Child {
381 graph: SpanGraphRef {
382 graph: child.clone(),
383 store: self.store,
384 },
385 },
386 })
387 }
388
389 pub fn bottom_up(self) -> impl Iterator<Item = SpanBottomUpRef<'a>> {
390 self.extra()
391 .bottom_up
392 .get_or_init(|| build_bottom_up_graph([self].into_iter()))
393 .iter()
394 .map(move |bottom_up| SpanBottomUpRef {
395 bottom_up: bottom_up.clone(),
396 store: self.store,
397 })
398 }
399
400 pub fn search(&self, query: &str) -> impl Iterator<Item = SpanRef<'a>> {
401 let mut query_items = query.split(",").map(str::trim);
402 let index = self.search_index();
403 let mut result = FxHashSet::default();
404 let query = query_items.next().unwrap();
405 for (key, spans) in index {
406 if key.contains(query) {
407 result.extend(spans.iter().copied());
408 }
409 }
410 for query in query_items {
411 let mut and_result = FxHashSet::default();
412 for (key, spans) in index {
413 if key.contains(query) {
414 and_result.extend(spans.iter().copied());
415 }
416 }
417 result.retain(|index| and_result.contains(index));
418 }
419 let store = self.store;
420 result.into_iter().map(move |index| SpanRef {
421 span: &store.spans[index.get()],
422 store,
423 index: index.get(),
424 })
425 }
426
427 fn search_index(&self) -> &HashMap<String, Vec<SpanIndex>> {
428 self.extra().search_index.get_or_init(|| {
429 let mut all_spans = Vec::new();
430 all_spans.push(self.index);
431 let mut i = 0;
432 while i < all_spans.len() {
433 let index = all_spans[i];
434 let span = SpanRef {
435 span: &self.store.spans[index],
436 store: self.store,
437 index,
438 };
439 for child in span.children() {
440 all_spans.push(child.index);
441 }
442 i += 1;
443 }
444
445 enum SpanOrMap<'a> {
446 Span(SpanRef<'a>),
447 Map(HashMap<String, Vec<SpanIndex>>),
448 }
449
450 fn add_span_to_map<'a>(index: &mut HashMap<String, Vec<SpanIndex>>, span: SpanRef<'a>) {
451 if !span.is_root() {
452 let (cat, name) = span.nice_name();
453 if !cat.is_empty() {
454 index
455 .raw_entry_mut()
456 .from_key(cat)
457 .and_modify(|_, v| v.push(span.index()))
458 .or_insert_with(|| (cat.to_string(), vec![span.index()]));
459 }
460 if !name.is_empty() {
461 index
462 .raw_entry_mut()
463 .from_key(name)
464 .and_modify(|_, v| v.push(span.index()))
465 .or_insert_with(|| (format!("name={name}"), vec![span.index()]));
466 }
467 for (name, value) in span.span.args.iter() {
468 index
469 .raw_entry_mut()
470 .from_key(value)
471 .and_modify(|_, v| v.push(span.index()))
472 .or_insert_with(|| (format!("{name}={value}"), vec![span.index()]));
473 }
474 if !span.is_complete() && !span.time_data().ignore_self_time {
475 let name = "incomplete_span";
476 index
477 .raw_entry_mut()
478 .from_key(name)
479 .and_modify(|_, v| v.push(span.index()))
480 .or_insert_with(|| (name.to_string(), vec![span.index()]));
481 }
482 }
483 }
484
485 let result = all_spans
486 .into_par_iter()
487 .map(|index| {
488 SpanOrMap::Span(SpanRef {
489 span: &self.store.spans[index],
490 store: self.store,
491 index,
492 })
493 })
494 .reduce(
495 || SpanOrMap::Map(HashMap::default()),
496 |a, b| {
497 let mut map = match a {
498 SpanOrMap::Span(span) => {
499 let mut map = HashMap::default();
500 add_span_to_map(&mut map, span);
501 map
502 }
503 SpanOrMap::Map(map) => map,
504 };
505 match b {
506 SpanOrMap::Span(span) => {
507 add_span_to_map(&mut map, span);
508 }
509 SpanOrMap::Map(other_map) => {
510 for (name, value) in other_map {
511 map.entry(name).or_default().extend(value);
512 }
513 }
514 }
515 SpanOrMap::Map(map)
516 },
517 );
518 match result {
519 SpanOrMap::Span(span) => {
520 let mut map = HashMap::default();
521 add_span_to_map(&mut map, span);
522 map
523 }
524 SpanOrMap::Map(map) => map,
525 }
526 })
527 }
528}
529
530impl Debug for SpanRef<'_> {
531 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
532 f.debug_struct("SpanRef")
533 .field("id", &self.id())
534 .field("name", &self.nice_name())
535 .field("start", &self.start())
536 .field("end", &self.end())
537 .field("is_complete", &self.is_complete())
538 .field("self_time", &self.self_time())
539 .field("total_time", &self.total_time())
540 .field("max_depth", &self.max_depth())
541 .finish()
542 }
543}
544
545#[allow(dead_code)]
546#[derive(Copy, Clone)]
547pub enum SpanEventRef<'a> {
548 SelfTime { start: Timestamp, end: Timestamp },
549 Child { span: SpanRef<'a> },
550}