turbo_trace_server/
span.rs

1use std::{
2    num::NonZeroUsize,
3    sync::{Arc, OnceLock},
4};
5
6use hashbrown::HashMap;
7
8use crate::timestamp::Timestamp;
9
10pub type SpanIndex = NonZeroUsize;
11
12pub struct Span {
13    // These values won't change after creation:
14    pub parent: Option<SpanIndex>,
15    pub depth: u32,
16    pub start: Timestamp,
17    pub category: String,
18    pub name: String,
19    pub args: Vec<(String, String)>,
20
21    // This might change during writing:
22    pub events: Vec<SpanEvent>,
23    pub is_complete: bool,
24
25    // These values are computed automatically:
26    pub self_allocations: u64,
27    pub self_allocation_count: u64,
28    pub self_deallocations: u64,
29    pub self_deallocation_count: u64,
30
31    // These values are computed when accessed (and maybe deleted during writing):
32    pub max_depth: OnceLock<u32>,
33    pub total_allocations: OnceLock<u64>,
34    pub total_deallocations: OnceLock<u64>,
35    pub total_persistent_allocations: OnceLock<u64>,
36    pub total_span_count: OnceLock<u64>,
37    pub total_allocation_count: OnceLock<u64>,
38
39    // More nested fields, but memory lazily allocated
40    pub time_data: OnceLock<Box<SpanTimeData>>,
41    pub extra: OnceLock<Box<SpanExtra>>,
42    pub names: OnceLock<Box<SpanNames>>,
43}
44
45#[derive(Default)]
46pub struct SpanTimeData {
47    // These values won't change after creation:
48    pub ignore_self_time: bool,
49
50    // This might change during writing:
51    pub self_end: Timestamp,
52
53    // These values are computed automatically:
54    pub self_time: Timestamp,
55
56    // These values are computed when accessed (and maybe deleted during writing):
57    pub end: OnceLock<Timestamp>,
58    pub total_time: OnceLock<Timestamp>,
59    pub corrected_self_time: OnceLock<Timestamp>,
60    pub corrected_total_time: OnceLock<Timestamp>,
61}
62
63#[derive(Default)]
64pub struct SpanExtra {
65    pub graph: OnceLock<Vec<SpanGraphEvent>>,
66    pub bottom_up: OnceLock<Vec<Arc<SpanBottomUp>>>,
67    pub search_index: OnceLock<HashMap<String, Vec<SpanIndex>>>,
68}
69
70#[derive(Default)]
71pub struct SpanNames {
72    // These values are computed when accessed (and maybe deleted during writing):
73    pub nice_name: OnceLock<(String, String)>,
74    pub group_name: OnceLock<String>,
75}
76
77impl Span {
78    pub fn time_data(&self) -> &SpanTimeData {
79        self.time_data.get_or_init(|| {
80            Box::new(SpanTimeData {
81                self_end: self.start,
82                ignore_self_time: &self.name == "thread",
83                ..Default::default()
84            })
85        })
86    }
87
88    pub fn time_data_mut(&mut self) -> &mut SpanTimeData {
89        self.time_data();
90        self.time_data.get_mut().unwrap()
91    }
92
93    pub fn extra(&self) -> &SpanExtra {
94        self.extra.get_or_init(Default::default)
95    }
96
97    pub fn names(&self) -> &SpanNames {
98        self.names.get_or_init(Default::default)
99    }
100}
101
102#[derive(Copy, Clone, PartialEq, Eq)]
103pub enum SpanEvent {
104    SelfTime { start: Timestamp, end: Timestamp },
105    Child { index: SpanIndex },
106}
107
108#[derive(Clone)]
109pub enum SpanGraphEvent {
110    // TODO(sokra) use events instead of children for visualizing span graphs
111    #[allow(dead_code)]
112    SelfTime {
113        duration: Timestamp,
114    },
115    Child {
116        child: Arc<SpanGraph>,
117    },
118}
119
120pub struct SpanGraph {
121    // These values won't change after creation:
122    pub root_spans: Vec<SpanIndex>,
123    pub recursive_spans: Vec<SpanIndex>,
124
125    // These values are computed when accessed:
126    pub max_depth: OnceLock<u32>,
127    pub events: OnceLock<Vec<SpanGraphEvent>>,
128    pub self_time: OnceLock<Timestamp>,
129    pub self_allocations: OnceLock<u64>,
130    pub self_deallocations: OnceLock<u64>,
131    pub self_persistent_allocations: OnceLock<u64>,
132    pub self_allocation_count: OnceLock<u64>,
133    pub total_time: OnceLock<Timestamp>,
134    pub total_allocations: OnceLock<u64>,
135    pub total_deallocations: OnceLock<u64>,
136    pub total_persistent_allocations: OnceLock<u64>,
137    pub total_allocation_count: OnceLock<u64>,
138    pub total_span_count: OnceLock<u64>,
139    pub corrected_self_time: OnceLock<Timestamp>,
140    pub corrected_total_time: OnceLock<Timestamp>,
141    pub bottom_up: OnceLock<Vec<Arc<SpanBottomUp>>>,
142}
143
144pub struct SpanBottomUp {
145    // These values won't change after creation:
146    pub self_spans: Vec<SpanIndex>,
147    pub children: Vec<Arc<SpanBottomUp>>,
148    pub example_span: SpanIndex,
149
150    // These values are computed when accessed:
151    pub max_depth: OnceLock<u32>,
152    pub events: OnceLock<Vec<SpanGraphEvent>>,
153    pub self_time: OnceLock<Timestamp>,
154    pub corrected_self_time: OnceLock<Timestamp>,
155    pub self_allocations: OnceLock<u64>,
156    pub self_deallocations: OnceLock<u64>,
157    pub self_persistent_allocations: OnceLock<u64>,
158    pub self_allocation_count: OnceLock<u64>,
159}
160
161impl SpanBottomUp {
162    pub fn new(
163        self_spans: Vec<SpanIndex>,
164        example_span: SpanIndex,
165        children: Vec<Arc<SpanBottomUp>>,
166    ) -> Self {
167        Self {
168            self_spans,
169            children,
170            example_span,
171            max_depth: OnceLock::new(),
172            events: OnceLock::new(),
173            self_time: OnceLock::new(),
174            corrected_self_time: OnceLock::new(),
175            self_allocations: OnceLock::new(),
176            self_deallocations: OnceLock::new(),
177            self_persistent_allocations: OnceLock::new(),
178            self_allocation_count: OnceLock::new(),
179        }
180    }
181}