xref: /cloud-hypervisor/event_monitor/src/lib.rs (revision 2571e59438597f53aa4993cd70d6462fe1364ba7)
1 // Copyright © 2021 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 //
5 
6 use serde::Serialize;
7 use std::borrow::Cow;
8 use std::collections::HashMap;
9 use std::fs::File;
10 use std::io;
11 use std::os::unix::io::AsRawFd;
12 use std::time::{Duration, Instant};
13 
14 static mut MONITOR: Option<MonitorHandle> = None;
15 
16 #[derive(Serialize)]
17 struct Event<'a> {
18     timestamp: Duration,
19     source: &'a str,
20     event: &'a str,
21     properties: Option<&'a HashMap<Cow<'a, str>, Cow<'a, str>>>,
22 }
23 
24 pub struct Monitor {
25     pub rx: flume::Receiver<String>,
26     pub file: File,
27 }
28 
29 struct MonitorHandle {
30     tx: flume::Sender<String>,
31     start: Instant,
32 }
33 
34 fn set_file_nonblocking(file: &File) -> io::Result<()> {
35     let fd = file.as_raw_fd();
36 
37     // SAFETY: FFI call to configure the fd
38     let ret = unsafe {
39         let mut flags = libc::fcntl(fd, libc::F_GETFL);
40         flags |= libc::O_NONBLOCK;
41         libc::fcntl(fd, libc::F_SETFL, flags)
42     };
43 
44     if ret < 0 {
45         Err(io::Error::last_os_error())
46     } else {
47         Ok(())
48     }
49 }
50 
51 /// This function must only be called once from the main thread before any threads
52 /// are created to avoid race conditions.
53 pub fn set_monitor(file: File) -> io::Result<Monitor> {
54     // SAFETY: there is only one caller of this function, so MONITOR is written to only once
55     assert!(unsafe { MONITOR.is_none() });
56 
57     set_file_nonblocking(&file)?;
58     let (tx, rx) = flume::unbounded();
59     let monitor = Monitor { rx, file };
60 
61     // SAFETY: MONITOR is None. Nobody else can hold a reference to it.
62     unsafe {
63         MONITOR = Some(MonitorHandle {
64             tx,
65             start: Instant::now(),
66         });
67     };
68 
69     Ok(monitor)
70 }
71 
72 pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>, Cow<str>>>) {
73     // SAFETY: `MONITOR` is always in a valid state (None or Some), because it
74     // is set only once before any threads are spawned, and it's not mutated
75     // afterwards. This function only creates immutable references to `MONITOR`.
76     // Because `MONITOR.tx` is `Sync`, it's safe to share `MONITOR` across
77     // threads, making this function thread-safe.
78     if let Some(monitor_handle) = unsafe { MONITOR.as_ref() } {
79         let event = Event {
80             timestamp: monitor_handle.start.elapsed(),
81             source,
82             event,
83             properties,
84         };
85 
86         if let Ok(event) = serde_json::to_string_pretty(&event) {
87             monitor_handle.tx.send(event).ok();
88         }
89     }
90 }
91 
92 /*
93     Through the use of Cow<'a, str> it is possible to use String as well as
94     &str as the parameters:
95     e.g.
96     event!("cpu_manager", "create_vcpu", "id", cpu_id.to_string());
97 */
98 #[macro_export]
99 macro_rules! event {
100     ($source:expr, $event:expr) => {
101         $crate::event_log($source, $event, None)
102     };
103     ($source:expr, $event:expr, $($key:expr, $value:expr),*) => {
104         {
105             let mut properties = ::std::collections::HashMap::new();
106             $(
107                 properties.insert($key.into(), $value.into());
108             )+
109             $crate::event_log($source, $event, Some(&properties))
110         }
111      };
112 }
113