diff --git a/Cargo.toml b/Cargo.toml index 4aff190..3773dd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ readme = "README.md" keywords = ["TSC", "clock", "rdtsc", "timing", "nanosecond"] [dependencies] -ctor = "0.1.20" +ctor = "0.2" [target.'cfg(not(target_os = "wasi"))'.dependencies] libc = "0.2" @@ -21,8 +21,8 @@ libc = "0.2" wasi = "0.7" [dev-dependencies] -criterion = "0.3" -quanta = "0.9" +criterion = "0.4" +quanta = "0.11" rand = "0.8" [[bench]] diff --git a/src/tsc_now.rs b/src/tsc_now.rs index 68deb85..1f78c49 100644 --- a/src/tsc_now.rs +++ b/src/tsc_now.rs @@ -216,13 +216,37 @@ fn is_tsc_percpu_stable() -> bool { f().unwrap_or(false) } +#[derive(Debug)] +enum TscReadError { + FailedToRead(std::io::Error), + FailedToParse((core::num::ParseIntError, String)), +} + +/// Attempts to read the TSC frequency as reported by the linux kernel. +/// This value only exists in Google's production kernel, but it is not upstreamed to the mainline kernel tree. +/// However it is possible to export the value using a custom kernel module: +/// https://github.com/trailofbits/tsc_freq_khz +fn try_read_tsc_freq_khz() -> Result { + let s = std::fs::read_to_string("/sys/devices/system/cpu/cpu0/tsc_freq_khz") + .map_err(TscReadError::FailedToRead)?; + s.trim() + .parse() + .map_err(|e| TscReadError::FailedToParse((e, s))) +} + /// Returns (1) cycles per second and (2) cycles from anchor. /// The result of subtracting `cycles_from_anchor` from newly fetched TSC /// can be used to /// 1. readjust TSC to begin from zero /// 2. sync TSCs between all CPUs fn cycles_per_sec(anchor: Instant) -> (u64, u64) { - let (cps, last_monotonic, last_tsc) = _cycles_per_sec(); + let (cps, last_monotonic, last_tsc) = if let Ok(tsc_freq_khz) = try_read_tsc_freq_khz() { + let (last_monotonic, last_tsc) = monotonic_with_tsc(); + (tsc_freq_khz * 1000, last_monotonic, last_tsc) + } else { + _calculate_cycles_per_sec() + }; + let nanos_from_anchor = (last_monotonic - anchor).as_nanos(); let cycles_flied = cps as f64 * nanos_from_anchor as f64 / 1_000_000_000.0; let cycles_from_anchor = last_tsc - cycles_flied.ceil() as u64; @@ -231,7 +255,7 @@ fn cycles_per_sec(anchor: Instant) -> (u64, u64) { } /// Returns (1) cycles per second, (2) last monotonic time and (3) associated tsc. -fn _cycles_per_sec() -> (u64, Instant, u64) { +fn _calculate_cycles_per_sec() -> (u64, Instant, u64) { let mut cycles_per_sec; let mut last_monotonic; let mut last_tsc;