Skip to main content

pingap_performance/
process.rs

1// Copyright 2024-2025 Tree xie.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use bytesize::ByteSize;
16use memory_stats::memory_stats;
17use serde::{Deserialize, Serialize};
18use std::process;
19use std::sync::LazyLock;
20use std::sync::atomic::{AtomicI32, AtomicU64, Ordering};
21use sysinfo::MemoryRefreshKind;
22use sysinfo::{RefreshKind, System};
23
24static ACCEPTED: LazyLock<AtomicU64> = LazyLock::new(|| AtomicU64::new(0));
25static PROCESSING: LazyLock<AtomicI32> = LazyLock::new(|| AtomicI32::new(0));
26
27/// Increments the request acceptance and processing counters.
28/// This should be called when a new request is received to track request metrics.
29pub fn accept_request() {
30    ACCEPTED.fetch_add(1, Ordering::Relaxed);
31    PROCESSING.fetch_add(1, Ordering::Relaxed);
32}
33
34/// Decrements the request processing counter when a request completes.
35/// This should be called when a request finishes processing to maintain accurate metrics.
36pub fn end_request() {
37    PROCESSING.fetch_sub(1, Ordering::Relaxed);
38}
39
40/// Returns a tuple of (currently processing requests, total accepted requests).
41///
42/// Returns:
43/// - `i32`: Number of requests currently being processed
44/// - `u64`: Total number of requests accepted since startup
45pub fn get_processing_accepted() -> (i32, u64) {
46    let processing = PROCESSING.load(Ordering::Relaxed);
47    let accepted = ACCEPTED.load(Ordering::Relaxed);
48    (processing, accepted)
49}
50
51#[derive(Serialize, Deserialize, Debug)]
52pub struct ProcessSystemInfo {
53    /// Current memory usage in megabytes
54    pub memory_mb: usize,
55    /// Current memory usage as a human-readable string (e.g. "100 MB")
56    pub memory: String,
57    /// CPU architecture (e.g. "x86_64", "aarch64")
58    pub arch: String,
59    /// Number of logical CPU cores
60    pub cpus: usize,
61    /// Number of physical CPU cores
62    pub physical_cpus: usize,
63    /// Total system memory as a human-readable string
64    pub total_memory: String,
65    /// Used system memory as a human-readable string
66    pub used_memory: String,
67    /// Kernel version string
68    pub kernel: String,
69    /// Process ID of the current process
70    pub pid: u32,
71    /// Number of threads configured across all servers
72    pub threads: i64,
73    /// Number of open file descriptors (Linux only)
74    pub fd_count: usize,
75    /// Number of IPv4 TCP connections (Linux only)
76    pub tcp_count: usize,
77    /// Number of IPv6 TCP connections (Linux only)
78    pub tcp6_count: usize,
79}
80
81/// Gathers and returns system information including memory usage, CPU details,
82/// process statistics and network connection counts
83pub fn get_process_system_info() -> ProcessSystemInfo {
84    let pid = process::id();
85
86    cfg_if::cfg_if! {
87        if #[cfg(target_os = "linux")] {
88            let (fd_count, tcp_count, tcp6_count, threads) = if let Ok(p) = procfs::process::Process::new(pid as i32) {
89                let mut threads = -1_i64;
90                if let Ok(stat) = p.stat() {
91                    threads = stat.num_threads;
92                }
93                (
94                    p.fd_count().unwrap_or_default(),
95                    p.tcp().unwrap_or_default().len(),
96                    p.tcp6().unwrap_or_default().len(),
97                    threads,
98                )
99            } else {
100                (0, 0, 0, -1_i64)
101            };
102        } else {
103            let (fd_count, tcp_count, tcp6_count, threads) = (0, 0, 0, -1_i64);
104        }
105    }
106
107    let mut memory = "".to_string();
108    let mut memory_mb = 0;
109    if let Some(value) = memory_stats() {
110        memory_mb = value.physical_mem / (1024 * 1024);
111        memory = ByteSize(value.physical_mem as u64).to_string();
112    }
113    let cpus = num_cpus::get();
114    let physical_cpus = num_cpus::get_physical();
115    let kind = MemoryRefreshKind::nothing().with_ram();
116    let mut sys =
117        System::new_with_specifics(RefreshKind::nothing().with_memory(kind));
118    sys.refresh_memory();
119
120    ProcessSystemInfo {
121        memory,
122        memory_mb,
123        arch: System::cpu_arch(),
124        cpus,
125        physical_cpus,
126        kernel: System::kernel_version().unwrap_or_default(),
127        total_memory: ByteSize(sys.total_memory()).to_string(),
128        used_memory: ByteSize(sys.used_memory()).to_string(),
129        pid,
130        threads,
131        fd_count,
132        tcp_count,
133        tcp6_count,
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140    use pretty_assertions::assert_eq;
141
142    #[test]
143    fn test_get_process_system_info() {
144        let info = get_process_system_info();
145        assert_eq!(true, info.memory_mb > 0);
146        assert_eq!(true, !info.memory.is_empty());
147        assert_eq!(true, !info.arch.is_empty());
148        assert_eq!(true, info.cpus > 0);
149        assert_eq!(true, info.physical_cpus > 0);
150        assert_eq!(true, !info.kernel.is_empty());
151        assert_eq!(true, info.pid != 0);
152    }
153
154    #[test]
155    fn test_get_processing_accepted() {
156        let (processing, accepted) = get_processing_accepted();
157        assert_eq!(processing, 0);
158        assert_eq!(accepted, 0);
159        accept_request();
160        let (processing, accepted) = get_processing_accepted();
161        assert_eq!(processing, 1);
162        assert_eq!(accepted, 1);
163        end_request();
164        let (processing, accepted) = get_processing_accepted();
165        assert_eq!(processing, 0);
166        assert_eq!(accepted, 1);
167    }
168}