groundwork/
stat.rs

1use std::sync::Arc;
2
3use poem::error::InternalServerError;
4use poem::web::Data;
5use poem::{Error, Result, handler, http::StatusCode};
6use serde::Serialize;
7
8pub struct StatsData {
9    name: String,
10    usage_time_to_us: f64,
11}
12
13impl StatsData {
14    pub fn new(name: &str) -> Self {
15        Self {
16            name: name.to_string(),
17            usage_time_to_us: usage_time_to_us(),
18        }
19    }
20}
21
22#[cfg(target_os = "macos")]
23fn usage_time_to_us() -> f64 {
24    unsafe {
25        let mut i = mach2::mach_time::mach_timebase_info { numer: 0, denom: 0 };
26        if mach2::mach_time::mach_timebase_info(&mut i) != 0 {
27            panic!();
28        }
29        i.numer as f64 / i.denom as f64 / 1e3
30    }
31}
32
33#[cfg(target_os = "linux")]
34fn usage_time_to_us() -> f64 {
35    let ticks_per_second = procfs::ticks_per_second();
36    1e6 / ticks_per_second as f64
37}
38
39#[handler]
40pub fn stats(data: Data<&Arc<StatsData>>) -> Result<String> {
41    let allocator_metrics = alloc_metrics::global_metrics();
42    let mem_allocated_bytes = allocator_metrics.allocated_bytes as u64;
43    let allocations = allocator_metrics.allocations as u64;
44    let name = data.name.clone();
45    let hostname = hostname::get()
46        .map_err(InternalServerError)?
47        .into_string()
48        .map_err(|e| Error::from_string(e.to_string_lossy(), StatusCode::INTERNAL_SERVER_ERROR))?;
49
50    let stats = {
51        #[cfg(target_os = "linux")]
52        {
53            use procfs::WithCurrentSystemInfo;
54            let map_err = |e: procfs::ProcError| {
55                Error::from_string(e.to_string(), StatusCode::INTERNAL_SERVER_ERROR)
56            };
57            let process = procfs::process::Process::myself().map_err(map_err)?;
58            let status = process.status().map_err(map_err)?;
59            let stat = process.stat().map_err(map_err)?;
60            Stats {
61                name,
62                hostname,
63                mem_allocated_bytes,
64                allocations,
65                mem_rss: status.vmrss.unwrap() * 1024,
66                mem_rss_peak: status.vmhwm.unwrap() * 1024,
67                mem_virtual: status.vmsize.unwrap() * 1024,
68                fd_count: process.fd_count().map_err(map_err)? as u64,
69                threads_count: status.threads,
70                user_time_us: (stat.utime as f64 * data.usage_time_to_us) as u64,
71                system_time_us: (stat.stime as f64 * data.usage_time_to_us) as u64,
72                start_time_ms: stat.starttime().get().map_err(map_err)?.timestamp_millis() as u64,
73            }
74        }
75        #[cfg(target_os = "macos")]
76        {
77            let pid = std::process::id();
78            let info = libproc::proc_pid::pidinfo::<libproc::task_info::TaskAllInfo>(pid as i32, 1)
79                .map_err(|e| Error::from_string(e, StatusCode::INTERNAL_SERVER_ERROR))?;
80            let mut rusage;
81            unsafe {
82                rusage = std::mem::zeroed();
83                if libc::getrusage(libc::RUSAGE_SELF, &mut rusage) != libc::EXIT_SUCCESS {
84                    panic!();
85                }
86            }
87            Stats {
88                name,
89                hostname,
90                mem_allocated_bytes,
91                allocations,
92                mem_rss: info.ptinfo.pti_resident_size,
93                mem_rss_peak: rusage.ru_maxrss as u64,
94                mem_virtual: info.ptinfo.pti_virtual_size,
95                fd_count: info.pbsd.pbi_nfiles as u64,
96                threads_count: info.ptinfo.pti_threadnum as u64,
97                user_time_us: (info.ptinfo.pti_total_user as f64 * data.usage_time_to_us) as u64,
98                system_time_us: (info.ptinfo.pti_total_system as f64 * data.usage_time_to_us)
99                    as u64,
100                start_time_ms: info.pbsd.pbi_start_tvsec * 1000 + info.pbsd.pbi_start_tvusec / 1000,
101            }
102        }
103    };
104    serde_json::to_string(&stats).map_err(InternalServerError)
105}
106
107#[derive(Serialize, Debug)]
108#[serde(rename_all = "camelCase")]
109struct Stats {
110    name: String,
111    hostname: String,
112    mem_rss: u64,
113    mem_rss_peak: u64,
114    mem_virtual: u64,
115    mem_allocated_bytes: u64,
116    allocations: u64,
117    fd_count: u64,
118    threads_count: u64,
119    user_time_us: u64,
120    system_time_us: u64,
121    start_time_ms: u64,
122}