1use crate::{trace_header, ParseResult, INIT_RECORD_TYPE};
6use nom::combinator::all_consuming;
7use nom::number::complete::le_u64;
8use nom::Parser;
9use std::time::Duration;
10
11#[derive(Clone, Copy, Debug, PartialEq)]
12pub(crate) struct Ticks(pub(crate) u64);
13
14impl Ticks {
15 pub(crate) fn parse(buf: &[u8]) -> ParseResult<'_, Self> {
16 nom::combinator::map(nom::number::complete::le_u64, Ticks).parse(buf)
17 }
18
19 pub(crate) fn scale(self, ticks_per_second: u64) -> i64 {
20 const NANOS_PER_SECOND: u128 = Duration::from_secs(1).as_nanos() as _;
21 ((self.0 as u128 * NANOS_PER_SECOND) / ticks_per_second as u128)
22 .try_into()
23 .expect("overflowing a signed monotonic timestamp would take ~292 years of uptime")
24 }
25}
26
27#[derive(Clone, Debug, PartialEq)]
28pub(super) struct InitRecord {
29 pub ticks_per_second: u64,
30}
31
32trace_header! {
33 InitHeader (INIT_RECORD_TYPE) {}
34}
35
36impl InitRecord {
37 pub(super) fn parse(buf: &[u8]) -> ParseResult<'_, Self> {
38 let (buf, header) = InitHeader::parse(buf)?;
39 let (rem, payload) = header.take_payload(buf)?;
40 let (empty, ticks_per_second) = all_consuming(le_u64).parse(payload)?;
41 assert!(empty.is_empty(), "all_consuming must not return any remaining buffer");
42 Ok((rem, Self { ticks_per_second }))
43 }
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49 use crate::fxt_builder::FxtBuilder;
50 use crate::RawTraceRecord;
51
52 #[test]
53 fn basic_ticks_to_monotonic() {
54 assert_eq!(
55 Ticks(1024).scale(2_000_000_000),
56 512,
57 "2 billion ticks/sec is twice the rate of the monotonic clock, half as many nanos",
58 );
59
60 assert_eq!(
61 Ticks(1024).scale(500_000_000),
62 2048,
63 "500mm ticks/sec is half the rate of the monotonic clock, twice as many nanos",
64 );
65 }
66
67 #[test]
68 fn init_record() {
69 assert_parses_to_record!(
70 FxtBuilder::new(InitHeader::empty()).atom(2u64.to_le_bytes()).build(),
71 RawTraceRecord::Init(InitRecord { ticks_per_second: 2 }),
72 );
73 }
74}