xref: /cloud-hypervisor/event_monitor/src/lib.rs (revision 4182ef91e03131731a10b1af2b36c36af476a521)
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