Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement events command for cgroup v1 stats #171

Merged
merged 12 commits into from
Jul 31, 2021
10 changes: 10 additions & 0 deletions src/cgroups/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ fn booted() -> Result<bool> {
use crate::cgroups::v1;
use crate::cgroups::v2;

use super::stats::Stats;

pub const CGROUP_PROCS: &str = "cgroup.procs";
pub const DEFAULT_CGROUP_ROOT: &str = "/sys/fs/cgroup";

Expand All @@ -32,6 +34,8 @@ pub trait CgroupManager {
fn remove(&self) -> Result<()>;
// Sets the freezer cgroup to the specified state
fn freeze(&self, state: FreezerState) -> Result<()>;
/// Retrieve statistics for the cgroup
fn stats(&self) -> Result<Stats>;
}

#[derive(Debug)]
Expand Down Expand Up @@ -79,6 +83,12 @@ pub fn write_cgroup_file<P: AsRef<Path>, T: ToString>(path: P, data: T) -> Resul
Ok(())
}

#[inline]
pub fn read_cgroup_file<P: AsRef<Path>>(path: P) -> Result<String> {
let path = path.as_ref();
fs::read_to_string(path).with_context(|| format!("failed to open {:?}", path))
}

pub fn get_supported_cgroup_fs() -> Result<Vec<Cgroup>> {
let cgroup_mount = Process::myself()?
.mountinfo()?
Expand Down
1 change: 1 addition & 0 deletions src/cgroups/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! freezing, checkpointing and restarting groups of processes.
pub mod common;
pub mod stats;
mod test;
pub mod v1;
pub mod v2;
263 changes: 263 additions & 0 deletions src/cgroups/stats.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
use anyhow::Result;
use std::{collections::HashMap, fmt::Display, path::Path};

pub trait StatsProvider {
type Stats;

fn stats(cgroup_path: &Path) -> Result<Self::Stats>;
}

/// Reports the statistics for a cgroup
#[derive(Debug)]
pub struct Stats {
/// Cpu statistics for the cgroup
pub cpu: CpuStats,
/// Pid statistics for the cgroup
pub pids: PidStats,
/// Hugetlb statistics for the cgroup
pub hugetlb: HashMap<String, HugeTlbStats>,
/// Blkio statistics for the cgroup
pub blkio: BlkioStats,
/// Memory statistics for the cgroup
pub memory: MemoryStats,
}

impl Default for Stats {
fn default() -> Self {
Self {
cpu: CpuStats::default(),
pids: PidStats::default(),
hugetlb: HashMap::new(),
blkio: BlkioStats::default(),
memory: MemoryStats::default(),
}
}
}

/// Reports the cpu statistics for a cgroup
#[derive(Debug)]
pub struct CpuStats {
/// Cpu usage statistics for the cgroup
pub usage: CpuUsage,
/// Cpu Throttling statistics for the cgroup
pub throttling: CpuThrottling,
}

impl Default for CpuStats {
fn default() -> Self {
Self {
usage: CpuUsage::default(),
throttling: CpuThrottling::default(),
}
}
}

/// Reports the cpu usage for a cgroup
#[derive(Debug, PartialEq, Eq)]
pub struct CpuUsage {
/// Cpu time consumed by tasks in total
pub usage_total: u64,
/// Cpu time consumed by tasks in user mode
pub usage_user: u64,
/// Cpu time consumed by tasks in kernel mode
pub usage_kernel: u64,
/// Cpu time consumed by tasks itemized per core
pub per_core_usage_total: Vec<u64>,
/// Cpu time consumed by tasks in user mode itemized per core
pub per_core_usage_user: Vec<u64>,
/// Cpu time consumed by tasks in kernel mode itemized per core
pub per_core_usage_kernel: Vec<u64>,
}

impl Default for CpuUsage {
fn default() -> Self {
Self {
usage_total: 0,
usage_user: 0,
usage_kernel: 0,
per_core_usage_total: Vec::new(),
per_core_usage_user: Vec::new(),
per_core_usage_kernel: Vec::new(),
}
}
}

/// Reports the cpu throttling for a cgroup
#[derive(Debug, PartialEq, Eq)]
pub struct CpuThrottling {
/// Number of period intervals (as specified in cpu.cfs_period_us) that have elapsed
pub periods: u64,
/// Number of period intervals where tasks have been throttled because they exhausted their quota
pub throttled_periods: u64,
/// Total time duration for which tasks have been throttled
pub throttled_time: u64,
}

impl Default for CpuThrottling {
fn default() -> Self {
Self {
periods: 0,
throttled_periods: 0,
throttled_time: 0,
}
}
}

/// Reports memory stats for a cgroup
#[derive(Debug)]
pub struct MemoryStats {
/// Usage of memory
pub memory: MemoryData,
/// Usage of memory and swap
pub memswap: MemoryData,
/// Usage of kernel memory
pub kernel: MemoryData,
/// Usage of kernel tcp memory
pub kernel_tcp: MemoryData,
/// Page cache in bytes
pub cache: u64,
/// Returns true if hierarchical accounting is enabled
pub hierarchy: bool,
/// Various memory statistics
pub stats: HashMap<String, u64>,
}

impl Default for MemoryStats {
fn default() -> Self {
Self {
memory: MemoryData::default(),
memswap: MemoryData::default(),
kernel: MemoryData::default(),
kernel_tcp: MemoryData::default(),
cache: 0,
hierarchy: false,
stats: HashMap::default(),
}
}
}

/// Reports memory stats for one type of memory
#[derive(Debug, PartialEq, Eq)]
pub struct MemoryData {
/// Usage in bytes
pub usage: u64,
/// Maximum recorded usage in bytes
pub max_usage: u64,
/// Number of times memory usage hit limits
pub fail_count: u64,
/// Memory usage limit
pub limit: u64,
}

impl Default for MemoryData {
fn default() -> Self {
Self {
usage: 0,
max_usage: 0,
fail_count: 0,
limit: 0,
}
}
}

/// Reports pid stats for a cgroup
#[derive(Debug, PartialEq, Eq)]
pub struct PidStats {
/// Current number of active pids
pub current: u64,
/// Allowed number of active pids (0 means no limit)
pub limit: u64,
}

impl Default for PidStats {
fn default() -> Self {
Self {
current: 0,
limit: 0,
}
}
}

/// Reports block io stats for a cgroup
#[derive(Debug, PartialEq, Eq)]
pub struct BlkioStats {
// Number of bytes transfered to/from a device by the cgroup
pub service_bytes: Vec<BlkioDeviceStat>,
// Number of I/O operations performed on a device by the cgroup
pub serviced: Vec<BlkioDeviceStat>,
// Time in milliseconds that the cgroup had access to a device
pub time: Vec<BlkioDeviceStat>,
// Number of sectors transferred to/from a device by the cgroup
pub sectors: Vec<BlkioDeviceStat>,
// Total time between request dispatch and request completion
pub service_time: Vec<BlkioDeviceStat>,
// Total time spend waiting in the scheduler queues for service
pub wait_time: Vec<BlkioDeviceStat>,
// Number of requests queued for I/O operations
pub queued: Vec<BlkioDeviceStat>,
// Number of requests merged into requests for I/O operations
pub merged: Vec<BlkioDeviceStat>,
}

impl Default for BlkioStats {
fn default() -> Self {
Self {
service_bytes: Vec::new(),
serviced: Vec::new(),
time: Vec::new(),
sectors: Vec::new(),
service_time: Vec::new(),
wait_time: Vec::new(),
queued: Vec::new(),
merged: Vec::new(),
}
}
}

/// Reports single stat value for a specific device
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct BlkioDeviceStat {
/// Major device number
pub major: u64,
/// Minor device number
pub minor: u64,
/// Operation type
pub op_type: Option<String>,
/// Stat value
pub value: u64,
}

impl Display for BlkioDeviceStat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(op_type) = &self.op_type {
write!(
f,
"{}:{} {} {}",
self.major, self.minor, op_type, self.value
)
} else {
write!(f, "{}:{} {}", self.major, self.minor, self.value)
}
}
}

/// Reports hugetlb stats for a cgroup
#[derive(Debug, PartialEq, Eq)]
pub struct HugeTlbStats {
/// Current usage in bytes
pub usage: u64,
/// Maximum recorded usage in bytes
pub max_usage: u64,
/// Number of allocation failures due to HugeTlb usage limit
pub fail_count: u64,
}

impl Default for HugeTlbStats {
fn default() -> Self {
Self {
usage: 0,
max_usage: 0,
fail_count: 0,
}
}
}
8 changes: 5 additions & 3 deletions src/cgroups/test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![cfg(test)]

use anyhow::Result;
use anyhow::{Context, Result};
use std::{
io::Write,
path::{Path, PathBuf},
Expand All @@ -25,8 +25,10 @@ pub fn set_fixture(temp_dir: &Path, filename: &str, val: &str) -> Result<PathBuf
.create(true)
.write(true)
.truncate(true)
.open(&full_path)?
.write_all(val.as_bytes())?;
.open(&full_path)
.with_context(|| format!("failed to open {:?}", full_path))?
.write_all(val.as_bytes())
.with_context(|| format!("failed to write to {:?}", full_path))?;

Ok(full_path)
}
Expand Down
Loading