1ddbef745SRob Bradford // Copyright © 2021 Intel Corporation
2ddbef745SRob Bradford //
3ddbef745SRob Bradford // SPDX-License-Identifier: Apache-2.0
4ddbef745SRob Bradford //
5ddbef745SRob Bradford
6ddbef745SRob Bradford use std::borrow::Cow;
7ddbef745SRob Bradford use std::collections::HashMap;
8ddbef745SRob Bradford use std::fs::File;
902e1c544SOmer Faruk Bayram use std::io;
10ddbef745SRob Bradford use std::os::unix::io::AsRawFd;
11*4182ef91SPhilipp Schuster use std::sync::{Arc, OnceLock};
12ddbef745SRob Bradford use std::time::{Duration, Instant};
13ddbef745SRob Bradford
1488a9f799SRob Bradford use serde::Serialize;
1588a9f799SRob Bradford
16*4182ef91SPhilipp Schuster static MONITOR: OnceLock<MonitorHandle> = OnceLock::new();
17ddbef745SRob Bradford
18ddbef745SRob Bradford #[derive(Serialize)]
19ddbef745SRob Bradford struct Event<'a> {
20ddbef745SRob Bradford timestamp: Duration,
21ddbef745SRob Bradford source: &'a str,
22ddbef745SRob Bradford event: &'a str,
23ddbef745SRob Bradford properties: Option<&'a HashMap<Cow<'a, str>, Cow<'a, str>>>,
24ddbef745SRob Bradford }
25ddbef745SRob Bradford
2602e1c544SOmer Faruk Bayram pub struct Monitor {
2702e1c544SOmer Faruk Bayram pub rx: flume::Receiver<String>,
282ed96cd3SOmer Faruk Bayram pub file: Option<File>,
29e02efe9bSOmer Faruk Bayram pub broadcast: Vec<flume::Sender<Arc<String>>>,
30e02efe9bSOmer Faruk Bayram }
31e02efe9bSOmer Faruk Bayram
32e02efe9bSOmer Faruk Bayram impl Monitor {
new(rx: flume::Receiver<String>, file: Option<File>) -> Self332ed96cd3SOmer Faruk Bayram pub fn new(rx: flume::Receiver<String>, file: Option<File>) -> Self {
34e02efe9bSOmer Faruk Bayram Self {
35e02efe9bSOmer Faruk Bayram rx,
36e02efe9bSOmer Faruk Bayram file,
37e02efe9bSOmer Faruk Bayram broadcast: vec![],
38e02efe9bSOmer Faruk Bayram }
39e02efe9bSOmer Faruk Bayram }
40e02efe9bSOmer Faruk Bayram
subscribe(&mut self) -> flume::Receiver<Arc<String>>41e02efe9bSOmer Faruk Bayram pub fn subscribe(&mut self) -> flume::Receiver<Arc<String>> {
42e02efe9bSOmer Faruk Bayram let (tx, rx) = flume::unbounded();
43e02efe9bSOmer Faruk Bayram self.broadcast.push(tx);
44e02efe9bSOmer Faruk Bayram rx
45e02efe9bSOmer Faruk Bayram }
4602e1c544SOmer Faruk Bayram }
4702e1c544SOmer Faruk Bayram
4802e1c544SOmer Faruk Bayram struct MonitorHandle {
4902e1c544SOmer Faruk Bayram tx: flume::Sender<String>,
5002e1c544SOmer Faruk Bayram start: Instant,
5102e1c544SOmer Faruk Bayram }
5202e1c544SOmer Faruk Bayram
set_file_nonblocking(file: &File) -> io::Result<()>5302e1c544SOmer Faruk Bayram fn set_file_nonblocking(file: &File) -> io::Result<()> {
5402e1c544SOmer Faruk Bayram let fd = file.as_raw_fd();
5502e1c544SOmer Faruk Bayram
5602e1c544SOmer Faruk Bayram // SAFETY: FFI call to configure the fd
5702e1c544SOmer Faruk Bayram let ret = unsafe {
5802e1c544SOmer Faruk Bayram let mut flags = libc::fcntl(fd, libc::F_GETFL);
5902e1c544SOmer Faruk Bayram flags |= libc::O_NONBLOCK;
6002e1c544SOmer Faruk Bayram libc::fcntl(fd, libc::F_SETFL, flags)
6102e1c544SOmer Faruk Bayram };
6202e1c544SOmer Faruk Bayram
6302e1c544SOmer Faruk Bayram if ret < 0 {
6402e1c544SOmer Faruk Bayram Err(io::Error::last_os_error())
6502e1c544SOmer Faruk Bayram } else {
6602e1c544SOmer Faruk Bayram Ok(())
6702e1c544SOmer Faruk Bayram }
6802e1c544SOmer Faruk Bayram }
6902e1c544SOmer Faruk Bayram
7002e1c544SOmer Faruk Bayram /// This function must only be called once from the main thread before any threads
7102e1c544SOmer Faruk Bayram /// are created to avoid race conditions.
set_monitor(file: Option<File>) -> io::Result<Monitor>722ed96cd3SOmer Faruk Bayram pub fn set_monitor(file: Option<File>) -> io::Result<Monitor> {
7388f3537bSChristian Blichmann // There is only one caller of this function, so MONITOR is written to only once
7488f3537bSChristian Blichmann assert!(MONITOR.get().is_none());
7502e1c544SOmer Faruk Bayram
762ed96cd3SOmer Faruk Bayram if let Some(ref file) = file {
772ed96cd3SOmer Faruk Bayram set_file_nonblocking(file)?;
782ed96cd3SOmer Faruk Bayram }
792ed96cd3SOmer Faruk Bayram
8002e1c544SOmer Faruk Bayram let (tx, rx) = flume::unbounded();
81e02efe9bSOmer Faruk Bayram let monitor = Monitor::new(rx, file);
8202e1c544SOmer Faruk Bayram
8388f3537bSChristian Blichmann MONITOR.get_or_init(|| MonitorHandle {
8402e1c544SOmer Faruk Bayram tx,
8502e1c544SOmer Faruk Bayram start: Instant::now(),
8602e1c544SOmer Faruk Bayram });
8702e1c544SOmer Faruk Bayram
8802e1c544SOmer Faruk Bayram Ok(monitor)
8902e1c544SOmer Faruk Bayram }
9002e1c544SOmer Faruk Bayram
event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>, Cow<str>>>)91ddbef745SRob Bradford pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>, Cow<str>>>) {
9288f3537bSChristian Blichmann // `MONITOR` is always in a valid state (None or Some), because it is set
9388f3537bSChristian Blichmann // only once before any threads are spawned, and it's not mutated
9402e1c544SOmer Faruk Bayram // afterwards. This function only creates immutable references to `MONITOR`.
9502e1c544SOmer Faruk Bayram // Because `MONITOR.tx` is `Sync`, it's safe to share `MONITOR` across
9602e1c544SOmer Faruk Bayram // threads, making this function thread-safe.
9788f3537bSChristian Blichmann if let Some(monitor_handle) = MONITOR.get().as_ref() {
9802e1c544SOmer Faruk Bayram let event = Event {
9902e1c544SOmer Faruk Bayram timestamp: monitor_handle.start.elapsed(),
100ddbef745SRob Bradford source,
101ddbef745SRob Bradford event,
102ddbef745SRob Bradford properties,
103ddbef745SRob Bradford };
10432353fa3SBo Chen
10502e1c544SOmer Faruk Bayram if let Ok(event) = serde_json::to_string_pretty(&event) {
10602e1c544SOmer Faruk Bayram monitor_handle.tx.send(event).ok();
10702e1c544SOmer Faruk Bayram }
108ddbef745SRob Bradford }
109ddbef745SRob Bradford }
110ddbef745SRob Bradford
111ddbef745SRob Bradford /*
112ddbef745SRob Bradford Through the use of Cow<'a, str> it is possible to use String as well as
113ddbef745SRob Bradford &str as the parameters:
114ddbef745SRob Bradford e.g.
115ddbef745SRob Bradford event!("cpu_manager", "create_vcpu", "id", cpu_id.to_string());
116ddbef745SRob Bradford */
117ddbef745SRob Bradford #[macro_export]
118ddbef745SRob Bradford macro_rules! event {
119ddbef745SRob Bradford ($source:expr, $event:expr) => {
120ddbef745SRob Bradford $crate::event_log($source, $event, None)
121ddbef745SRob Bradford };
122ddbef745SRob Bradford ($source:expr, $event:expr, $($key:expr, $value:expr),*) => {
123ddbef745SRob Bradford {
124ddbef745SRob Bradford let mut properties = ::std::collections::HashMap::new();
125ddbef745SRob Bradford $(
126ddbef745SRob Bradford properties.insert($key.into(), $value.into());
127ddbef745SRob Bradford )+
128ddbef745SRob Bradford $crate::event_log($source, $event, Some(&properties))
129ddbef745SRob Bradford }
130ddbef745SRob Bradford };
131ddbef745SRob Bradford }
132