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