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
172fn 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 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 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 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 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 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 }
871 }
872
873 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 line.push(LineEntry {
889 start,
890 width,
891 secondary: 0,
892 ty: LineEntryType::Placeholder(filtered),
893 });
894 }
895 } else {
896 enqueue_children(children, &mut queue);
898
899 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 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 if line_index > (view_rect.y + view_rect.height + EXTRA_HEIGHT) as usize {
1096 return false;
1097 }
1098
1099 if line_index > 0 {
1100 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}