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