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 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}