elfo_core/tracing/
trace_id.rs

1use std::{
2    convert::TryFrom,
3    num::{NonZeroU64, TryFromIntError},
4    time::SystemTime,
5};
6
7use derive_more::{Deref, Display, From, Into};
8use serde::{Deserialize, Serialize};
9
10use crate::node::NodeNo;
11
12/// The struct that represents the trace id layout. It's built around
13/// `NonZeroU64` for now.
14// TODO(v0.2): remove `derive(Deserialize)`.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16#[derive(Serialize, Deserialize, Into, From, Display)]
17#[display(fmt = "{_0}")]
18pub struct TraceId(NonZeroU64);
19
20impl TraceId {
21    pub(crate) fn from_layout(layout: TraceIdLayout) -> Self {
22        let raw = (u64::from(*layout.timestamp)) << 38
23            | u64::from(layout.node_no) << 22
24            | u64::from(*layout.bottom);
25
26        Self::try_from(raw).unwrap()
27    }
28
29    pub(crate) fn to_layout(self) -> TraceIdLayout {
30        let raw = self.0.get();
31
32        TraceIdLayout {
33            timestamp: TruncatedTime((raw >> 38) as u32 & 0x1ff_ffff),
34            node_no: (raw >> 22 & 0xffff) as NodeNo,
35            bottom: Bottom((raw & 0x3f_ffff) as u32),
36        }
37    }
38}
39
40impl TryFrom<u64> for TraceId {
41    type Error = TryFromIntError;
42
43    #[inline]
44    fn try_from(raw: u64) -> Result<Self, Self::Error> {
45        NonZeroU64::try_from(raw).map(TraceId)
46    }
47}
48
49impl From<TraceId> for u64 {
50    #[inline]
51    fn from(trace_id: TraceId) -> Self {
52        trace_id.0.get()
53    }
54}
55
56// === TraceIdLayout ===
57
58#[derive(Clone, Copy)]
59pub(crate) struct TraceIdLayout {
60    pub(crate) timestamp: TruncatedTime,
61    pub(crate) node_no: NodeNo,
62    pub(crate) bottom: Bottom,
63}
64
65/// 25-bit time in seconds.
66#[derive(Clone, Copy, Deref)]
67pub(crate) struct TruncatedTime(u32);
68
69impl TruncatedTime {
70    pub(crate) fn abs_delta(self, other: TruncatedTime) -> u32 {
71        let a = self.0.wrapping_sub(other.0) & 0x1ff_ffff;
72        let b = other.0.wrapping_sub(self.0) & 0x1ff_ffff;
73        a.min(b)
74    }
75}
76
77impl From<SystemTime> for TruncatedTime {
78    fn from(time: SystemTime) -> Self {
79        let unixtime = time
80            .duration_since(SystemTime::UNIX_EPOCH)
81            .expect("invalid system time")
82            .as_secs();
83
84        Self(unixtime as u32 & 0x1ff_ffff)
85    }
86}
87
88/// 22-bit bottom part (usually, a counter).
89#[derive(Clone, Copy, Deref)]
90pub(crate) struct Bottom(u32);
91
92impl From<u32> for Bottom {
93    fn from(value: u32) -> Self {
94        debug_assert!((1..=0x3f_ffff).contains(&value));
95        Self(value)
96    }
97}
98
99#[test]
100fn truncated_time_delta_secs() {
101    let check = |a, b, expected| {
102        assert_eq!(
103            TruncatedTime(a).abs_delta(TruncatedTime(b)),
104            expected,
105            "{a} abs {b} != {expected}",
106        );
107    };
108
109    check(5, 5, 0);
110    check(25, 50, 25);
111    check((1 << 25) - 15, (1 << 25) - 5, 10);
112    check(0, (1 << 25) - 5, 5);
113    check(1, (1 << 25) - 5, 6);
114}
115
116#[test]
117fn layout_roundtrip() {
118    fn check(raw_trace_id: u64) {
119        let trace_id = TraceId::try_from(raw_trace_id).unwrap();
120        let layout = trace_id.to_layout();
121        assert_eq!(TraceId::from_layout(layout), trace_id);
122    }
123
124    check(1);
125    check(9223372036854775807);
126
127    // Just random numbers.
128    check(36203675);
129    check(75997165362483795);
130    check(276561162443934569);
131    check(2765611624439345695);
132    check(5197794958151101819);
133    check(8446744073709551614);
134}