Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try to read tsc_freq_khz from kernel if available, for more accurate cycles_per_sec #26

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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]]
Expand Down
28 changes: 26 additions & 2 deletions src/tsc_now.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u64, TscReadError> {
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;
Expand All @@ -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;
Expand Down