diff --git a/Cargo.lock b/Cargo.lock index 1c147503b..ff4ac951d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -341,6 +341,16 @@ dependencies = [ "libc", ] +[[package]] +name = "event_monitor" +version = "0.1.0" +dependencies = [ + "libc", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "failure" version = "0.1.8" diff --git a/Cargo.toml b/Cargo.toml index 3b80d182a..1d899f98f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,7 @@ members = [ "arch_gen", "block_util", "devices", + "event_monitor", "hypervisor", "net_gen", "net_util", diff --git a/event_monitor/Cargo.toml b/event_monitor/Cargo.toml new file mode 100644 index 000000000..830660098 --- /dev/null +++ b/event_monitor/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "event_monitor" +version = "0.1.0" +authors = ["The Cloud Hypervisor Authors"] +edition = "2018" + +[dependencies] +libc = "0.2.86" +serde = {version = ">=1.0.27", features = ["rc"] } +serde_derive = ">=1.0.27" +serde_json = ">=1.0.9" diff --git a/event_monitor/src/lib.rs b/event_monitor/src/lib.rs new file mode 100644 index 000000000..cb58cd293 --- /dev/null +++ b/event_monitor/src/lib.rs @@ -0,0 +1,77 @@ +// Copyright © 2021 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +#[macro_use] +extern crate serde_derive; + +use std::borrow::Cow; +use std::collections::HashMap; +use std::fs::File; +use std::os::unix::io::AsRawFd; +use std::time::{Duration, Instant}; + +static mut MONITOR: Option<(File, Instant)> = None; + +/// This function must only be called once from the main process before any threads +/// are created to avoid race conditions +pub fn set_monitor(file: File) -> Result<(), std::io::Error> { + assert!(unsafe { MONITOR.is_none() }); + let fd = file.as_raw_fd(); + let ret = unsafe { + let mut flags = libc::fcntl(fd, libc::F_GETFL); + flags |= libc::O_NONBLOCK; + libc::fcntl(fd, libc::F_SETFL, flags) + }; + if ret < 0 { + return Err(std::io::Error::last_os_error()); + } + unsafe { + MONITOR = Some((file, Instant::now())); + }; + Ok(()) +} + +#[derive(Serialize)] +struct Event<'a> { + timestamp: Duration, + source: &'a str, + event: &'a str, + properties: Option<&'a HashMap, Cow<'a, str>>>, +} + +pub fn event_log(source: &str, event: &str, properties: Option<&HashMap, Cow>>) { + if let Some((file, start)) = unsafe { MONITOR.as_ref() } { + let e = Event { + timestamp: start.elapsed(), + source, + event, + properties, + }; + serde_json::to_writer_pretty(file, &e).ok(); + } +} + +/* + Through the use of Cow<'a, str> it is possible to use String as well as + &str as the parameters: + e.g. + event!("cpu_manager", "create_vcpu", "id", cpu_id.to_string()); +*/ +#[macro_export] +macro_rules! event { + ($source:expr, $event:expr) => { + $crate::event_log($source, $event, None) + }; + ($source:expr, $event:expr, $($key:expr, $value:expr),*) => { + { + let mut properties = ::std::collections::HashMap::new(); + $( + properties.insert($key.into(), $value.into()); + )+ + $crate::event_log($source, $event, Some(&properties)) + } + }; + +}