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::sync::Arc; 13 use std::time::{Duration, Instant}; 14 15 static mut MONITOR: Option<MonitorHandle> = None; 16 17 #[derive(Serialize)] 18 struct Event<'a> { 19 timestamp: Duration, 20 source: &'a str, 21 event: &'a str, 22 properties: Option<&'a HashMap<Cow<'a, str>, Cow<'a, str>>>, 23 } 24 25 pub struct Monitor { 26 pub rx: flume::Receiver<String>, 27 pub file: Option<File>, 28 pub broadcast: Vec<flume::Sender<Arc<String>>>, 29 } 30 31 impl Monitor { 32 pub fn new(rx: flume::Receiver<String>, file: Option<File>) -> Self { 33 Self { 34 rx, 35 file, 36 broadcast: vec![], 37 } 38 } 39 40 pub fn subscribe(&mut self) -> flume::Receiver<Arc<String>> { 41 let (tx, rx) = flume::unbounded(); 42 self.broadcast.push(tx); 43 rx 44 } 45 } 46 47 struct MonitorHandle { 48 tx: flume::Sender<String>, 49 start: Instant, 50 } 51 52 fn set_file_nonblocking(file: &File) -> io::Result<()> { 53 let fd = file.as_raw_fd(); 54 55 // SAFETY: FFI call to configure the fd 56 let ret = unsafe { 57 let mut flags = libc::fcntl(fd, libc::F_GETFL); 58 flags |= libc::O_NONBLOCK; 59 libc::fcntl(fd, libc::F_SETFL, flags) 60 }; 61 62 if ret < 0 { 63 Err(io::Error::last_os_error()) 64 } else { 65 Ok(()) 66 } 67 } 68 69 /// This function must only be called once from the main thread before any threads 70 /// are created to avoid race conditions. 71 pub fn set_monitor(file: Option<File>) -> io::Result<Monitor> { 72 // SAFETY: there is only one caller of this function, so MONITOR is written to only once 73 assert!(unsafe { MONITOR.is_none() }); 74 75 if let Some(ref file) = file { 76 set_file_nonblocking(file)?; 77 } 78 79 let (tx, rx) = flume::unbounded(); 80 let monitor = Monitor::new(rx, file); 81 82 // SAFETY: MONITOR is None. Nobody else can hold a reference to it. 83 unsafe { 84 MONITOR = Some(MonitorHandle { 85 tx, 86 start: Instant::now(), 87 }); 88 }; 89 90 Ok(monitor) 91 } 92 93 pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>, Cow<str>>>) { 94 // SAFETY: `MONITOR` is always in a valid state (None or Some), because it 95 // is set only once before any threads are spawned, and it's not mutated 96 // afterwards. This function only creates immutable references to `MONITOR`. 97 // Because `MONITOR.tx` is `Sync`, it's safe to share `MONITOR` across 98 // threads, making this function thread-safe. 99 if let Some(monitor_handle) = unsafe { MONITOR.as_ref() } { 100 let event = Event { 101 timestamp: monitor_handle.start.elapsed(), 102 source, 103 event, 104 properties, 105 }; 106 107 if let Ok(event) = serde_json::to_string_pretty(&event) { 108 monitor_handle.tx.send(event).ok(); 109 } 110 } 111 } 112 113 /* 114 Through the use of Cow<'a, str> it is possible to use String as well as 115 &str as the parameters: 116 e.g. 117 event!("cpu_manager", "create_vcpu", "id", cpu_id.to_string()); 118 */ 119 #[macro_export] 120 macro_rules! event { 121 ($source:expr, $event:expr) => { 122 $crate::event_log($source, $event, None) 123 }; 124 ($source:expr, $event:expr, $($key:expr, $value:expr),*) => { 125 { 126 let mut properties = ::std::collections::HashMap::new(); 127 $( 128 properties.insert($key.into(), $value.into()); 129 )+ 130 $crate::event_log($source, $event, Some(&properties)) 131 } 132 }; 133 } 134