Skip to main content

resource_sampler/
net.rs

1// Network device stats from /proc/net/dev (global) or /proc/<pid>/net/dev (process namespace).
2// Used for delta-over-time rate computation.
3
4/// Total RX and TX bytes across all interfaces from `/proc/net/dev`.
5/// Returns (rx_bytes, tx_bytes). Use with two samples and a time delta for rate.
6#[cfg(target_os = "linux")]
7pub fn read_net_dev() -> Option<(u64, u64)> {
8    read_net_dev_from_path("/proc/net/dev")
9}
10
11/// Total RX and TX bytes for the process's network namespace from `/proc/<pid>/net/dev`.
12/// Same format as global; useful for per-process (actually per-namespace) traffic rate.
13#[cfg(target_os = "linux")]
14pub fn read_net_dev_for_pid(pid: i32) -> Option<(u64, u64)> {
15    read_net_dev_from_path(&format!("/proc/{}/net/dev", pid))
16}
17
18#[cfg(target_os = "linux")]
19fn read_net_dev_from_path(path: &str) -> Option<(u64, u64)> {
20    let raw = std::fs::read_to_string(path).ok()?;
21    let mut rx_total = 0u64;
22    let mut tx_total = 0u64;
23    for line in raw.lines().skip(2) {
24        let colon = line.find(':')?;
25        let rest = line[colon + 1..].trim_start();
26        let nums: Vec<u64> = rest
27            .split_whitespace()
28            .filter_map(|s| s.parse().ok())
29            .collect();
30        if nums.len() >= 9 {
31            rx_total += nums[0];
32            tx_total += nums[8];
33        }
34    }
35    Some((rx_total, tx_total))
36}
37
38/// Sample RX/TX bytes per second for the process's network namespace.
39///
40/// The sampling window is controlled by the `PEEK_NET_SAMPLE_MS` environment variable:
41/// - If unset, a default of 1000ms (1s) is used.
42/// - If set to `0`, rate sampling is skipped and `None` is returned.
43/// - Otherwise, the value is interpreted as a window in milliseconds.
44///
45/// Returns (rx_bytes_per_sec, tx_bytes_per_sec).
46#[cfg(target_os = "linux")]
47pub fn sample_network_rate(pid: i32) -> Option<(u64, u64)> {
48    let window_ms = std::env::var("PEEK_NET_SAMPLE_MS")
49        .ok()
50        .and_then(|s| s.parse::<u64>().ok())
51        .unwrap_or(1000);
52
53    if window_ms == 0 {
54        return None;
55    }
56
57    let window = std::time::Duration::from_millis(window_ms);
58
59    let (r1, t1) = read_net_dev_for_pid(pid)?;
60    std::thread::sleep(window);
61    let (r2, t2) = read_net_dev_for_pid(pid)?;
62    let elapsed_secs = window_ms as f64 / 1000.0;
63    let rx = (r2.saturating_sub(r1) as f64 / elapsed_secs) as u64;
64    let tx = (t2.saturating_sub(t1) as f64 / elapsed_secs) as u64;
65    Some((rx, tx))
66}
67
68#[cfg(not(target_os = "linux"))]
69pub fn read_net_dev() -> Option<(u64, u64)> {
70    None
71}
72
73#[cfg(not(target_os = "linux"))]
74pub fn read_net_dev_for_pid(_pid: i32) -> Option<(u64, u64)> {
75    None
76}
77
78#[cfg(not(target_os = "linux"))]
79pub fn sample_network_rate(_pid: i32) -> Option<(u64, u64)> {
80    None
81}