workflow_perf_monitor/cpu/windows/
mod.rs

1use super::processor_numbers;
2use super::windows::process_times::ProcessTimes;
3use super::windows::system_times::SystemTimes;
4use super::windows::thread_times::ThreadTimes;
5use std::io::Result;
6use std::time::Duration;
7use windows_sys::Win32::Foundation::FILETIME;
8use windows_sys::Win32::System::Threading::GetCurrentThreadId;
9
10pub mod process_times;
11pub mod system_times;
12pub mod thread_times;
13
14#[derive(Clone, Copy)]
15pub struct ThreadId(u32);
16
17impl ThreadId {
18    #[inline]
19    pub fn current() -> Self {
20        ThreadId(unsafe { GetCurrentThreadId() })
21    }
22}
23
24/// convert to u64, unit 100 ns
25fn filetime_to_ns100(ft: &FILETIME) -> u64 {
26    ((ft.dwHighDateTime as u64) << 32) + ft.dwLowDateTime as u64
27}
28
29pub struct ThreadStat {
30    tid: ThreadId,
31    last_work_time: u64,
32    last_total_time: u64,
33}
34
35impl ThreadStat {
36    fn get_times(thread_id: ThreadId) -> Result<(u64, u64)> {
37        let system_times = SystemTimes::capture()?;
38        let thread_times = ThreadTimes::capture_with_thread_id(thread_id)?;
39
40        let work_time =
41            filetime_to_ns100(&thread_times.kernel) + filetime_to_ns100(&thread_times.user);
42        let total_time =
43            filetime_to_ns100(&system_times.kernel) + filetime_to_ns100(&system_times.user);
44        Ok((work_time, total_time))
45    }
46
47    pub fn cur() -> Result<Self> {
48        let tid = ThreadId::current();
49        let (work_time, total_time) = Self::get_times(tid)?;
50        Ok(ThreadStat {
51            tid,
52            last_work_time: work_time,
53            last_total_time: total_time,
54        })
55    }
56
57    pub fn build(tid: ThreadId) -> Result<Self> {
58        let (work_time, total_time) = Self::get_times(tid)?;
59        Ok(ThreadStat {
60            tid,
61            last_work_time: work_time,
62            last_total_time: total_time,
63        })
64    }
65
66    pub fn cpu(&mut self) -> Result<f64> {
67        let (work_time, total_time) = Self::get_times(self.tid)?;
68
69        let dt_total_time = total_time - self.last_total_time;
70        if dt_total_time == 0 {
71            return Ok(0.0);
72        }
73        let dt_work_time = work_time - self.last_work_time;
74
75        self.last_work_time = work_time;
76        self.last_total_time = total_time;
77
78        Ok(dt_work_time as f64 / dt_total_time as f64 * processor_numbers()? as f64)
79    }
80
81    pub fn cpu_time(&mut self) -> Result<Duration> {
82        let (work_time, total_time) = Self::get_times(self.tid)?;
83
84        let cpu_time = work_time - self.last_work_time;
85
86        self.last_work_time = work_time;
87        self.last_total_time = total_time;
88
89        Ok(Duration::from_nanos(cpu_time))
90    }
91}
92
93#[inline]
94pub fn cpu_time() -> Result<Duration> {
95    let process_times = ProcessTimes::capture_current()?;
96
97    let kt = filetime_to_ns100(&process_times.kernel);
98    let ut = filetime_to_ns100(&process_times.user);
99
100    // convert ns
101    //
102    // Note: make it ns unit may overflow in some cases.
103    // For example, a machine with 128 cores runs for one year.
104    let cpu = (kt + ut).saturating_mul(100);
105
106    // make it un-normalized
107    let cpu = cpu * processor_numbers()? as u64;
108
109    Ok(Duration::from_nanos(cpu))
110}