workflow_perf_monitor/cpu/
android_linux.rs

1use 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    /// un-normalized
65    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}