workflow_perf_monitor/cpu/
android_linux.rs1use libc::{pthread_t, timespec};
2use std::{
3 convert::TryInto,
4 io::Error,
5 io::Result,
6 mem::MaybeUninit,
7 time::{Duration, Instant},
8};
9
10#[derive(Clone, Copy)]
11pub struct ThreadId(pthread_t);
12
13impl ThreadId {
14 #[inline]
15 pub fn current() -> Self {
16 ThreadId(unsafe { libc::pthread_self() })
17 }
18}
19
20fn timespec_to_duration(timespec { tv_sec, tv_nsec }: timespec) -> Duration {
21 let sec: u64 = tv_sec.try_into().unwrap_or_default();
22 let nsec: u64 = tv_nsec.try_into().unwrap_or_default();
23 let (sec, nanos) = (
24 sec.saturating_add(nsec / 1_000_000_000),
25 (nsec % 1_000_000_000) as u32,
26 );
27 Duration::new(sec, nanos)
28}
29
30fn get_thread_cputime(ThreadId(thread): ThreadId) -> Result<timespec> {
31 let mut clk_id = 0;
32 let ret = unsafe { libc::pthread_getcpuclockid(thread, &mut clk_id) };
33 if ret != 0 {
34 return Err(Error::from_raw_os_error(ret));
35 }
36
37 let mut timespec = MaybeUninit::<timespec>::uninit();
38 let ret = unsafe { libc::clock_gettime(clk_id, timespec.as_mut_ptr()) };
39 if ret != 0 {
40 return Err(Error::last_os_error());
41 }
42 Ok(unsafe { timespec.assume_init() })
43}
44
45pub struct ThreadStat {
46 tid: ThreadId,
47 last_stat: (timespec, Instant),
48}
49
50impl ThreadStat {
51 pub fn cur() -> Result<Self> {
52 Self::build(ThreadId::current())
53 }
54
55 pub fn build(tid: ThreadId) -> Result<Self> {
56 let cputime = get_thread_cputime(tid)?;
57 let total_time = Instant::now();
58 Ok(ThreadStat {
59 tid,
60 last_stat: (cputime, total_time),
61 })
62 }
63
64 pub fn cpu(&mut self) -> Result<f64> {
66 let cputime = get_thread_cputime(self.tid)?;
67 let total_time = Instant::now();
68 let (old_cputime, old_total_time) =
69 std::mem::replace(&mut self.last_stat, (cputime, total_time));
70 let cputime = cputime.tv_sec as f64 + cputime.tv_nsec as f64 / 1_000_000_000f64;
71 let old_cputime = old_cputime.tv_sec as f64 + old_cputime.tv_nsec as f64 / 1_000_000_000f64;
72 let dt_cputime = cputime - old_cputime;
73 let dt_total_time = total_time
74 .saturating_duration_since(old_total_time)
75 .as_secs_f64();
76 Ok(dt_cputime / dt_total_time)
77 }
78
79 pub fn cpu_time(&mut self) -> Result<Duration> {
80 let cputime = get_thread_cputime(self.tid)?;
81 let total_time = Instant::now();
82 let (old_cputime, _old_total_time) =
83 std::mem::replace(&mut self.last_stat, (cputime, total_time));
84 Ok(timespec_to_duration(cputime).saturating_sub(timespec_to_duration(old_cputime)))
85 }
86}
87
88pub fn cpu_time() -> Result<Duration> {
89 let mut timespec = MaybeUninit::<timespec>::uninit();
90 let ret = unsafe { libc::clock_gettime(libc::CLOCK_PROCESS_CPUTIME_ID, timespec.as_mut_ptr()) };
91 if ret != 0 {
92 return Err(Error::last_os_error());
93 }
94 Ok(timespec_to_duration(unsafe { timespec.assume_init() }))
95}