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_count(&self) -> usize {
182        self.span.events.len()
183    }
184
185    // TODO(sokra) use events instead of children for visualizing span graphs
186    #[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}