xref: /cloud-hypervisor/tracer/src/tracer.rs (revision eea9bcea38e0c5649f444c829f3a4f9c22aa486c)
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