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