blob: 7461f0d747056bff0e457a8c125cc44c712f6cfb [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![warn(missing_docs)]
//! `time_test_client` is a simple component that logs diagnostic information about the time it
//! receives from the system to aid in debugging and (potentially in the future) automated testing.
use {
anyhow::{Context as _, Error},
chrono::prelude::*,
fuchsia_async as fasync, fuchsia_runtime as runtime, fuchsia_zircon as zx,
futures::prelude::*,
lazy_static::lazy_static,
log::{info, warn},
};
/// Delay between polls of system and userspace clocks.
const POLL_DELAY: zx::Duration = zx::Duration::from_seconds(2);
lazy_static! {
/// Rights to request when duplicating handles to userspace clocks.
static ref CLOCK_RIGHTS: zx::Rights = zx::Rights::READ | zx::Rights::DUPLICATE | zx::Rights::WAIT;
}
#[fasync::run_singlethreaded]
async fn main() {
fuchsia_syslog::init_with_tags(&["time"]).context("initializing logging").unwrap();
fuchsia_syslog::set_severity(fuchsia_syslog::levels::INFO);
let mut futures = vec![];
RuntimeUtcMonitor::new();
futures.push(KernelUtcMonitor::new().execute().boxed());
match ClockMonitor::new() {
Ok(clock_monitor) => futures.push(clock_monitor.execute().boxed()),
Err(err) => warn!("{}", err),
}
future::join_all(futures).await;
}
/// Returns a standard format timestamp for a zx::Time.
fn zx_timestamp(time: &zx::Time) -> String {
chrono_timestamp(&Utc.timestamp_nanos(time.into_nanos()))
}
/// Returns a standard format timestamp for a chrono::DateTime.
fn chrono_timestamp(time: &DateTime<Utc>) -> String {
(&time.to_rfc3339()[0..23]).to_string()
}
/// A monitor for UTC as reported by the runtime.
struct RuntimeUtcMonitor {
/// The UTC time when this monitor was initialized.
initial: DateTime<Utc>,
}
impl RuntimeUtcMonitor {
/// Creates a new `RuntimeUtcMonitor`, logging the initial state.
pub fn new() -> Self {
let initial = Utc::now();
info!("Runtime UTC at initialization: {}", chrono_timestamp(&initial));
RuntimeUtcMonitor { initial }
}
/// Async function to operate this monitor. Currently unused because we don't need to dump both
/// the runtime and kernel clocks every minute, but when the Kernel clock goes away we'll need
/// this again.
#[allow(unused)]
async fn execute(self) {
let mut last_logged = self.initial;
loop {
fasync::Timer::new(fasync::Time::after(POLL_DELAY)).await;
// Only log UTC when we reach a new minute.
let current = Utc::now();
if current.hour() != last_logged.hour() || current.minute() != last_logged.minute() {
info!("Runtime UTC: {}", chrono_timestamp(&current));
last_logged = current;
}
}
}
}
/// A monitor for UTC as reported by the kernel.
struct KernelUtcMonitor {
/// The UTC time when this monitor was initialized.
initial: DateTime<Utc>,
}
// Deprecated zx::Time::get calls are used here for debugging the kernel UTC clock. Do not copy.
#[allow(deprecated)]
impl KernelUtcMonitor {
/// Creates a new `KernelUtcMonitor`, logging the initial state.
pub fn new() -> Self {
let initial = Utc.timestamp_nanos(zx::Time::get(zx::ClockId::UTC).into_nanos());
info!("Kernel UTC at initialization: {}", initial);
KernelUtcMonitor { initial }
}
/// Async function to operate this monitor.
async fn execute(self) {
let mut last_logged = self.initial;
loop {
fasync::Timer::new(fasync::Time::after(POLL_DELAY)).await;
// Only log Kernel UTC when we reach a new minute.
let current = Utc.timestamp_nanos(zx::Time::get(zx::ClockId::UTC).into_nanos());
if current.hour() != last_logged.hour() || current.minute() != last_logged.minute() {
info!("Kernel UTC: {}", chrono_timestamp(&current));
last_logged = current;
}
}
}
}
/// A monitor for a UTC `zx::Clock` to log changes in clock details.
struct ClockMonitor {
/// The `zx::Clock` to be monitored.
clock: zx::Clock,
/// The generation counter that was present during initialization.
initial_generation: u32,
}
impl ClockMonitor {
/// Creates a new `ClockMonitor`, logging the initial state, or returns an error if a clock
/// could not be found.
pub fn new() -> Result<Self, Error> {
// Retrieve a handle to the UTC clock.
let clock = runtime::duplicate_utc_clock_handle(*CLOCK_RIGHTS)
.map_err(|stat| anyhow::anyhow!("Error retreiving userspace UTC handle: {}", stat))?;
// Log the time reported by the clock.
match clock.read() {
Ok(time) => info!("Userspace UTC at initialization: {}", zx_timestamp(&time)),
Err(stat) => warn!("Error reading userspace UTC initial time {}", stat),
}
// Log the initial details and backstop time on creation
match clock.get_details() {
Ok(details) => {
info!("Userspace UTC backstop time: {}", zx_timestamp(&details.backstop));
info!(
"Userspace UTC details at initialization: {}",
Self::describe_clock_details(&details)
);
Ok(ClockMonitor { clock, initial_generation: details.generation_counter })
}
Err(stat) => {
warn!("Error reading userspace UTC details {}", stat);
Ok(ClockMonitor { clock, initial_generation: 0 })
}
}
}
/// Async function to operate this monitor.
async fn execute(self) {
// Future to log when the clock started signal is observed.
let clock_start_fut = async {
match fasync::OnSignals::new(&self.clock, zx::Signals::CLOCK_STARTED).await {
Ok(_) => info!("Userspace UTC has started"),
Err(err) => warn!("Error waiting for userspace UTC start: {}", err),
}
};
// Future to log every time a new generation_counter is seen (note we don't print errors on
// failing to read details, such a failure is likely persistent and would be spammy).
let generation_change_fut = async {
let mut last_logged = self.initial_generation;
loop {
fasync::Timer::new(fasync::Time::after(POLL_DELAY)).await;
if let Ok(details) = self.clock.get_details() {
if details.generation_counter != last_logged {
info!(
"Updated userspace UTC details: {}",
Self::describe_clock_details(&details)
);
match self.clock.read() {
Ok(time) => info!("Updated userspace UTC: {}", zx_timestamp(&time)),
Err(stat) => warn!("Error reading userspace UTC after update {}", stat),
}
last_logged = details.generation_counter;
}
}
}
};
future::join(clock_start_fut, generation_change_fut).await;
}
/// Returns a string description of the most useful information in a `zx::ClockDetails`.
fn describe_clock_details(clock_details: &zx::ClockDetails) -> String {
let transform = &clock_details.mono_to_synthetic;
format!(
"gen={}, offset={:.1}s, rate={}/{}, err_bound={:.3}ms",
clock_details.generation_counter,
(transform.synthetic_offset - transform.reference_offset) as f64 / 1e9f64,
transform.rate.synthetic_ticks,
transform.rate.reference_ticks,
(clock_details.error_bounds as f64) / 1e6f64,
)
}
}