micromegas_tracing/
time.rs

1//! System & monotonic tick count
2use chrono::{DateTime, Utc};
3
4#[derive(Debug)]
5pub struct DualTime {
6    pub ticks: i64,
7    pub time: DateTime<Utc>,
8}
9
10impl DualTime {
11    pub fn now() -> Self {
12        Self {
13            ticks: now(),
14            time: Utc::now(),
15        }
16    }
17}
18
19#[cfg(windows)]
20pub fn now_windows() -> i64 {
21    unsafe {
22        let mut tick_count = std::mem::zeroed();
23        winapi::um::profileapi::QueryPerformanceCounter(&mut tick_count);
24        *tick_count.QuadPart() as i64
25    }
26}
27
28#[cfg(windows)]
29pub fn freq_windows() -> i64 {
30    unsafe {
31        let mut tick_count = std::mem::zeroed();
32        winapi::um::profileapi::QueryPerformanceFrequency(&mut tick_count);
33        *tick_count.QuadPart() as i64
34    }
35}
36
37#[allow(unreachable_code, clippy::cast_possible_wrap)]
38#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
39pub fn now() -> i64 {
40    #[cfg(windows)]
41    return now_windows();
42
43    //_rdtsc does not wait for previous instructions to be retired
44    // we could use __rdtscp if we needed more precision at the cost of slightly
45    // higher overhead
46    use core::arch::x86_64::_rdtsc;
47    unsafe { _rdtsc() as i64 }
48}
49
50#[allow(clippy::cast_possible_wrap)]
51#[cfg(target_arch = "aarch64")]
52pub fn now() -> i64 {
53    //essentially from https://github.com/sheroz/tick_counter/blob/main/src/lib.rs
54    //(MIT license)
55    let tick_counter: i64;
56    unsafe {
57        core::arch::asm!(
58            "mrs x0, cntvct_el0",
59            out("x0") tick_counter
60        );
61    }
62    tick_counter
63}
64
65#[cfg(target_arch = "wasm32")]
66pub fn now() -> i64 {
67    (js_sys::Date::now() * 1000.0) as i64 // ms → µs
68}
69
70#[allow(unreachable_code)]
71#[cfg(not(target_arch = "wasm32"))]
72pub fn frequency() -> i64 {
73    #[cfg(windows)]
74    return freq_windows();
75
76    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
77    {
78        let cpuid = raw_cpuid::CpuId::new();
79        return cpuid
80            .get_tsc_info()
81            .map(|tsc_info| tsc_info.tsc_frequency().unwrap_or(0))
82            .unwrap_or(0) as i64;
83    }
84    #[cfg(target_arch = "aarch64")]
85    {
86        let counter_frequency: i64;
87        unsafe {
88            core::arch::asm!(
89                "mrs x0, cntfrq_el0",
90                out("x0") counter_frequency
91            );
92        }
93        return counter_frequency;
94    }
95    0
96}
97
98#[cfg(target_arch = "wasm32")]
99pub fn frequency() -> i64 {
100    1_000_000 // µs per second (matches now() which returns µs)
101}
102
103#[allow(unused_imports)]
104#[cfg(test)]
105mod tests {
106    use crate::time::frequency;
107
108    #[test]
109    fn test_frequency() {
110        eprintln!("cpu frequency: {}", frequency());
111    }
112}