diff --git a/src/backends/qemu/client.rs b/src/backends/qemu/client.rs index cd72439a..c2a339b8 100644 --- a/src/backends/qemu/client.rs +++ b/src/backends/qemu/client.rs @@ -36,6 +36,7 @@ use crate::{ }, }, instance::{InstanceClient, ThreadPoolSnapshot}, + util::Path, }; /// Wire client for an QEMU-managed VM. @@ -261,7 +262,7 @@ impl InstanceClient for QemuInstanceClient { "qemu topology reported no iothread TIDs".into(), )); } - let pid = Process::new(first_tid)? + let pid = Process::new_with_root(Path::proc_pid(first_tid))? .status() .map_err(|e| BackendClientError::InvalidState(format!("pid resolution: {e}")))? .tgid; diff --git a/src/backends/qemu/helpers.rs b/src/backends/qemu/helpers.rs index c62d08c3..f422ce7d 100644 --- a/src/backends/qemu/helpers.rs +++ b/src/backends/qemu/helpers.rs @@ -29,7 +29,10 @@ use regex::Regex; use rustix::param; use thiserror::Error; -use crate::backends::{BackendClientError, VqMapping}; +use crate::{ + backends::{BackendClientError, VqMapping}, + util::Path, +}; /// Errors produced while querying or reconfiguring a QEMU vm. #[derive(Debug, Error)] @@ -229,7 +232,7 @@ impl CpuSample { wall: Instant::now(), }; - let p = Process::new(pid)?; + let p = Process::new_with_root(Path::proc_pid(pid))?; for &tid in iothread_tids.values() { let stat = p.task_from_tid(tid)?.stat()?; out.jiffies.insert(tid, stat.utime + stat.stime); diff --git a/src/controller.rs b/src/controller.rs index e5026061..21fe1be8 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -14,7 +14,7 @@ use std::{ }; use futures_util::future::join_all; -use procfs::{CpuPressure, Current, CurrentSI, IoPressure, MemoryPressure, ProcError}; +use procfs::{CpuPressure, Current, FromReadSI, IoPressure, MemoryPressure, ProcError}; use serde::{Deserialize, Serialize}; use statistical::median; use thiserror::Error; @@ -1127,7 +1127,9 @@ fn read_psi_sample() -> PsiSample { /// Read aggregate host CPU counters. fn read_host_cpu_sample() -> Result { - let total = procfs::KernelStats::current()?.total; + let total = + procfs::KernelStats::from_file(Path::new("/proc/stat"), procfs::current_system_info())? + .total; let idle_ticks = total.idle.saturating_add(total.iowait.unwrap_or(0)); let total_ticks = total .user diff --git a/src/instance.rs b/src/instance.rs index 8ce74b2f..5473cb29 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -645,7 +645,7 @@ fn read_cpu_sample( pid: i32, filter: &crate::instance::ThreadNameFilter, ) -> Result { - let process = Process::new(pid)?; + let process = Process::new_with_root(Path::proc_pid(pid))?; let tasks = process.tasks()?; let mut cpu_ticks = 0u64; let mut thread_count = 0u32; @@ -698,7 +698,7 @@ fn read_cpu_sample( /// Read cgroup v2 CPU quota and throttle counters for `pid`. fn read_cgroup_sample(pid: i32) -> Result { - let process = Process::new(pid)?; + let process = Process::new_with_root(Path::proc_pid(pid))?; let cgroups = process.cgroups()?; let path = cgroups .0 diff --git a/src/util.rs b/src/util.rs index 49667f2a..60c77198 100644 --- a/src/util.rs +++ b/src/util.rs @@ -90,6 +90,16 @@ impl Path { pub fn glob(&self) -> glob::Paths { glob::glob(&self.path.to_string_lossy()).expect("valid glob pattern") } + + /// `/proc/` after applying [`IO_THREAD_CONTROLLER_ROOT_PATH`]. + /// + /// `procfs::process::Process::new` always opens the real `/proc`. Callers + /// that should honor the mock root pass this path to + /// `Process::new_with_root`. + pub fn proc_pid(pid: i32) -> std::path::PathBuf { + let path = Self::new(&format!("/proc/{pid}")); + std::path::PathBuf::from(std::path::Path::as_os_str(&path)) + } } impl From<&str> for Path {