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