Skip to main content

turbo_trace_server/
span_ref.rs

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