tracing_tape_parser/
lib.rs

1//! # Tracing Tape Parser
2//! This craet provides parsing functionality for the `tracing-tape` format.
3//! It is mainly used in the trace-deck crate to parse the tape files.
4
5use std::{fmt::Display, sync::Arc};
6
7use ahash::HashMap;
8use smallvec::SmallVec;
9use tracing_tape::{
10    intro::Intro,
11    record::{
12        field_type, record_kind, CallsiteFieldRecord, CallsiteRecord, EventRecord,
13        EventValueRecord, RecordHeader, SpanCloseRecord, SpanEnterRecord, SpanExitRecord,
14        SpanOpenRecord, SpanOpenRecord2, SpanValueRecord,
15    },
16};
17use zerocopy::FromBytes;
18
19#[derive(Debug)]
20pub enum Value {
21    Bool(bool),
22    I64(i64),
23    U64(u64),
24    I128(i128),
25    U128(u128),
26    F64(f64),
27    String(Arc<str>),
28    Error(Arc<str>),
29}
30
31impl Value {
32    fn parse(kind: u8, data: &[u8]) -> Self {
33        match kind {
34            field_type::BOOL => Value::Bool(data[0] != 0),
35            field_type::I64 => Value::I64(i64::from_le_bytes(data.try_into().unwrap())),
36            field_type::U64 => Value::U64(u64::from_le_bytes(data.try_into().unwrap())),
37            field_type::I128 => Value::I128(i128::from_le_bytes(data.try_into().unwrap())),
38            field_type::U128 => Value::U128(u128::from_le_bytes(data.try_into().unwrap())),
39            field_type::F64 => Value::F64(f64::from_le_bytes(data.try_into().unwrap())),
40            field_type::STR => {
41                let value = Arc::from(String::from_utf8_lossy(data));
42                Value::String(value)
43            }
44            field_type::ERROR => {
45                let value = Arc::from(String::from_utf8_lossy(data));
46                Value::Error(value)
47            }
48            _ => {
49                panic!("unknown field type: {}", kind);
50            }
51        }
52    }
53}
54
55impl Display for Value {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        match self {
58            Value::Bool(value) => value.fmt(f),
59            Value::I64(value) => value.fmt(f),
60            Value::U64(value) => value.fmt(f),
61            Value::I128(value) => value.fmt(f),
62            Value::U128(value) => value.fmt(f),
63            Value::F64(value) => value.fmt(f),
64            Value::String(value) => value.fmt(f),
65            Value::Error(value) => value.fmt(f),
66        }
67    }
68}
69
70#[derive(Debug)]
71struct Intermediate {
72    min_timestamp: i64,
73    max_timestamp: i64,
74
75    /// Callsites where not all fields have been parsed yet.
76    intermediate_callsites: HashMap<u64, IntermediateCallsite>,
77
78    /// Fully parsed callsites.
79    callsites: Vec<IntermediateCallsite>,
80
81    /// Events where not all values have been parsed yet.
82    ///
83    /// The key is the thread_id.
84    intermediate_events: HashMap<u64, IntermediateEvent>,
85
86    /// Complete events.
87    events: Vec<IntermediateEvent>,
88
89    span_graph:
90        petgraph::stable_graph::StableGraph<IntermediateSpan, (), petgraph::Directed, usize>,
91    root_nodes: Vec<petgraph::stable_graph::NodeIndex<usize>>,
92    opened_spans: HashMap<u64, petgraph::stable_graph::NodeIndex<usize>>,
93    context: HashMap<u64, Vec<petgraph::stable_graph::NodeIndex<usize>>>,
94    threads: HashMap<u64, Option<String>>,
95}
96
97impl Default for Intermediate {
98    fn default() -> Self {
99        Self {
100            min_timestamp: i64::MAX,
101            max_timestamp: i64::MIN,
102
103            intermediate_callsites: HashMap::default(),
104            callsites: Vec::new(),
105
106            intermediate_events: HashMap::default(),
107            events: Vec::new(),
108
109            span_graph: Default::default(),
110            root_nodes: Vec::new(),
111            opened_spans: HashMap::default(),
112            threads: HashMap::default(),
113            context: HashMap::default(),
114        }
115    }
116}
117
118impl Intermediate {
119    fn callsite<'a>(&mut self, slice: &'a [u8]) -> &'a [u8] {
120        let (callsite, remaining) = IntermediateCallsite::parse(slice);
121
122        if callsite.fields.capacity() == 0 {
123            self.callsites.push(callsite);
124        } else {
125            self.intermediate_callsites.insert(callsite.id, callsite);
126        }
127        remaining
128    }
129
130    fn callsite_field<'a>(&mut self, slice: &'a [u8]) -> &'a [u8] {
131        let callsite_field_record = CallsiteFieldRecord::ref_from_prefix(slice).unwrap();
132
133        let name_len = callsite_field_record.field_name_len.get() as usize;
134        let offset = std::mem::size_of::<CallsiteFieldRecord>();
135        let name = &slice[offset..offset + name_len];
136        let name = Arc::from(String::from_utf8_lossy(name));
137
138        let callsite_id = callsite_field_record.callsite_id.get();
139        let mut callsite = self.intermediate_callsites.remove(&callsite_id).unwrap();
140        callsite.fields.push(Field {
141            name,
142            id: callsite_field_record.field_id.get(),
143        });
144        if callsite.fields.len() == callsite.fields.capacity() {
145            self.callsites.push(callsite);
146        } else {
147            self.intermediate_callsites.insert(callsite_id, callsite);
148        }
149
150        &slice[callsite_field_record.header.len.get() as usize..]
151    }
152
153    fn event<'a>(&mut self, slice: &'a [u8]) -> &'a [u8] {
154        let event_record = EventRecord::ref_from_prefix(slice).unwrap();
155
156        // TODO: change once try_insert is stable
157        self.threads
158            .entry(event_record.thread_id.get())
159            .or_insert(None);
160
161        let event = IntermediateEvent {
162            timestamp: event_record.timestamp.get(),
163            callsite_id: event_record.callsite_id.get(),
164            values: Vec::with_capacity(event_record.value_count.get() as usize),
165        };
166
167        let thread_id = event_record.thread_id.get();
168        assert!(!self.intermediate_events.contains_key(&thread_id));
169
170        if event.values.capacity() == 0 {
171            self.events.push(event);
172        } else {
173            self.intermediate_events.insert(thread_id, event);
174        }
175
176        self.min_timestamp = self.min_timestamp.min(event_record.timestamp.get());
177        self.max_timestamp = self.max_timestamp.max(event_record.timestamp.get());
178
179        &slice[event_record.header.len.get() as usize..]
180    }
181
182    fn event_value<'a>(&mut self, slice: &'a [u8]) -> &'a [u8] {
183        let event_value_record = EventValueRecord::ref_from_prefix(slice).unwrap();
184
185        let value_len =
186            event_value_record.header.len.get() as usize - std::mem::size_of::<EventValueRecord>();
187        let value = &slice[std::mem::size_of::<EventValueRecord>()..][..value_len];
188
189        let kind = event_value_record.kind;
190        let value = Value::parse(kind, value);
191
192        let thread_id = event_value_record.thread_id.get();
193        let mut event = self.intermediate_events.remove(&thread_id).unwrap();
194
195        // TODO: use push_within_capacity once it's stable
196        event.values.push(IntermediateValue {
197            value,
198            field_id: event_value_record.field_id.get(),
199        });
200
201        if event.values.len() == event.values.capacity() {
202            self.events.push(event);
203        } else {
204            self.intermediate_events.insert(thread_id, event);
205        }
206
207        &slice[event_value_record.header.len.get() as usize..]
208    }
209
210    fn open_span<'a>(&mut self, slice: &'a [u8]) -> &'a [u8] {
211        let span_record = if slice.len() >= std::mem::size_of::<SpanOpenRecord2>() {
212            SpanOpenRecord2::ref_from_prefix(slice).unwrap().clone()
213        } else if slice.len() >= std::mem::size_of::<SpanOpenRecord>() {
214            SpanOpenRecord::ref_from_prefix(slice)
215                .unwrap()
216                .clone()
217                .into()
218        } else {
219            panic!("invalid span record");
220        };
221
222        let span = IntermediateSpan {
223            id: span_record.span_open_record.id.get(),
224            opened: span_record.span_open_record.timestamp.get(),
225            closed: 0,
226            entrances: SmallVec::new(),
227            callsite_id: span_record.span_open_record.callsite_id.get(),
228            parent: Parent::Contextual,
229            values: HashMap::default(),
230        };
231
232        self.min_timestamp = self
233            .min_timestamp
234            .min(span_record.span_open_record.timestamp.get());
235        self.max_timestamp = self
236            .max_timestamp
237            .max(span_record.span_open_record.timestamp.get());
238
239        let span_id = span.id;
240        let index = self.span_graph.add_node(span);
241        self.opened_spans.insert(span_id, index);
242
243        &slice[span_record.span_open_record.header.len.get() as usize..]
244    }
245
246    fn enter_span<'a>(&mut self, slice: &'a [u8]) -> &'a [u8] {
247        let span_enter_record = SpanEnterRecord::ref_from_prefix(slice).unwrap();
248
249        self.threads
250            .entry(span_enter_record.thread_id.get())
251            .or_insert(None);
252
253        let index = self.opened_spans[&span_enter_record.id.get()];
254        let span = &mut self.span_graph[index];
255        let thread_id = span_enter_record.thread_id.get();
256        span.entrances.push(SpanEntrance {
257            entered: span_enter_record.timestamp.get(),
258            exited: 0,
259            thread_id,
260        });
261        self.context
262            .entry(thread_id)
263            .or_insert_with(Vec::new)
264            .push(index);
265
266        &slice[span_enter_record.header.len.get() as usize..]
267    }
268
269    fn exit_span<'a>(&mut self, slice: &'a [u8]) -> &'a [u8] {
270        let span_exit_record = SpanExitRecord::ref_from_prefix(slice).unwrap();
271
272        let index = self.opened_spans[&span_exit_record.id.get()];
273        let span = &mut self.span_graph[index];
274        let last_entrance = span.entrances.last_mut().unwrap();
275        last_entrance.exited = span_exit_record.timestamp.get();
276
277        assert_eq!(
278            self.context
279                .get_mut(&last_entrance.thread_id)
280                .unwrap()
281                .pop(),
282            Some(index)
283        );
284
285        &slice[span_exit_record.header.len.get() as usize..]
286    }
287
288    fn close_span<'a>(&mut self, slice: &'a [u8]) -> &'a [u8] {
289        let span_record = SpanCloseRecord::ref_from_prefix(slice).unwrap();
290
291        self.min_timestamp = self.min_timestamp.min(span_record.timestamp.get());
292        self.max_timestamp = self.max_timestamp.max(span_record.timestamp.get());
293
294        let span_index = self.opened_spans.remove(&span_record.id.get()).unwrap();
295        let span = &mut self.span_graph[span_index];
296        span.closed = span_record.timestamp.get();
297
298        if matches!(span.parent, Parent::Root) {
299            self.root_nodes.push(span_index);
300        } else if let Some(last_entrance) = span.entrances.last() {
301            let thread_id = last_entrance.thread_id;
302            if let Some(parent_index) = self.context[&thread_id].last() {
303                self.span_graph.add_edge(*parent_index, span_index, ());
304            } else {
305                self.root_nodes.push(span_index);
306            }
307        } else {
308            self.root_nodes.push(span_index);
309        }
310
311        &slice[span_record.header.len.get() as usize..]
312    }
313
314    fn span_value<'a>(&mut self, slice: &'a [u8]) -> &'a [u8] {
315        let span_value_record = SpanValueRecord::ref_from_prefix(slice).unwrap();
316
317        let value_len =
318            span_value_record.header.len.get() as usize - std::mem::size_of::<SpanValueRecord>();
319        let value = &slice[std::mem::size_of::<SpanValueRecord>()..][..value_len];
320
321        let kind = span_value_record.kind;
322        let value = Value::parse(kind, value);
323
324        let span_id = span_value_record.span_id.get();
325        let index = self.opened_spans[&span_id];
326        let span = &mut self.span_graph[index];
327        span.values.insert(span_value_record.field_id.get(), value);
328
329        &slice[span_value_record.header.len.get() as usize..]
330    }
331
332    fn parse(&mut self, mut data: &[u8]) -> Result<(), u8> {
333        while !data.is_empty() {
334            let record_kind = data[0];
335
336            match record_kind {
337                record_kind::NOOP => {
338                    data = &data[1..];
339                }
340                record_kind::CALLSITE => {
341                    data = self.callsite(data);
342                }
343                record_kind::CALLSITE_FIELD => {
344                    data = self.callsite_field(data);
345                }
346                record_kind::SPAN_OPEN => {
347                    data = self.open_span(data);
348                }
349                record_kind::SPAN_ENTER => {
350                    data = self.enter_span(data);
351                }
352                record_kind::SPAN_EXIT => {
353                    data = self.exit_span(data);
354                }
355                record_kind::SPAN_CLOSE => {
356                    data = self.close_span(data);
357                }
358                record_kind::SPAN_VALUE => {
359                    data = self.span_value(data);
360                }
361                record_kind::EVENT => {
362                    data = self.event(data);
363                }
364                record_kind::EVENT_VALUE => {
365                    data = self.event_value(data);
366                }
367                _ => {
368                    let header = RecordHeader::ref_from_prefix(data).unwrap();
369                    data = &data[header.len.get() as usize..];
370                }
371            }
372        }
373
374        Ok(())
375    }
376}
377
378#[derive(Debug)]
379struct IntermediateValue {
380    value: Value,
381    field_id: u64,
382}
383
384#[derive(Debug)]
385struct IntermediateEvent {
386    timestamp: i64,
387    callsite_id: u64,
388    values: Vec<IntermediateValue>,
389}
390
391#[derive(Debug)]
392enum Parent {
393    #[allow(dead_code)]
394    Root,
395    #[allow(dead_code)]
396    Explicit(u64),
397    Contextual,
398}
399
400#[derive(Debug)]
401struct IntermediateSpan {
402    id: u64,
403    opened: i64,
404    closed: i64,
405    entrances: SmallVec<[SpanEntrance; 1]>,
406    callsite_id: u64,
407    parent: Parent,
408    values: HashMap<u64, Value>,
409}
410
411#[derive(Debug)]
412pub struct SpanEntrance {
413    pub entered: i64,
414    pub exited: i64,
415    pub thread_id: u64,
416}
417
418#[derive(Debug)]
419pub struct Span {
420    pub opened: i64,
421    pub closed: i64,
422    pub callsite_index: usize,
423    pub entrances: Arc<[SpanEntrance]>,
424    pub values: Arc<[Value]>,
425}
426
427#[derive(Debug)]
428pub struct Event {
429    pub timestamp: i64,
430    pub callsite_index: usize,
431    pub values: Arc<[Value]>,
432}
433
434#[derive(Debug, Hash, PartialEq, Eq, Clone)]
435pub struct Field {
436    name: Arc<str>,
437    id: u64,
438}
439
440#[derive(Debug)]
441struct IntermediateCallsite {
442    id: u64,
443    kind: tracing::metadata::Kind,
444    level: tracing::Level,
445    name: Arc<str>,
446    target: Arc<str>,
447    module_path: Arc<str>,
448    file: Option<Arc<str>>,
449    line: Option<u32>,
450    fields: Vec<Field>,
451}
452
453#[derive(Debug, Hash, PartialEq, Eq, Clone)]
454pub struct Metadata {
455    pub level: tracing::Level,
456    pub name: Arc<str>,
457    pub target: Arc<str>,
458    pub module_path: Arc<str>,
459    pub file: Option<Arc<str>>,
460    pub line: Option<u32>,
461    pub fields: Arc<[Arc<str>]>,
462}
463
464impl From<IntermediateCallsite> for Metadata {
465    fn from(value: IntermediateCallsite) -> Self {
466        Self {
467            level: value.level,
468            name: value.name,
469            target: value.target,
470            module_path: value.module_path,
471            file: value.file,
472            line: value.line,
473            fields: value
474                .fields
475                .into_iter()
476                .map(|field| field.name)
477                .collect::<Vec<_>>()
478                .into(),
479        }
480    }
481}
482
483#[derive(Debug, Hash, PartialEq, Eq, Clone)]
484pub enum Callsite {
485    Event(Metadata),
486    Span(Metadata),
487}
488
489impl Callsite {
490    pub fn metadata(&self) -> &Metadata {
491        match self {
492            Self::Event(metadata) => metadata,
493            Self::Span(metadata) => metadata,
494        }
495    }
496
497    pub fn kind(&self) -> tracing::metadata::Kind {
498        match self {
499            Self::Event(_) => tracing::metadata::Kind::EVENT,
500            Self::Span(_) => tracing::metadata::Kind::SPAN,
501        }
502    }
503
504    pub fn level(&self) -> tracing::Level {
505        self.metadata().level
506    }
507
508    pub fn name(&self) -> &str {
509        &self.metadata().name
510    }
511
512    pub fn target(&self) -> &str {
513        &self.metadata().target
514    }
515
516    pub fn module_path(&self) -> &str {
517        &self.metadata().module_path
518    }
519
520    pub fn file(&self) -> Option<&str> {
521        self.metadata().file.as_deref()
522    }
523
524    pub fn line(&self) -> Option<u32> {
525        self.metadata().line
526    }
527
528    pub fn fields(&self) -> &[Arc<str>] {
529        &self.metadata().fields
530    }
531}
532
533impl From<IntermediateCallsite> for Callsite {
534    fn from(value: IntermediateCallsite) -> Self {
535        if value.kind == tracing::metadata::Kind::SPAN {
536            Self::Span(value.into())
537        } else {
538            Self::Event(value.into())
539        }
540    }
541}
542
543impl IntermediateCallsite {
544    fn parse(slice: &[u8]) -> (Self, &[u8]) {
545        let callsite_record = CallsiteRecord::ref_from_prefix(slice).unwrap();
546
547        let remaining = &slice[callsite_record.header.len.get() as usize..];
548
549        let slice = &slice[std::mem::size_of::<CallsiteRecord>()..];
550        let (name, slice) = slice.split_at(callsite_record.name_len.get() as usize);
551        let (target, slice) = slice.split_at(callsite_record.target_len.get() as usize);
552        let (module_path, slice) = slice.split_at(callsite_record.module_path_len.get() as usize);
553        let (file, _) = slice.split_at(callsite_record.file_len.get() as usize);
554
555        let name = Arc::from(String::from_utf8_lossy(name));
556        let target = Arc::from(String::from_utf8_lossy(target));
557        let module_path = Arc::from(String::from_utf8_lossy(module_path));
558        let file = if file.is_empty() {
559            None
560        } else {
561            Some(Arc::from(String::from_utf8_lossy(file)))
562        };
563        let line = if callsite_record.line.get() == 0 {
564            None
565        } else {
566            Some(callsite_record.line.get())
567        };
568
569        let callsite = Self {
570            id: callsite_record.id.get(),
571            kind: callsite_record.info.kind().expect("invalid kind"),
572            level: callsite_record.info.level().expect("invalid level"),
573            name,
574            target,
575            module_path,
576            file,
577            line,
578            fields: Vec::with_capacity(callsite_record.field_count.get() as usize),
579        };
580
581        (callsite, remaining)
582    }
583}
584
585#[derive(Debug)]
586pub struct TapeData {
587    min_timestamp: i64,
588    max_timestamp: i64,
589    callsites: Vec<Callsite>,
590    events: Vec<Event>,
591    spans: petgraph::graph::Graph<Span, (), petgraph::Directed, usize>,
592    root_spans: Vec<petgraph::graph::NodeIndex<usize>>,
593    threads: HashMap<u64, Option<String>>,
594}
595
596impl TapeData {
597    fn new(intermediate: Intermediate) -> Self {
598        let mut callsite_map = HashMap::default();
599        let mut callsite_field_map = HashMap::default();
600        let callsites = intermediate
601            .callsites
602            .into_iter()
603            .enumerate()
604            .map(|(index, callsite)| {
605                callsite_map.insert(callsite.id, index);
606
607                for (index, field) in callsite.fields.iter().enumerate() {
608                    callsite_field_map.insert((callsite.id, field.id), index);
609                }
610
611                callsite.into()
612            })
613            .collect::<Vec<_>>();
614
615        let mut events = intermediate.events;
616        events.sort_by_key(|event| event.timestamp);
617        let events = events
618            .into_iter()
619            .map(|event| {
620                let mut values = event.values;
621                values.sort_by_cached_key(|value| {
622                    callsite_field_map[&(event.callsite_id, value.field_id)]
623                });
624                let values = values
625                    .into_iter()
626                    .map(|value| value.value)
627                    .collect::<Vec<_>>();
628
629                Event {
630                    timestamp: event.timestamp,
631                    callsite_index: callsite_map[&event.callsite_id],
632                    values: Arc::from(values.into_boxed_slice()),
633                }
634            })
635            .collect();
636
637        struct SpanMapping {
638            old_children: Vec<petgraph::stable_graph::NodeIndex<usize>>,
639            new_parent: petgraph::graph::NodeIndex<usize>,
640        }
641
642        let mut root_nodes = vec![];
643        let mut intermediate_graph = intermediate.span_graph;
644        let mut spans = petgraph::Graph::with_capacity(
645            intermediate_graph.node_count(),
646            intermediate_graph.edge_count(),
647        );
648        let mut nodes_to_process = Vec::new();
649
650        for node in intermediate.root_nodes {
651            let children = intermediate_graph.neighbors(node).collect::<Vec<_>>();
652            let intermediate_span = intermediate_graph.remove_node(node).unwrap();
653
654            // if !callsite_map.contains_key(&intermediate_span.callsite_id) {
655            //     continue;
656            // }
657
658            let callsite_index = callsite_map[&intermediate_span.callsite_id];
659            let mut values = intermediate_span.values.into_iter().collect::<Vec<_>>();
660            values.sort_by_cached_key(|(field_id, _)| {
661                callsite_field_map[&(intermediate_span.callsite_id, *field_id)]
662            });
663            let value = values
664                .into_iter()
665                .map(|(_, value)| value)
666                .collect::<Vec<_>>();
667
668            let span = Span {
669                callsite_index,
670                opened: intermediate_span.opened,
671                closed: intermediate_span.closed,
672                entrances: Arc::from(intermediate_span.entrances.into_boxed_slice()),
673                values: Arc::from(value.into_boxed_slice()),
674            };
675
676            let span_node = spans.add_node(span);
677            root_nodes.push(span_node);
678            if !children.is_empty() {
679                nodes_to_process.push(SpanMapping {
680                    old_children: children,
681                    new_parent: span_node,
682                });
683            }
684        }
685
686        while let Some(mapping) = nodes_to_process.pop() {
687            let children = mapping.old_children;
688            let parent = mapping.new_parent;
689
690            for child in children {
691                let children = intermediate_graph.neighbors(child).collect::<Vec<_>>();
692                let intermediate_span = intermediate_graph.remove_node(child).unwrap();
693
694                // if !callsite_map.contains_key(&intermediate_span.callsite_id) {
695                //     continue;
696                // }
697
698                let callsite_index = callsite_map[&intermediate_span.callsite_id];
699                let mut values = intermediate_span.values.into_iter().collect::<Vec<_>>();
700                values.sort_by_cached_key(|(field_id, _)| {
701                    callsite_field_map[&(intermediate_span.callsite_id, *field_id)]
702                });
703                let value = values
704                    .into_iter()
705                    .map(|(_, value)| value)
706                    .collect::<Vec<_>>();
707
708                let span = Span {
709                    callsite_index,
710                    opened: intermediate_span.opened,
711                    closed: intermediate_span.closed,
712                    entrances: Arc::from(intermediate_span.entrances.into_boxed_slice()),
713                    values: Arc::from(value.into_boxed_slice()),
714                };
715
716                let span_node = spans.add_node(span);
717                spans.add_edge(parent, span_node, ());
718                if !children.is_empty() {
719                    nodes_to_process.push(SpanMapping {
720                        old_children: children,
721                        new_parent: span_node,
722                    });
723                }
724            }
725        }
726
727        Self {
728            min_timestamp: intermediate.min_timestamp,
729            max_timestamp: intermediate.max_timestamp,
730            callsites,
731            events,
732            spans,
733            root_spans: root_nodes,
734            threads: intermediate.threads,
735        }
736    }
737}
738
739#[derive(Debug)]
740pub struct Tape {
741    intro: Intro,
742    data: TapeData,
743}
744
745impl Tape {
746    pub fn parse(data: &[u8]) -> Self {
747        let intro = Intro::read_from_prefix(data).unwrap();
748
749        let mut intermediate = Intermediate::default();
750        intermediate
751            .parse(&data[std::mem::size_of::<Intro>()..])
752            .unwrap();
753
754        let data = TapeData::new(intermediate);
755
756        Self { intro, data }
757    }
758
759    pub fn time_range(&self) -> std::ops::RangeInclusive<i128> {
760        let start = self.intro.timestamp_base.get();
761        let end = start + self.data.max_timestamp as i128;
762        start..=end
763    }
764
765    pub fn timestamp_range(&self) -> std::ops::RangeInclusive<i64> {
766        self.data.min_timestamp..=self.data.max_timestamp
767    }
768
769    pub fn events(&self) -> &[Event] {
770        &self.data.events
771    }
772
773    pub fn callsites(&self) -> &[Callsite] {
774        &self.data.callsites
775    }
776
777    pub fn root_spans(&self) -> &[petgraph::graph::NodeIndex<usize>] {
778        &self.data.root_spans
779    }
780
781    pub fn spans(&self) -> &petgraph::graph::Graph<Span, (), petgraph::Directed, usize> {
782        &self.data.spans
783    }
784
785    pub fn threads(&self) -> &HashMap<u64, Option<String>> {
786        &self.data.threads
787    }
788}