1extern crate lazy_static;
43
44pub mod alloc;
46pub mod global;
48
49use std::io::Write;
50use std::sync::atomic::AtomicI64;
51use std::sync::atomic::Ordering::Relaxed;
52use std::sync::Arc;
53use std::time::Duration;
54use std::{io, sync, time};
55
56use thiserror::Error;
57
58#[derive(Error, Debug)]
60pub enum ReadingsError {
61 #[error("Metrics can only be added before first event")]
62 LateRegistertingMetricsAttempt,
63 #[error("io Error accessing /proc/self/stat")]
64 ProcStat(io::Error),
65 #[error("Io error writing readings")]
66 Io(#[from] io::Error),
67 #[error("Poisoned probe")]
68 PoisonedProbe,
69}
70
71pub type ReadingsResult<T> = Result<T, ReadingsError>;
73
74#[cfg(any(target_os = "linux", target_os = "android"))]
75mod linux;
76
77#[cfg(any(target_os = "macos", target_os = "ios"))]
78mod macos;
79
80#[cfg(target_os = "windows")]
81mod windows;
82
83#[allow(unreachable_code)]
87pub fn get_os_readings() -> ReadingsResult<OsReadings> {
88 #[cfg(any(target_os = "linux", target_os = "android"))]
89 return linux::get_os_readings();
90 #[cfg(any(target_os = "macos", target_os = "ios"))]
91 return macos::get_os_readings();
92 #[cfg(target_os = "windows")]
93 return windows::get_os_readings();
94 return unsafe { Ok(std::mem::zeroed()) };
95}
96
97#[derive(Debug)]
98pub struct OsReadings {
99 pub virtual_size: u64,
101 pub resident_size: u64,
103 pub resident_size_max: u64,
105 pub user_time: Duration,
107 pub system_time: Duration,
109 pub minor_fault: u64,
111 pub major_fault: u64,
113}
114
115#[derive(Clone)]
117pub struct Probe(sync::Arc<sync::Mutex<ProbeData>>);
118
119struct ProbeData {
120 cores: usize,
121 origin: Option<std::time::Instant>,
122 writer: io::BufWriter<Box<dyn io::Write + Send>>,
123 metrics_i64: Vec<(String, Arc<AtomicI64>)>,
124}
125
126impl ProbeData {
127 fn write_line(&mut self, now: time::Instant, reason: &str) -> ReadingsResult<()> {
128 if self.origin.is_none() {
129 write!(self.writer, " time cor vsz rsz rszmax")?;
130 write!(self.writer, " utime stime minf majf")?;
131 write!(self.writer, " alloc free")?;
132 for m in &self.metrics_i64 {
133 write!(self.writer, " {:>10}", m.0)?;
134 }
135 writeln!(self.writer, " event")?;
136 self.origin = Some(now)
137 }
138 let usage = get_os_readings()?;
139 write!(
140 self.writer,
141 "{:7.3} {:3}",
142 (now - self.origin.unwrap()).as_secs_f32(),
143 self.cores
144 )?;
145 write!(
146 self.writer,
147 " {:10} {:10} {:10}",
148 usage.virtual_size, usage.resident_size, usage.resident_size_max
149 )?;
150 write!(
151 self.writer,
152 " {:8.6} {:8.6} {:10} {:10}",
153 usage.user_time.as_secs_f64(),
154 usage.system_time.as_secs_f64(),
155 usage.minor_fault,
156 usage.major_fault
157 )?;
158 write!(
159 self.writer,
160 " {:10} {:10}",
161 alloc::ALLOCATED.load(Relaxed),
162 alloc::FREEED.load(Relaxed)
163 )?;
164 for m in &self.metrics_i64 {
165 write!(self.writer, " {:10}", m.1.load(Relaxed),)?;
166 }
167 writeln!(self.writer, " {}", reason)?;
168 self.writer.flush()?;
169 Ok(())
170 }
171}
172
173impl Probe {
174 pub fn new<W: Write + Send + 'static>(write: W) -> ReadingsResult<Probe> {
177 let mut writer = io::BufWriter::new(Box::new(write) as _);
178 writeln!(writer, "#ReadingsV1")?;
179 let data = ProbeData {
180 cores: num_cpus::get(),
181 origin: None,
182 writer,
183 metrics_i64: vec![],
184 };
185 Ok(Probe(sync::Arc::new(sync::Mutex::new(data))))
186 }
187
188 pub fn register_i64<S: AsRef<str>>(&mut self, name: S) -> ReadingsResult<Arc<AtomicI64>> {
197 let mut m = self.0.lock().map_err(|_| ReadingsError::PoisonedProbe)?;
198 if m.origin.is_some() {
199 return Err(ReadingsError::LateRegistertingMetricsAttempt);
200 }
201 let it = Arc::new(AtomicI64::new(0));
202 m.metrics_i64
203 .push((name.as_ref().replace(" ", "_"), it.clone()));
204 Ok(it)
205 }
206
207 pub fn spawn_heartbeat(&mut self, interval: time::Duration) -> ReadingsResult<()> {
209 let probe = self.clone();
210 probe.log_event("spawned_heartbeat")?;
211 let origin = probe
212 .0
213 .lock()
214 .map_err(|_| ReadingsError::PoisonedProbe)?
215 .origin
216 .unwrap();
217 std::thread::spawn(move || {
218 for step in 1.. {
219 let wanted = origin + (step * interval);
220 let now = std::time::Instant::now();
221 if wanted > now {
222 ::std::thread::sleep(wanted - now);
223 }
224 if let Err(e) = probe.log_event("") {
225 eprintln!("{:?}", e);
226 }
227 }
228 });
229 Ok(())
230 }
231
232 pub fn log_event(&self, event: &str) -> ReadingsResult<()> {
234 self.write_line(std::time::Instant::now(), &event.replace(" ", "_"))
235 }
236
237 pub fn get_i64<S: AsRef<str>>(&self, name: S) -> Option<Arc<AtomicI64>> {
247 let name = name.as_ref().replace(" ", "_");
248 self.0.lock().ok().and_then(|l| {
249 l.metrics_i64
250 .iter()
251 .find(|m| (m.0 == name))
252 .map(|m| m.1.clone())
253 })
254 }
255
256 fn write_line(&self, now: time::Instant, reason: &str) -> ReadingsResult<()> {
257 self.0
258 .lock()
259 .map_err(|_| ReadingsError::PoisonedProbe)?
260 .write_line(now, reason)
261 }
262}