From 1901caa83fde43cdd097208fa8e6a8cb4d8285e0 Mon Sep 17 00:00:00 2001 From: "Rivas Paz, Jose L" Date: Thu, 3 Sep 2026 09:03:52 +0200 Subject: [PATCH] lib: include extraction time in Crash Log `METADATA` This patch includes the extraction time in the Crash Log `METADATA` when the Crash Log source executes its extraction flow. Each Crash Log extraction source should add explicit code to read and assign the extraction time in their own extraction path. The time is expressed in local time of the system; a new API returns the current instant: ``` use intel_crashlog::metadata::Time; if let Some(time) = Time::now() { println!("{}", time) } ``` Some Crash Log sources, call directly the `Time::now()` API, for getting the current instance Signed-off-by: Rivas Paz, Jose L --- app/Cargo.lock | 2 + lib/Cargo.lock | 41 +++++++++++++++++ lib/Cargo.toml | 13 +++++- lib/src/metadata.rs | 56 ++++++++++------------- lib/src/metadata/time.rs | 80 +++++++++++++++++++++++++++++++++ lib/src/source.rs | 1 - lib/src/source/acpi.rs | 2 +- lib/src/source/acpi/efi.rs | 11 +---- lib/src/source/acpi/sysfs.rs | 10 ++++- lib/src/source/event_log/win.rs | 2 + lib/src/source/pmt/sysfs.rs | 10 ++++- 11 files changed, 179 insertions(+), 49 deletions(-) create mode 100644 lib/src/metadata/time.rs diff --git a/app/Cargo.lock b/app/Cargo.lock index 5b46250..8928e98 100644 --- a/app/Cargo.lock +++ b/app/Cargo.lock @@ -177,6 +177,7 @@ version = "1.1.0" dependencies = [ "acpi", "cargo-emit", + "jiff", "log", "serde", "serde_json", @@ -220,6 +221,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", + "windows-link", ] [[package]] diff --git a/lib/Cargo.lock b/lib/Cargo.lock index 9ac1f35..c1e82b3 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -201,6 +201,7 @@ dependencies = [ "acpi", "cargo-emit", "cbindgen", + "jiff", "log", "serde", "serde_json", @@ -223,6 +224,31 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jiff" +version = "0.2.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link", +] + +[[package]] +name = "jiff-static" +version = "0.2.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "libc" version = "0.2.186" @@ -259,6 +285,21 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + [[package]] name = "proc-macro2" version = "1.0.106" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 29adc75..8a21e73 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -47,7 +47,8 @@ serialize = [ "dep:serde_json", ] std = [ - "serde/std" + "serde/std", + "dep:jiff", ] [dev-dependencies] @@ -64,6 +65,16 @@ log = "0.4" version = "5" default-features = false +[dependencies.jiff] +version = "0.2" +default-features = false +features = [ + "std", + "tz-system", + "tzdb-zoneinfo", +] +optional = true + [dependencies.serde] version = "1" optional = true diff --git a/lib/src/metadata.rs b/lib/src/metadata.rs index ebbffa2..acadcbb 100644 --- a/lib/src/metadata.rs +++ b/lib/src/metadata.rs @@ -3,6 +3,8 @@ //! Information extracted alongside the Crash Log records. +mod time; + #[cfg(not(feature = "std"))] use alloc::vec::Vec; #[cfg(not(feature = "std"))] @@ -13,6 +15,8 @@ use std::fmt; use crate::cper::CperSectionBody; use crate::source::CrashLogSource; +pub use time::Time; + /// Crash Log Metadata #[derive(Default, Clone)] pub struct Metadata { @@ -27,41 +31,29 @@ pub struct Metadata { pub extra_cper_sections: Vec, } -/// Crash Log Extraction Time -#[derive(Clone)] -pub struct Time { - pub year: u16, - pub month: u8, - pub day: u8, - pub hour: u8, - pub minute: u8, -} - impl fmt::Display for Metadata { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match ( - self.computer.as_ref(), - self.source.as_ref(), - self.time.as_ref(), - ) { - (Some(computer), Some(source), Some(time)) => write!(f, "{computer}-{source}-{time}"), - (Some(computer), None, Some(time)) => write!(f, "{computer}-{time}"), - (None, None, Some(time)) => write!(f, "{time}"), - (None, Some(source), Some(time)) => write!(f, "{source}-{time}"), - (Some(computer), None, None) => write!(f, "{computer}"), - (Some(computer), Some(source), None) => write!(f, "{computer}-{source}"), - (None, Some(source), None) => write!(f, "{source}"), - (None, None, None) => write!(f, "unnamed"), + let mut sep = ""; + + if let Some(computer) = &self.computer { + write!(f, "{computer}")?; + sep = "-"; } - } -} -impl fmt::Display for Time { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{:04}-{:02}-{:02}-{:02}-{:02}", - self.year, self.month, self.day, self.hour, self.minute - ) + if let Some(source) = &self.source { + write!(f, "{sep}{source}")?; + sep = "-"; + } + + if let Some(time) = &self.time { + write!(f, "{sep}{time}")?; + sep = "-"; + } + + if sep.is_empty() { + write!(f, "unnamed")?; + } + + Ok(()) } } diff --git a/lib/src/metadata/time.rs b/lib/src/metadata/time.rs new file mode 100644 index 0000000..1c01b46 --- /dev/null +++ b/lib/src/metadata/time.rs @@ -0,0 +1,80 @@ +// Copyright (C) 2026 Intel Corporation +// SPDX-License-Identifier: MIT + +#[cfg(not(feature = "std"))] +use alloc::fmt; +#[cfg(feature = "std")] +use std::fmt; + +/// Crash Log Extraction Time, expressed in the local time of the system +#[derive(Clone)] +pub struct Time { + pub year: u16, + pub month: u8, + pub day: u8, + pub hour: u8, + pub minute: u8, + pub second: u8, + pub millisecond: u16, +} + +impl fmt::Display for Time { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{:04}{:02}{:02}T{:02}{:02}{:02}.{:03}", + self.year, self.month, self.day, self.hour, self.minute, self.second, self.millisecond, + ) + } +} + +impl Time { + /// Returns the current [Time], or [None] if it cannot be determined + /// + /// The time is read from the operating system + #[cfg(feature = "std")] + pub fn now() -> Option { + use jiff::{Timestamp, tz}; + + let now = Timestamp::try_from(std::time::SystemTime::now()) + .inspect_err(|err| log::info!("Cannot get the current time: {err}")) + .ok()?; + + let tz = tz::TimeZone::system(); + + if tz == tz::TimeZone::unknown() { + log::warn!("Cannot determine the system time zone"); + } + + let local = now.to_zoned(tz).datetime(); + Some(Time { + year: local.year() as u16, + month: local.month() as u8, + day: local.day() as u8, + hour: local.hour() as u8, + minute: local.minute() as u8, + second: local.second() as u8, + millisecond: local.millisecond() as u16, + }) + } + + /// Returns the current [Time], or [None] if it cannot be determined + /// + /// The time is read from the UEFI runtime services + #[cfg(all(not(feature = "std"), target_os = "uefi"))] + pub fn now() -> Option { + let time = uefi::runtime::get_time() + .inspect_err(|err| log::info!("Cannot get RTC time: {err}")) + .ok()?; + + Some(Time { + year: time.year(), + month: time.month(), + day: time.day(), + hour: time.hour(), + minute: time.minute(), + second: time.second(), + millisecond: (time.nanosecond() / 1_000_000) as u16, + }) + } +} diff --git a/lib/src/source.rs b/lib/src/source.rs index b0086d4..e4ea8ce 100644 --- a/lib/src/source.rs +++ b/lib/src/source.rs @@ -198,7 +198,6 @@ impl CrashLogSource { crashlog.metadata.source = Some(self.clone()); } } - crashlogs } diff --git a/lib/src/source/acpi.rs b/lib/src/source/acpi.rs index 7b8616a..29a1a22 100644 --- a/lib/src/source/acpi.rs +++ b/lib/src/source/acpi.rs @@ -26,7 +26,7 @@ impl Acpi { #[cfg(target_os = "linux")] { match self.sysfs.extract() { - Ok(crashlogs) => return Ok(crashlogs), + Ok(crashlog) => return Ok(crashlog), Err(err) => log::error!("Cannot extract Crash Log from ACPI sysfs: {err}"), } } diff --git a/lib/src/source/acpi/efi.rs b/lib/src/source/acpi/efi.rs index 0fcaa33..0ebc148 100644 --- a/lib/src/source/acpi/efi.rs +++ b/lib/src/source/acpi/efi.rs @@ -82,16 +82,7 @@ pub(super) fn extract_crashlog() -> Result { crashlog.metadata = metadata::Metadata { computer: Some("efi".to_string()), - time: uefi::runtime::get_time() - .map(|time| metadata::Time { - year: time.year(), - month: time.month(), - day: time.day(), - hour: time.hour(), - minute: time.minute(), - }) - .inspect_err(|err| log::warn!("Cannot get time: {err}")) - .ok(), + time: metadata::Time::now(), ..Default::default() }; diff --git a/lib/src/source/acpi/sysfs.rs b/lib/src/source/acpi/sysfs.rs index 91c9a51..27227c8 100644 --- a/lib/src/source/acpi/sysfs.rs +++ b/lib/src/source/acpi/sysfs.rs @@ -4,6 +4,7 @@ use crate::CrashLog; use crate::bert::Berr; use crate::error::Error; +use crate::metadata::Time; use std::path::{Path, PathBuf}; pub(super) struct AcpiSysFs { @@ -54,7 +55,9 @@ impl AcpiSysFs { Berr::from_slice(&berr).ok_or(Error::InvalidBootErrorRecordRegion) })?; - CrashLog::from_berr(berr) + let mut crashlog = CrashLog::from_berr(berr)?; + crashlog.metadata.time = Time::now(); + Ok(crashlog) } } @@ -92,7 +95,10 @@ mod tests { let berr = Berr::from_crashlog(&crashlog); std::fs::write(berr_path, berr.to_bytes()).unwrap(); - let extracted_crashlog = acpi.extract().unwrap(); + let mut extracted_crashlog = acpi.extract().unwrap(); + assert!(extracted_crashlog.metadata.time.is_some()); + + extracted_crashlog.metadata.time = None; assert_eq!(crashlog.to_bytes(), extracted_crashlog.to_bytes()); } diff --git a/lib/src/source/event_log/win.rs b/lib/src/source/event_log/win.rs index d5409e9..247e2c6 100644 --- a/lib/src/source/event_log/win.rs +++ b/lib/src/source/event_log/win.rs @@ -187,6 +187,8 @@ impl EvtRecord { day: time.wDay as u8, hour: time.wHour as u8, minute: time.wMinute as u8, + second: time.wSecond as u8, + millisecond: time.wMilliseconds, }); } diff --git a/lib/src/source/pmt/sysfs.rs b/lib/src/source/pmt/sysfs.rs index 55e6c3c..9b8d903 100644 --- a/lib/src/source/pmt/sysfs.rs +++ b/lib/src/source/pmt/sysfs.rs @@ -5,6 +5,7 @@ use super::PmtDeviceId; use super::bdf::PciBdf; use crate::CrashLog; use crate::error::Error; +use crate::metadata::Time; use crate::region::Region; use crate::source::{Capabilities, Capability}; use std::collections::BTreeSet; @@ -212,7 +213,10 @@ impl PmtSysFs { for endpoint in self.get_endpoints(dev) { match endpoint.extract() { - Ok(crashlog) => crashlogs.push(crashlog), + Ok(mut crashlog) => { + crashlog.metadata.time = Time::now(); + crashlogs.push(crashlog); + } Err(Error::EmptyRegion) => (), Err(err) => return Err(err), } @@ -340,7 +344,9 @@ impl CrashLog { .flat_map(|crashlog| crashlog.regions) .collect(); - CrashLog::from_regions(regions) + let mut crashlog = CrashLog::from_regions(regions)?; + crashlog.metadata.time = Time::now(); + Ok(crashlog) } }