1 // Copyright © 2022 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 use once_cell::unsync::OnceCell; 7 use serde::Serialize; 8 use std::collections::HashMap; 9 use std::fs::File; 10 use std::io::Write; 11 use std::sync::atomic::{AtomicU64, Ordering}; 12 use std::sync::{Arc, Mutex}; 13 use std::time::{Duration, Instant}; 14 15 #[derive(Debug)] 16 struct Tracer { 17 events: Arc<Mutex<HashMap<String, Vec<TraceEvent>>>>, 18 thread_depths: HashMap<String, Arc<AtomicU64>>, 19 start: Instant, 20 } 21 22 impl Tracer { 23 fn new() -> Self { 24 Self { 25 events: Arc::new(Mutex::new(HashMap::default())), 26 start: Instant::now(), 27 thread_depths: HashMap::default(), 28 } 29 } 30 31 fn end(&self) { 32 let end = Instant::now(); 33 let path = format!("cloud-hypervisor-{}.trace", unsafe { libc::getpid() }); 34 let mut file = File::create(&path).unwrap(); 35 36 #[derive(Serialize)] 37 struct TraceReport { 38 duration: Duration, 39 events: Arc<Mutex<HashMap<String, Vec<TraceEvent>>>>, 40 } 41 42 let trace_report = TraceReport { 43 duration: end.duration_since(self.start), 44 events: self.events.clone(), 45 }; 46 47 serde_json::to_writer_pretty(&file, &trace_report).unwrap(); 48 49 file.flush().unwrap(); 50 51 warn!("Trace output: {}", path); 52 } 53 54 fn add_event(&mut self, event: TraceEvent) { 55 let thread_name = std::thread::current().name().unwrap_or("").to_string(); 56 let mut events = self.events.lock().unwrap(); 57 if let Some(thread_events) = events.get_mut(&thread_name) { 58 thread_events.push(event); 59 } else { 60 events.insert(thread_name.to_string(), vec![event]); 61 } 62 } 63 64 fn increase_thread_depth(&mut self) { 65 let thread_name = std::thread::current().name().unwrap_or("").to_string(); 66 if let Some(depth) = self.thread_depths.get_mut(&thread_name) { 67 depth.fetch_add(1, Ordering::SeqCst); 68 } else { 69 self.thread_depths 70 .insert(thread_name, Arc::new(AtomicU64::new(0))); 71 } 72 } 73 74 fn decrease_thread_depth(&mut self) { 75 let thread_name = std::thread::current().name().unwrap_or("").to_string(); 76 if let Some(depth) = self.thread_depths.get_mut(&thread_name) { 77 depth.fetch_sub(1, Ordering::SeqCst); 78 } else { 79 panic!("Unmatched decreas for thread: {}", thread_name); 80 } 81 } 82 83 fn thread_depth(&self) -> u64 { 84 let thread_name = std::thread::current().name().unwrap_or("").to_string(); 85 self.thread_depths 86 .get(&thread_name) 87 .map(|v| v.load(Ordering::SeqCst)) 88 .unwrap_or_default() 89 } 90 } 91 92 static mut TRACER: OnceCell<Tracer> = OnceCell::new(); 93 94 #[derive(Clone, Debug, Serialize)] 95 struct TraceEvent { 96 timestamp: Duration, 97 event: &'static str, 98 end_timestamp: Option<Duration>, 99 depth: u64, 100 } 101 102 pub fn trace_point_log(event: &'static str) { 103 let trace_event = TraceEvent { 104 timestamp: Instant::now().duration_since(unsafe { TRACER.get().unwrap().start }), 105 event, 106 end_timestamp: None, 107 depth: unsafe { TRACER.get().unwrap().thread_depth() }, 108 }; 109 unsafe { 110 TRACER.get_mut().unwrap().add_event(trace_event); 111 } 112 } 113 114 pub struct TraceBlock { 115 start: Instant, 116 event: &'static str, 117 } 118 119 impl TraceBlock { 120 pub fn new(event: &'static str) -> Self { 121 unsafe { 122 TRACER.get_mut().unwrap().increase_thread_depth(); 123 } 124 Self { 125 start: Instant::now(), 126 event, 127 } 128 } 129 } 130 131 impl Drop for TraceBlock { 132 fn drop(&mut self) { 133 let trace_event = TraceEvent { 134 timestamp: self 135 .start 136 .duration_since(unsafe { TRACER.get().unwrap().start }), 137 event: self.event, 138 end_timestamp: Some( 139 Instant::now().duration_since(unsafe { TRACER.get().unwrap().start }), 140 ), 141 depth: unsafe { TRACER.get().unwrap().thread_depth() }, 142 }; 143 unsafe { 144 TRACER.get_mut().unwrap().add_event(trace_event); 145 TRACER.get_mut().unwrap().decrease_thread_depth(); 146 } 147 } 148 } 149 150 #[macro_export] 151 macro_rules! trace_point { 152 ($event:expr) => { 153 $crate::trace_point_log($event) 154 }; 155 } 156 157 #[macro_export] 158 macro_rules! trace_scoped { 159 ($event:expr) => { 160 let _trace_scoped = $crate::TraceBlock::new($event); 161 }; 162 } 163 164 pub fn end() { 165 unsafe { TRACER.get().unwrap().end() } 166 } 167 168 pub fn start() { 169 unsafe { TRACER.set(Tracer::new()).unwrap() } 170 } 171