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
183fn 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 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 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 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 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 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 }
889 }
890
891 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 line.push(LineEntry {
907 start,
908 width,
909 secondary: 0,
910 ty: LineEntryType::Placeholder(filtered),
911 });
912 }
913 } else {
914 enqueue_children(children, &mut queue);
916
917 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 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 if line_index > (view_rect.y + view_rect.height + EXTRA_HEIGHT) as usize {
1114 return false;
1115 }
1116
1117 if line_index > 0 {
1118 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}