xref: /cloud-hypervisor/event_monitor/src/lib.rs (revision 6f8bd27cf7629733582d930519e98d19e90afb16)
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::Write;
11 use std::os::unix::io::AsRawFd;
12 use std::time::{Duration, Instant};
13 
14 static mut MONITOR: Option<(File, Instant)> = None;
15 
16 /// This function must only be called once from the main process before any threads
17 /// are created to avoid race conditions
18 pub fn set_monitor(file: File) -> Result<(), std::io::Error> {
19     // SAFETY: there is only one caller of this function, so MONITOR is written to only once
20     assert!(unsafe { MONITOR.is_none() });
21     let fd = file.as_raw_fd();
22     // SAFETY: FFI call to configure the fd
23     let ret = unsafe {
24         let mut flags = libc::fcntl(fd, libc::F_GETFL);
25         flags |= libc::O_NONBLOCK;
26         libc::fcntl(fd, libc::F_SETFL, flags)
27     };
28     if ret < 0 {
29         return Err(std::io::Error::last_os_error());
30     }
31     // SAFETY: MONITOR is None. Nobody else can hold a reference to it.
32     unsafe {
33         MONITOR = Some((file, Instant::now()));
34     };
35     Ok(())
36 }
37 
38 #[derive(Serialize)]
39 struct Event<'a> {
40     timestamp: Duration,
41     source: &'a str,
42     event: &'a str,
43     properties: Option<&'a HashMap<Cow<'a, str>, Cow<'a, str>>>,
44 }
45 
46 pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>, Cow<str>>>) {
47     // SAFETY: MONITOR is always in a valid state (None or Some).
48     if let Some((file, start)) = unsafe { MONITOR.as_ref() } {
49         let e = Event {
50             timestamp: start.elapsed(),
51             source,
52             event,
53             properties,
54         };
55         serde_json::to_writer_pretty(file, &e).ok();
56 
57         let mut file = file;
58         file.write_all(b"\n\n").ok();
59     }
60 }
61 
62 /*
63     Through the use of Cow<'a, str> it is possible to use String as well as
64     &str as the parameters:
65     e.g.
66     event!("cpu_manager", "create_vcpu", "id", cpu_id.to_string());
67 */
68 #[macro_export]
69 macro_rules! event {
70     ($source:expr, $event:expr) => {
71         $crate::event_log($source, $event, None)
72     };
73     ($source:expr, $event:expr, $($key:expr, $value:expr),*) => {
74         {
75             let mut properties = ::std::collections::HashMap::new();
76             $(
77                 properties.insert($key.into(), $value.into());
78             )+
79             $crate::event_log($source, $event, Some(&properties))
80         }
81      };
82 
83 }
84