turbo_trace_server/
viewer.rs

1use std::cmp::{Reverse, max};
2
3use either::Either;
4use itertools::Itertools;
5use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
6use rustc_hash::{FxHashMap, FxHashSet};
7use serde::Serialize;
8
9use crate::{
10    server::ViewRect,
11    span_bottom_up_ref::SpanBottomUpRef,
12    span_graph_ref::{SpanGraphEventRef, SpanGraphRef},
13    span_ref::{SpanEventRef, SpanRef},
14    store::{SpanId, Store},
15    timestamp::Timestamp,
16    u64_empty_string,
17};
18
19const EXTRA_WIDTH_PERCENTAGE: u64 = 50;
20const EXTRA_HEIGHT: u64 = 5;
21
22#[derive(Default)]
23pub struct Viewer {
24    span_options: FxHashMap<SpanId, SpanOptions>,
25}
26
27#[derive(Clone, Copy, Debug)]
28pub enum ValueMode {
29    Duration,
30    Cpu,
31    Allocations,
32    Deallocations,
33    PersistentAllocations,
34    AllocationCount,
35    Count,
36    AllocationsPerTime,
37    PersistentAllocationsPerTime,
38    AllocationCountPerTime,
39}
40
41impl ValueMode {
42    fn secondary(&self) -> ValueMode {
43        match self {
44            ValueMode::Duration => ValueMode::Cpu,
45            ValueMode::Cpu => ValueMode::Duration,
46            ValueMode::Allocations => ValueMode::PersistentAllocations,
47            ValueMode::Deallocations => ValueMode::PersistentAllocations,
48            ValueMode::PersistentAllocations => ValueMode::Allocations,
49            ValueMode::AllocationCount => ValueMode::Allocations,
50            ValueMode::Count => ValueMode::Count,
51            ValueMode::AllocationsPerTime => ValueMode::PersistentAllocationsPerTime,
52            ValueMode::PersistentAllocationsPerTime => ValueMode::AllocationsPerTime,
53            ValueMode::AllocationCountPerTime => ValueMode::AllocationsPerTime,
54        }
55    }
56
57    fn value_from_span(&self, span: &SpanRef<'_>) -> u64 {
58        match self {
59            ValueMode::Duration => *span.corrected_total_time(),
60            ValueMode::Cpu => *span.total_time(),
61            ValueMode::Allocations => span.total_allocations(),
62            ValueMode::Deallocations => span.total_deallocations(),
63            ValueMode::PersistentAllocations => span.total_persistent_allocations(),
64            ValueMode::AllocationCount => span.total_allocation_count(),
65            ValueMode::Count => span.total_span_count(),
66            ValueMode::AllocationsPerTime => {
67                value_over_time(span.total_allocations(), span.corrected_total_time())
68            }
69            ValueMode::AllocationCountPerTime => {
70                value_over_time(span.total_allocation_count(), span.corrected_total_time())
71            }
72            ValueMode::PersistentAllocationsPerTime => value_over_time(
73                span.total_persistent_allocations(),
74                span.corrected_total_time(),
75            ),
76        }
77    }
78
79    fn value_from_event(&self, event: &SpanEventRef<'_>) -> u64 {
80        match self {
81            ValueMode::Duration => *event.corrected_self_time(),
82            ValueMode::Cpu => *event.total_time(),
83            _ => match event {
84                SpanEventRef::Child { span } => self.value_from_span(span),
85                SpanEventRef::SelfTime { .. } => 0,
86            },
87        }
88    }
89
90    fn value_from_graph(&self, graph: &SpanGraphRef<'_>) -> u64 {
91        match self {
92            ValueMode::Duration => *graph.corrected_total_time(),
93            ValueMode::Cpu => *graph.total_time(),
94            ValueMode::Allocations => graph.total_allocations(),
95            ValueMode::Deallocations => graph.total_deallocations(),
96            ValueMode::PersistentAllocations => graph.total_persistent_allocations(),
97            ValueMode::AllocationCount => graph.total_allocation_count(),
98            ValueMode::Count => graph.total_span_count(),
99            ValueMode::AllocationsPerTime => {
100                value_over_time(graph.total_allocations(), graph.corrected_total_time())
101            }
102            ValueMode::AllocationCountPerTime => {
103                value_over_time(graph.total_allocation_count(), graph.corrected_total_time())
104            }
105            ValueMode::PersistentAllocationsPerTime => value_over_time(
106                graph.total_persistent_allocations(),
107                graph.corrected_total_time(),
108            ),
109        }
110    }
111
112    fn value_from_graph_event(&self, event: &SpanGraphEventRef<'_>) -> u64 {
113        match self {
114            ValueMode::Duration => *event.corrected_total_time(),
115            ValueMode::Cpu => *event.total_time(),
116            ValueMode::Allocations => event.total_allocations(),
117            ValueMode::Deallocations => event.total_deallocations(),
118            ValueMode::PersistentAllocations => event.total_persistent_allocations(),
119            ValueMode::AllocationCount => event.total_allocation_count(),
120            ValueMode::Count => event.total_span_count(),
121            ValueMode::AllocationsPerTime => {
122                value_over_time(event.total_allocations(), event.corrected_total_time())
123            }
124            ValueMode::AllocationCountPerTime => {
125                value_over_time(event.total_allocation_count(), event.corrected_total_time())
126            }
127            ValueMode::PersistentAllocationsPerTime => value_over_time(
128                event.total_persistent_allocations(),
129                event.corrected_total_time(),
130            ),
131        }
132    }
133
134    fn value_from_bottom_up(&self, bottom_up: &SpanBottomUpRef<'_>) -> u64 {
135        match self {
136            ValueMode::Duration => *bottom_up.corrected_self_time(),
137            ValueMode::Cpu => *bottom_up.self_time(),
138            ValueMode::Allocations => bottom_up.self_allocations(),
139            ValueMode::Deallocations => bottom_up.self_deallocations(),
140            ValueMode::PersistentAllocations => bottom_up.self_persistent_allocations(),
141            ValueMode::AllocationCount => bottom_up.self_allocation_count(),
142            ValueMode::Count => bottom_up.self_span_count(),
143            ValueMode::AllocationsPerTime => value_over_time(
144                bottom_up.self_allocations(),
145                bottom_up.corrected_self_time(),
146            ),
147            ValueMode::AllocationCountPerTime => value_over_time(
148                bottom_up.self_allocation_count(),
149                bottom_up.corrected_self_time(),
150            ),
151            ValueMode::PersistentAllocationsPerTime => value_over_time(
152                bottom_up.self_persistent_allocations(),
153                bottom_up.corrected_self_time(),
154            ),
155        }
156    }
157
158    fn value_from_bottom_up_span(&self, bottom_up_span: &SpanRef<'_>) -> u64 {
159        match self {
160            ValueMode::Duration => *bottom_up_span.corrected_self_time(),
161            ValueMode::Cpu => *bottom_up_span.self_time(),
162            ValueMode::Allocations => bottom_up_span.self_allocations(),
163            ValueMode::Deallocations => bottom_up_span.self_deallocations(),
164            ValueMode::PersistentAllocations => bottom_up_span.self_persistent_allocations(),
165            ValueMode::AllocationCount => bottom_up_span.self_allocation_count(),
166            ValueMode::Count => bottom_up_span.self_span_count(),
167            ValueMode::AllocationsPerTime => value_over_time(
168                bottom_up_span.self_allocations(),
169                bottom_up_span.corrected_self_time(),
170            ),
171            ValueMode::AllocationCountPerTime => value_over_time(
172                bottom_up_span.self_allocation_count(),
173                bottom_up_span.corrected_self_time(),
174            ),
175            ValueMode::PersistentAllocationsPerTime => value_over_time(
176                bottom_up_span.self_persistent_allocations(),
177                bottom_up_span.corrected_self_time(),
178            ),
179        }
180    }
181}
182
183/// this is unfortunately int division but itll have to do.
184///
185/// cases where count per time is very low is probably not important
186fn value_over_time(value: u64, time: Timestamp) -> u64 {
187    if *time == 0 { 0 } else { value / *time }
188}
189
190#[derive(Clone, Copy, Debug)]
191pub enum ViewMode {
192    RawSpans { sorted: bool },
193    Aggregated { sorted: bool },
194    BottomUp { sorted: bool },
195    AggregatedBottomUp { sorted: bool },
196}
197
198impl ViewMode {
199    fn as_spans(self) -> Self {
200        match self {
201            ViewMode::RawSpans { sorted } => ViewMode::RawSpans { sorted },
202            ViewMode::Aggregated { sorted } => ViewMode::RawSpans { sorted },
203            ViewMode::BottomUp { sorted } => ViewMode::BottomUp { sorted },
204            ViewMode::AggregatedBottomUp { sorted } => ViewMode::BottomUp { sorted },
205        }
206    }
207
208    fn as_bottom_up(self) -> Self {
209        match self {
210            ViewMode::RawSpans { sorted } => ViewMode::BottomUp { sorted },
211            ViewMode::Aggregated { sorted } => ViewMode::AggregatedBottomUp { sorted },
212            ViewMode::BottomUp { sorted } => ViewMode::BottomUp { sorted },
213            ViewMode::AggregatedBottomUp { sorted } => ViewMode::AggregatedBottomUp { sorted },
214        }
215    }
216
217    fn aggregate_children(&self) -> bool {
218        match self {
219            ViewMode::RawSpans { .. } => false,
220            ViewMode::Aggregated { .. } => true,
221            ViewMode::BottomUp { .. } => false,
222            ViewMode::AggregatedBottomUp { .. } => true,
223        }
224    }
225
226    fn bottom_up(&self) -> bool {
227        match self {
228            ViewMode::RawSpans { .. } => false,
229            ViewMode::Aggregated { .. } => false,
230            ViewMode::BottomUp { .. } => true,
231            ViewMode::AggregatedBottomUp { .. } => true,
232        }
233    }
234
235    fn sort_children(&self) -> bool {
236        match self {
237            ViewMode::RawSpans { sorted } => *sorted,
238            ViewMode::Aggregated { sorted } => *sorted,
239            ViewMode::BottomUp { sorted } => *sorted,
240            ViewMode::AggregatedBottomUp { sorted } => *sorted,
241        }
242    }
243}
244
245#[derive(Default)]
246struct SpanOptions {
247    view_mode: Option<(ViewMode, bool)>,
248}
249
250pub struct Update {
251    pub lines: Vec<ViewLineUpdate>,
252    pub max: u64,
253}
254
255#[derive(Serialize, Debug)]
256#[serde(rename_all = "camelCase")]
257pub struct ViewLineUpdate {
258    y: u64,
259    spans: Vec<ViewSpan>,
260}
261
262#[derive(Serialize, Debug)]
263#[serde(rename_all = "camelCase")]
264pub struct ViewSpan {
265    #[serde(with = "u64_empty_string")]
266    id: u64,
267    #[serde(rename = "x")]
268    start: u64,
269    #[serde(rename = "w")]
270    width: u64,
271    #[serde(rename = "cat")]
272    category: String,
273    #[serde(rename = "t")]
274    text: String,
275    #[serde(rename = "c")]
276    count: u64,
277    #[serde(rename = "k")]
278    kind: u8,
279    #[serde(rename = "s")]
280    start_in_parent: u32,
281    #[serde(rename = "e")]
282    end_in_parent: u32,
283    #[serde(rename = "v")]
284    secondary: u64,
285}
286
287#[derive(Debug)]
288enum QueueItem<'a> {
289    Span(SpanRef<'a>),
290    SpanGraph(SpanGraphRef<'a>),
291    SpanBottomUp(SpanBottomUpRef<'a>),
292    SpanBottomUpSpan(SpanRef<'a>),
293}
294
295impl QueueItem<'_> {
296    fn value(&self, value_mode: ValueMode) -> u64 {
297        match self {
298            QueueItem::Span(span) => value_mode.value_from_span(span),
299            QueueItem::SpanGraph(span_graph) => value_mode.value_from_graph(span_graph),
300            QueueItem::SpanBottomUp(span_bottom_up) => {
301                value_mode.value_from_bottom_up(span_bottom_up)
302            }
303            QueueItem::SpanBottomUpSpan(span) => value_mode.value_from_bottom_up_span(span),
304        }
305    }
306
307    fn max_depth(&self) -> u32 {
308        match self {
309            QueueItem::Span(span) => span.max_depth(),
310            QueueItem::SpanGraph(span_graph) => span_graph.max_depth(),
311            QueueItem::SpanBottomUp(span_bottom_up) => span_bottom_up.max_depth(),
312            QueueItem::SpanBottomUpSpan(span) => span.max_depth(),
313        }
314    }
315}
316
317#[derive(Debug, PartialEq, Eq)]
318enum FilterMode {
319    SelectedItem,
320    Parent,
321    Child,
322}
323
324#[derive(Debug)]
325struct QueueItemWithState<'a> {
326    item: QueueItem<'a>,
327    line_index: usize,
328    start: u64,
329    placeholder: bool,
330    view_mode: ViewMode,
331    filtered: Option<FilterMode>,
332}
333
334struct ChildItem<'a> {
335    item: QueueItemWithState<'a>,
336    depth: u32,
337    pixel_range: (u64, u64),
338}
339
340impl Viewer {
341    pub fn new() -> Self {
342        Self::default()
343    }
344
345    pub fn set_view_mode(&mut self, id: SpanId, view_mode: Option<(ViewMode, bool)>) {
346        self.span_options.entry(id).or_default().view_mode = view_mode;
347    }
348
349    pub fn compute_update(&mut self, store: &Store, view_rect: &ViewRect) -> Update {
350        let mut highlighted_spans: FxHashSet<SpanId> = FxHashSet::default();
351        let mut highlighted_span_parents: FxHashSet<SpanId> = FxHashSet::default();
352        let search_mode = !view_rect.query.is_empty();
353        let (query, focus_mode) = if let Some(query) = view_rect.query.strip_suffix('!') {
354            (query, true)
355        } else {
356            (view_rect.query.as_str(), false)
357        };
358
359        let default_view_mode = view_rect.view_mode.as_str();
360        let (default_view_mode, default_sorted) = default_view_mode
361            .strip_suffix("-sorted")
362            .map_or((default_view_mode, false), |s| (s, true));
363        let (default_view_mode, with_root) = match default_view_mode {
364            "aggregated" => (
365                ViewMode::Aggregated {
366                    sorted: default_sorted,
367                },
368                false,
369            ),
370            "root-aggregated" => (
371                ViewMode::Aggregated {
372                    sorted: default_sorted,
373                },
374                true,
375            ),
376            "raw-spans" => (
377                ViewMode::RawSpans {
378                    sorted: default_sorted,
379                },
380                false,
381            ),
382            "bottom-up" => (
383                ViewMode::BottomUp {
384                    sorted: default_sorted,
385                },
386                false,
387            ),
388            "aggregated-bottom-up" => (
389                ViewMode::AggregatedBottomUp {
390                    sorted: default_sorted,
391                },
392                false,
393            ),
394            "root-aggregated-bottom-up" => (
395                ViewMode::AggregatedBottomUp {
396                    sorted: default_sorted,
397                },
398                true,
399            ),
400            _ => (
401                ViewMode::Aggregated {
402                    sorted: default_sorted,
403                },
404                false,
405            ),
406        };
407
408        let value_mode = match view_rect.value_mode.as_str() {
409            "duration" => ValueMode::Duration,
410            "cpu" => ValueMode::Cpu,
411            "allocations" => ValueMode::Allocations,
412            "deallocations" => ValueMode::Deallocations,
413            "persistent-deallocations" => ValueMode::PersistentAllocations,
414            "allocation-count" => ValueMode::AllocationCount,
415            "allocations-per-time" => ValueMode::AllocationsPerTime,
416            "allocation-count-per-time" => ValueMode::AllocationCountPerTime,
417            "persistent-allocations-per-time" => ValueMode::PersistentAllocationsPerTime,
418            "count" => ValueMode::Count,
419            _ => ValueMode::Duration,
420        };
421
422        if !store.has_time_info() && matches!(value_mode, ValueMode::Duration) {
423            return Update {
424                lines: vec![ViewLineUpdate {
425                    spans: vec![ViewSpan {
426                        id: 0,
427                        start: 0,
428                        width: 1,
429                        category: "info".to_string(),
430                        text: "No time info in trace".to_string(),
431                        count: 1,
432                        kind: 0,
433                        start_in_parent: 0,
434                        end_in_parent: 0,
435                        secondary: 0,
436                    }],
437                    y: 0,
438                }],
439                max: 1,
440            };
441        }
442
443        let mut queue = Vec::new();
444
445        let root_spans = if with_root {
446            vec![store.root_span()]
447        } else {
448            let mut root_spans = store.root_spans().collect::<Vec<_>>();
449            root_spans.sort_by_key(|span| span.start());
450            root_spans
451        };
452
453        let mut children = Vec::new();
454        let mut current = 0;
455        let offset = root_spans
456            .iter()
457            .min_by_key(|span| span.start())
458            .map_or(Timestamp::ZERO, |span| span.start());
459        root_spans.par_iter().for_each(|span| {
460            span.max_depth();
461            QueueItem::Span(*span).value(value_mode);
462        });
463        for span in root_spans {
464            if matches!(value_mode, ValueMode::Duration) {
465                // Move current to start if needed.
466                current = max(current, *(span.start() - offset));
467            }
468            if add_child_item(
469                &mut children,
470                &mut current,
471                view_rect,
472                0,
473                default_view_mode,
474                value_mode,
475                QueueItem::Span(span),
476                Some(if search_mode {
477                    FilterMode::Parent
478                } else {
479                    FilterMode::SelectedItem
480                }),
481            ) && search_mode
482            {
483                let mut has_results = false;
484                for mut result in span.search(query) {
485                    has_results = true;
486                    highlighted_spans.insert(result.id());
487                    while let Some(parent) = result.parent() {
488                        result = parent;
489                        if !highlighted_span_parents.insert(result.id()) {
490                            break;
491                        }
492                    }
493                }
494                if has_results {
495                    highlighted_spans.insert(span.id());
496                } else {
497                    children.last_mut().unwrap().item.filtered = None;
498                }
499            }
500        }
501        enqueue_children(children, &mut queue);
502        queue.par_iter().for_each(|item| {
503            let QueueItem::Span(span) = item.item else {
504                return;
505            };
506            let view_mode = if span.is_complete() {
507                item.view_mode
508            } else {
509                item.view_mode.as_spans()
510            };
511
512            match (view_mode.bottom_up(), view_mode.aggregate_children()) {
513                (false, false) => {}
514                (false, true) => {
515                    span.graph()
516                        .collect::<Vec<_>>()
517                        .par_iter()
518                        .for_each(|event| {
519                            value_mode.value_from_graph_event(event);
520                        });
521                }
522                (true, false) => {
523                    span.bottom_up()
524                        .collect::<Vec<_>>()
525                        .par_iter()
526                        .for_each(|bu| {
527                            bu.spans().collect::<Vec<_>>().par_iter().for_each(|span| {
528                                value_mode.value_from_bottom_up_span(span);
529                            });
530                        });
531                }
532                (true, true) => {
533                    span.bottom_up()
534                        .collect::<Vec<_>>()
535                        .par_iter()
536                        .for_each(|bu| {
537                            value_mode.value_from_bottom_up(bu);
538                        });
539                }
540            }
541        });
542
543        let mut lines: Vec<Vec<LineEntry<'_>>> = vec![];
544
545        while let Some(QueueItemWithState {
546            item: span,
547            line_index,
548            start,
549            placeholder,
550            view_mode,
551            mut filtered,
552        }) = queue.pop()
553        {
554            let line = get_line(&mut lines, line_index);
555            let width = span.value(value_mode);
556            let secondary = span.value(value_mode.secondary());
557
558            let skipped_by_focus =
559                focus_mode && matches!(filtered, Some(FilterMode::Parent) | None);
560
561            let get_filter_mode = |span: SpanId| {
562                if focus_mode
563                    && matches!(filtered, Some(FilterMode::SelectedItem | FilterMode::Child))
564                {
565                    Some(FilterMode::Child)
566                } else if search_mode {
567                    if highlighted_spans.contains(&span) {
568                        Some(FilterMode::SelectedItem)
569                    } else if highlighted_span_parents.contains(&span) {
570                        Some(FilterMode::Parent)
571                    } else {
572                        None
573                    }
574                } else {
575                    Some(FilterMode::SelectedItem)
576                }
577            };
578
579            // compute children
580            let mut children = Vec::new();
581            let mut current = start;
582            let child_line_index = if skipped_by_focus {
583                line_index
584            } else {
585                line_index + 1
586            };
587            match &span {
588                QueueItem::Span(span) => {
589                    let (selected_view_mode, inherit) = (!span.is_root())
590                        .then(|| self.span_options.get(&span.id()).and_then(|o| o.view_mode))
591                        .flatten()
592                        .unwrap_or_else(|| {
593                            (
594                                if span.is_complete() {
595                                    view_mode
596                                } else {
597                                    view_mode.as_spans()
598                                },
599                                false,
600                            )
601                        });
602
603                    let view_mode = if inherit {
604                        selected_view_mode
605                    } else {
606                        view_mode
607                    };
608
609                    let selected_view_mode =
610                        if search_mode && highlighted_span_parents.contains(&span.id()) {
611                            selected_view_mode.as_spans()
612                        } else {
613                            selected_view_mode
614                        };
615
616                    if selected_view_mode.bottom_up() {
617                        let bottom_up = span.bottom_up();
618                        if selected_view_mode.aggregate_children() {
619                            let bottom_up = if selected_view_mode.sort_children() {
620                                Either::Left(bottom_up.sorted_by_cached_key(|child| {
621                                    Reverse(value_mode.value_from_bottom_up(child))
622                                }))
623                            } else {
624                                Either::Right(bottom_up)
625                            };
626                            for child in bottom_up {
627                                // TODO search
628                                add_child_item(
629                                    &mut children,
630                                    &mut current,
631                                    view_rect,
632                                    child_line_index,
633                                    view_mode,
634                                    value_mode,
635                                    QueueItem::SpanBottomUp(child),
636                                    Some(FilterMode::SelectedItem),
637                                );
638                            }
639                        } else {
640                            let bottom_up = bottom_up
641                                .flat_map(|bottom_up| bottom_up.spans().collect::<Vec<_>>());
642                            let bottom_up = if selected_view_mode.sort_children() {
643                                Either::Left(bottom_up.sorted_by_cached_key(|child| {
644                                    Reverse(value_mode.value_from_bottom_up_span(child))
645                                }))
646                            } else {
647                                Either::Right(bottom_up)
648                            };
649                            for child in bottom_up {
650                                let filtered = get_filter_mode(child.id());
651                                add_child_item(
652                                    &mut children,
653                                    &mut current,
654                                    view_rect,
655                                    child_line_index,
656                                    view_mode,
657                                    value_mode,
658                                    QueueItem::SpanBottomUpSpan(child),
659                                    filtered,
660                                );
661                            }
662                        }
663                    } else if !selected_view_mode.aggregate_children() {
664                        let spans = if selected_view_mode.sort_children() {
665                            Either::Left(span.events().sorted_by_cached_key(|child| {
666                                Reverse(value_mode.value_from_event(child))
667                            }))
668                        } else {
669                            Either::Right(span.events().sorted_by_key(|child| child.start()))
670                        };
671                        for child in spans {
672                            match child {
673                                SpanEventRef::SelfTime { .. } => {
674                                    current += value_mode.value_from_event(&child);
675                                }
676                                SpanEventRef::Child { span: child } => {
677                                    let filtered = get_filter_mode(child.id());
678                                    add_child_item(
679                                        &mut children,
680                                        &mut current,
681                                        view_rect,
682                                        child_line_index,
683                                        view_mode,
684                                        value_mode,
685                                        QueueItem::Span(child),
686                                        filtered,
687                                    );
688                                }
689                            }
690                        }
691                    } else {
692                        let events = if selected_view_mode.sort_children() {
693                            Either::Left(span.graph().sorted_by_cached_key(|child| {
694                                Reverse(value_mode.value_from_graph_event(child))
695                            }))
696                        } else {
697                            Either::Right(span.graph())
698                        };
699                        for event in events {
700                            let filtered = if search_mode {
701                                None
702                            } else {
703                                Some(FilterMode::SelectedItem)
704                            };
705                            match event {
706                                SpanGraphEventRef::SelfTime { duration: _ } => {}
707                                SpanGraphEventRef::Child { graph } => {
708                                    add_child_item(
709                                        &mut children,
710                                        &mut current,
711                                        view_rect,
712                                        child_line_index,
713                                        view_mode,
714                                        value_mode,
715                                        QueueItem::SpanGraph(graph),
716                                        filtered,
717                                    );
718                                }
719                            }
720                        }
721                    }
722                }
723                QueueItem::SpanGraph(span_graph) => {
724                    let (selected_view_mode, inherit) = self
725                        .span_options
726                        .get(&span_graph.id())
727                        .and_then(|o| o.view_mode)
728                        .unwrap_or((view_mode, false));
729
730                    let view_mode = if inherit {
731                        selected_view_mode
732                    } else {
733                        view_mode
734                    };
735                    if selected_view_mode.bottom_up() {
736                        let bottom_up = span_graph.bottom_up();
737                        if selected_view_mode.aggregate_children() {
738                            let bottom_up = if selected_view_mode.sort_children() {
739                                Either::Left(bottom_up.sorted_by_cached_key(|child| {
740                                    Reverse(value_mode.value_from_bottom_up(child))
741                                }))
742                            } else {
743                                Either::Right(bottom_up)
744                            };
745                            for child in bottom_up {
746                                // TODO search
747                                add_child_item(
748                                    &mut children,
749                                    &mut current,
750                                    view_rect,
751                                    child_line_index,
752                                    view_mode,
753                                    value_mode,
754                                    QueueItem::SpanBottomUp(child),
755                                    Some(FilterMode::SelectedItem),
756                                );
757                            }
758                        } else {
759                            let bottom_up = bottom_up
760                                .flat_map(|bottom_up| bottom_up.spans().collect::<Vec<_>>());
761                            let bottom_up = if selected_view_mode.sort_children() {
762                                Either::Left(bottom_up.sorted_by_cached_key(|child| {
763                                    Reverse(value_mode.value_from_bottom_up_span(child))
764                                }))
765                            } else {
766                                Either::Right(bottom_up.sorted_by_key(|child| child.start()))
767                            };
768                            for child in bottom_up {
769                                let filtered = get_filter_mode(child.id());
770                                add_child_item(
771                                    &mut children,
772                                    &mut current,
773                                    view_rect,
774                                    child_line_index,
775                                    view_mode,
776                                    value_mode,
777                                    QueueItem::SpanBottomUpSpan(child),
778                                    filtered,
779                                );
780                            }
781                        }
782                    } else if !selected_view_mode.aggregate_children() && span_graph.count() > 1 {
783                        let spans = if selected_view_mode.sort_children() {
784                            Either::Left(span_graph.root_spans().sorted_by_cached_key(|child| {
785                                Reverse(value_mode.value_from_span(child))
786                            }))
787                        } else {
788                            Either::Right(
789                                span_graph.root_spans().sorted_by_key(|child| child.start()),
790                            )
791                        };
792                        for child in spans {
793                            let filtered = get_filter_mode(child.id());
794                            add_child_item(
795                                &mut children,
796                                &mut current,
797                                view_rect,
798                                child_line_index,
799                                view_mode,
800                                value_mode,
801                                QueueItem::Span(child),
802                                filtered,
803                            );
804                        }
805                    } else {
806                        let events = if selected_view_mode.sort_children() {
807                            Either::Left(span_graph.events().sorted_by_cached_key(|child| {
808                                Reverse(value_mode.value_from_graph_event(child))
809                            }))
810                        } else {
811                            Either::Right(span_graph.events())
812                        };
813                        for child in events {
814                            if let SpanGraphEventRef::Child { graph } = child {
815                                let filtered = if search_mode {
816                                    None
817                                } else {
818                                    Some(FilterMode::SelectedItem)
819                                };
820                                add_child_item(
821                                    &mut children,
822                                    &mut current,
823                                    view_rect,
824                                    child_line_index,
825                                    view_mode,
826                                    value_mode,
827                                    QueueItem::SpanGraph(graph),
828                                    filtered,
829                                );
830                            }
831                        }
832                    }
833                }
834                QueueItem::SpanBottomUp(bottom_up) => {
835                    let view_mode = self
836                        .span_options
837                        .get(&bottom_up.id())
838                        .and_then(|o| o.view_mode)
839                        .map(|(v, _)| v.as_bottom_up())
840                        .unwrap_or(view_mode);
841
842                    if view_mode.aggregate_children() {
843                        let bottom_up = if view_mode.sort_children() {
844                            Either::Left(bottom_up.children().sorted_by_cached_key(|child| {
845                                Reverse(value_mode.value_from_bottom_up(child))
846                            }))
847                        } else {
848                            Either::Right(bottom_up.children())
849                        };
850                        for child in bottom_up {
851                            // TODO search
852                            add_child_item(
853                                &mut children,
854                                &mut current,
855                                view_rect,
856                                child_line_index,
857                                view_mode,
858                                value_mode,
859                                QueueItem::SpanBottomUp(child),
860                                Some(FilterMode::SelectedItem),
861                            );
862                        }
863                    } else {
864                        let spans = if view_mode.sort_children() {
865                            Either::Left(bottom_up.spans().sorted_by_cached_key(|child| {
866                                Reverse(value_mode.value_from_bottom_up_span(child))
867                            }))
868                        } else {
869                            Either::Right(bottom_up.spans().sorted_by_key(|child| child.start()))
870                        };
871                        for child in spans {
872                            let filtered = get_filter_mode(child.id());
873                            add_child_item(
874                                &mut children,
875                                &mut current,
876                                view_rect,
877                                child_line_index,
878                                view_mode,
879                                value_mode,
880                                QueueItem::SpanBottomUpSpan(child),
881                                filtered,
882                            );
883                        }
884                    }
885                }
886                QueueItem::SpanBottomUpSpan(_) => {
887                    // no children
888                }
889            }
890
891            // When span size is smaller than a pixel, we only show the deepest child.
892            if placeholder {
893                let child = children
894                    .into_iter()
895                    .max_by_key(|ChildItem { item, depth, .. }| (item.filtered.is_some(), *depth));
896                if let Some(ChildItem {
897                    item: mut entry, ..
898                }) = child
899                {
900                    entry.placeholder = true;
901                    queue.push(entry);
902                }
903
904                if !skipped_by_focus {
905                    // add span to line
906                    line.push(LineEntry {
907                        start,
908                        width,
909                        secondary: 0,
910                        ty: LineEntryType::Placeholder(filtered),
911                    });
912                }
913            } else {
914                // add children to queue
915                enqueue_children(children, &mut queue);
916
917                // check if we should filter based on width or count
918                if !skipped_by_focus {
919                    let count = match &span {
920                        QueueItem::Span(_) => 1,
921                        QueueItem::SpanGraph(span_graph) => span_graph.count(),
922                        QueueItem::SpanBottomUp(bottom_up) => bottom_up.count(),
923                        QueueItem::SpanBottomUpSpan(_) => 1,
924                    };
925
926                    if let Some(false) = view_rect.count_filter.as_ref().map(|filter| match filter
927                        .op
928                    {
929                        crate::server::Op::Gt => count > filter.value as usize,
930                        crate::server::Op::Lt => count < filter.value as usize,
931                    }) {
932                        filtered = Some(FilterMode::SelectedItem)
933                    }
934
935                    if let Some(false) = view_rect.value_filter.as_ref().map(|filter| match filter
936                        .op
937                    {
938                        crate::server::Op::Gt => width > filter.value,
939                        crate::server::Op::Lt => width < filter.value,
940                    }) {
941                        filtered = Some(FilterMode::SelectedItem)
942                    }
943
944                    // add span to line
945                    line.push(LineEntry {
946                        start,
947                        width,
948                        secondary,
949                        ty: match span {
950                            QueueItem::Span(span) => LineEntryType::Span { span, filtered },
951                            QueueItem::SpanGraph(span_graph) => {
952                                LineEntryType::SpanGraph(span_graph, filtered)
953                            }
954                            QueueItem::SpanBottomUp(bottom_up) => {
955                                LineEntryType::SpanBottomUp(bottom_up, filtered)
956                            }
957                            QueueItem::SpanBottomUpSpan(bottom_up_span) => {
958                                LineEntryType::SpanBottomUpSpan(bottom_up_span, filtered)
959                            }
960                        },
961                    });
962                }
963            }
964        }
965
966        let lines = lines
967            .into_iter()
968            .enumerate()
969            .map(|(y, line)| ViewLineUpdate {
970                y: y as u64,
971                spans: line
972                    .into_iter()
973                    .map(|entry| match entry.ty {
974                        LineEntryType::Placeholder(filtered) => ViewSpan {
975                            id: 0,
976                            start: entry.start,
977                            width: entry.width,
978                            category: String::new(),
979                            text: String::new(),
980                            count: 1,
981                            kind: match filtered {
982                                Some(_) => 1,
983                                None => 11,
984                            },
985                            start_in_parent: 0,
986                            end_in_parent: 0,
987                            secondary: 0,
988                        },
989                        LineEntryType::Span { span, filtered } => {
990                            let (category, text) = span.nice_name();
991                            let mut start_in_parent = 0;
992                            let mut end_in_parent = 0;
993                            if let Some(parent) = span.parent() {
994                                let parent_start = parent.start();
995                                let parent_duration = parent.end() - parent_start;
996                                if !parent_duration.is_zero() {
997                                    start_in_parent = ((span.start() - parent_start) * 10000
998                                        / parent_duration)
999                                        as u32;
1000                                    end_in_parent = ((span.end() - parent_start) * 10000
1001                                        / parent_duration)
1002                                        as u32;
1003                                } else {
1004                                    start_in_parent = 0;
1005                                    end_in_parent = 10000;
1006                                }
1007                            }
1008                            ViewSpan {
1009                                id: if !span.is_root() {
1010                                    span.id().get() as u64
1011                                } else {
1012                                    Default::default()
1013                                },
1014                                start: entry.start,
1015                                width: entry.width,
1016                                category: category.to_string(),
1017                                text: text.to_string(),
1018                                count: 1,
1019                                kind: match filtered {
1020                                    Some(_) => 0,
1021                                    None => 10,
1022                                },
1023                                start_in_parent,
1024                                end_in_parent,
1025                                secondary: entry.secondary,
1026                            }
1027                        }
1028                        LineEntryType::SpanGraph(graph, filtered) => {
1029                            let (category, text) = graph.nice_name();
1030                            ViewSpan {
1031                                id: graph.id().get() as u64,
1032                                start: entry.start,
1033                                width: entry.width,
1034                                category: category.to_string(),
1035                                text: text.to_string(),
1036                                count: graph.count() as u64,
1037                                kind: match filtered {
1038                                    Some(_) => 0,
1039                                    None => 10,
1040                                },
1041                                start_in_parent: 0,
1042                                end_in_parent: 0,
1043                                secondary: entry.secondary,
1044                            }
1045                        }
1046                        LineEntryType::SpanBottomUp(bottom_up, filtered) => {
1047                            let (category, text) = bottom_up.nice_name();
1048                            ViewSpan {
1049                                id: bottom_up.id().get() as u64,
1050                                start: entry.start,
1051                                width: entry.width,
1052                                category: category.to_string(),
1053                                text: text.to_string(),
1054                                count: bottom_up.count() as u64,
1055                                kind: match filtered {
1056                                    Some(_) => 2,
1057                                    None => 12,
1058                                },
1059                                start_in_parent: 0,
1060                                end_in_parent: 0,
1061                                secondary: entry.secondary,
1062                            }
1063                        }
1064                        LineEntryType::SpanBottomUpSpan(bottom_up_span, filtered) => {
1065                            let (category, text) = bottom_up_span.nice_name();
1066                            ViewSpan {
1067                                id: bottom_up_span.id().get() as u64,
1068                                start: entry.start,
1069                                width: entry.width,
1070                                category: category.to_string(),
1071                                text: text.to_string(),
1072                                count: 1,
1073                                kind: match filtered {
1074                                    Some(_) => 2,
1075                                    None => 12,
1076                                },
1077                                start_in_parent: 0,
1078                                end_in_parent: 0,
1079                                secondary: entry.secondary,
1080                            }
1081                        }
1082                    })
1083                    .collect(),
1084            })
1085            .collect();
1086
1087        Update {
1088            lines,
1089            max: max(1, current),
1090        }
1091    }
1092}
1093
1094#[allow(clippy::too_many_arguments)]
1095fn add_child_item<'a>(
1096    children: &mut Vec<ChildItem<'a>>,
1097    current: &mut u64,
1098    view_rect: &ViewRect,
1099    line_index: usize,
1100    view_mode: ViewMode,
1101    value_mode: ValueMode,
1102    child: QueueItem<'a>,
1103    filtered: Option<FilterMode>,
1104) -> bool {
1105    let child_width = child.value(value_mode);
1106    let max_depth = child.max_depth();
1107    let pixel1 = *current * view_rect.horizontal_pixels / view_rect.width;
1108    let pixel2 = ((*current + child_width) * view_rect.horizontal_pixels).div_ceil(view_rect.width);
1109    let start = *current;
1110    *current += child_width;
1111
1112    // filter by view rect (vertical)
1113    if line_index > (view_rect.y + view_rect.height + EXTRA_HEIGHT) as usize {
1114        return false;
1115    }
1116
1117    if line_index > 0 {
1118        // filter by view rect (horizontal)
1119        if start > view_rect.x + view_rect.width * (100 + EXTRA_WIDTH_PERCENTAGE) / 100 {
1120            return false;
1121        }
1122        if *current
1123            < view_rect
1124                .x
1125                .saturating_sub(view_rect.width * EXTRA_WIDTH_PERCENTAGE / 100)
1126        {
1127            return false;
1128        }
1129    }
1130
1131    children.push(ChildItem {
1132        item: QueueItemWithState {
1133            item: child,
1134            line_index,
1135            start,
1136            placeholder: false,
1137            view_mode,
1138            filtered,
1139        },
1140        depth: max_depth,
1141        pixel_range: (pixel1, pixel2),
1142    });
1143
1144    true
1145}
1146
1147const MIN_VISIBLE_PIXEL_SIZE: u64 = 3;
1148
1149fn enqueue_children<'a>(mut children: Vec<ChildItem<'a>>, queue: &mut Vec<QueueItemWithState<'a>>) {
1150    children.reverse();
1151    let mut last_pixel = u64::MAX;
1152    let mut last_max_depth = 0;
1153    for ChildItem {
1154        item: mut entry,
1155        depth: max_depth,
1156        pixel_range: (pixel1, pixel2),
1157    } in children
1158    {
1159        if last_pixel <= pixel1 + MIN_VISIBLE_PIXEL_SIZE {
1160            if last_max_depth < max_depth {
1161                queue.pop();
1162                entry.placeholder = true;
1163            } else {
1164                if let Some(entry) = queue.last_mut() {
1165                    entry.placeholder = true;
1166                }
1167                continue;
1168            }
1169        };
1170        queue.push(entry);
1171        last_max_depth = max_depth;
1172        last_pixel = pixel2;
1173    }
1174}
1175
1176fn get_line<T: Default>(lines: &mut Vec<T>, i: usize) -> &mut T {
1177    if i >= lines.len() {
1178        lines.resize_with(i + 1, || Default::default());
1179    }
1180    &mut lines[i]
1181}
1182
1183struct LineEntry<'a> {
1184    start: u64,
1185    width: u64,
1186    secondary: u64,
1187    ty: LineEntryType<'a>,
1188}
1189
1190enum LineEntryType<'a> {
1191    Placeholder(Option<FilterMode>),
1192    Span {
1193        span: SpanRef<'a>,
1194        filtered: Option<FilterMode>,
1195    },
1196    SpanGraph(SpanGraphRef<'a>, Option<FilterMode>),
1197    SpanBottomUp(SpanBottomUpRef<'a>, Option<FilterMode>),
1198    SpanBottomUpSpan(SpanRef<'a>, Option<FilterMode>),
1199}