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}