mirror of
https://github.com/containers/youki
synced 2024-06-10 08:46:21 +02:00
Retrieve cpu stats from cgroups v1
This commit is contained in:
parent
201274b12b
commit
87c7db7e5a
|
@ -1,9 +1,12 @@
|
|||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use oci_spec::{LinuxCpu, LinuxResources};
|
||||
|
||||
use crate::cgroups::common;
|
||||
use crate::cgroups::{
|
||||
common,
|
||||
stats::{CpuThrottling, StatsProvider},
|
||||
};
|
||||
|
||||
use super::Controller;
|
||||
|
||||
|
@ -12,6 +15,7 @@ const CGROUP_CPU_QUOTA: &str = "cpu.cfs_quota_us";
|
|||
const CGROUP_CPU_PERIOD: &str = "cpu.cfs_period_us";
|
||||
const CGROUP_CPU_RT_RUNTIME: &str = "cpu.rt_runtime_us";
|
||||
const CGROUP_CPU_RT_PERIOD: &str = "cpu.rt_period_us";
|
||||
const CGROUP_CPU_STAT: &str = "cpu.stat";
|
||||
|
||||
pub struct Cpu {}
|
||||
|
||||
|
@ -44,6 +48,51 @@ impl Controller for Cpu {
|
|||
}
|
||||
}
|
||||
|
||||
impl StatsProvider for Cpu {
|
||||
type Stats = CpuThrottling;
|
||||
|
||||
fn stats(cgroup_path: &Path) -> Result<Self::Stats> {
|
||||
let mut stats = CpuThrottling::default();
|
||||
let stat_path = cgroup_path.join(CGROUP_CPU_STAT);
|
||||
let stat_content = common::read_cgroup_file(&stat_path)?;
|
||||
|
||||
let parts: Vec<&str> = stat_content.split_ascii_whitespace().collect();
|
||||
if parts.len() < 6 {
|
||||
bail!(
|
||||
"{} contains less than the expected number of entries",
|
||||
stat_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
if parts[0] != "nr_periods" {
|
||||
bail!(
|
||||
"{} does not contain the number of elapsed periods",
|
||||
stat_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
if parts[2] != "nr_throttled" {
|
||||
bail!(
|
||||
"{} does not contain the number of throttled periods",
|
||||
stat_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
if parts[4] != "throttled_time" {
|
||||
bail!(
|
||||
"{} does not contain the total time tasks have spent throttled",
|
||||
stat_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
stats.periods = parts[1].parse().context("failed to parse nr_periods")?;
|
||||
stats.throttled_periods = parts[3].parse().context("failed to parse nr_throttled")?;
|
||||
stats.throttled_time = parts[5].parse().context("failed to parse throttled time")?;
|
||||
|
||||
Ok(stats)
|
||||
}
|
||||
}
|
||||
|
||||
impl Cpu {
|
||||
fn apply(root_path: &Path, cpu: &LinuxCpu) -> Result<()> {
|
||||
if let Some(cpu_shares) = cpu.shares {
|
||||
|
|
|
@ -1,10 +1,24 @@
|
|||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use oci_spec::LinuxResources;
|
||||
|
||||
use crate::cgroups::{
|
||||
common,
|
||||
stats::{CpuUsage, StatsProvider},
|
||||
};
|
||||
|
||||
use super::Controller;
|
||||
|
||||
// Contains user mode and kernel mode cpu consumption
|
||||
const CGROUP_CPUACCT_STAT: &str = "cpuacct.stat";
|
||||
// Contains overall cpu consumption
|
||||
const CGROUP_CPUACCT_USAGE: &str = "cpuacct.usage";
|
||||
// Contains user mode and kernel mode cpu consumption differentiated by core
|
||||
const CGROUP_CPUACCT_USAGE_ALL: &str = "cpuacct.usage_all";
|
||||
// Contains overall cpu consumption differentiated by core
|
||||
const CGROUP_CPUACCT_PERCPU: &str = "cpuacct.usage_percpu";
|
||||
|
||||
pub struct CpuAcct {}
|
||||
|
||||
impl Controller for CpuAcct {
|
||||
|
@ -14,12 +28,102 @@ impl Controller for CpuAcct {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// apply never needs to be called, for accounting only
|
||||
fn needs_to_handle(_linux_resources: &LinuxResources) -> Option<&Self::Resource> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl StatsProvider for CpuAcct {
|
||||
type Stats = CpuUsage;
|
||||
|
||||
fn stats(cgroup_path: &Path) -> Result<Self::Stats> {
|
||||
let mut stats = CpuUsage::default();
|
||||
Self::get_total_cpu_usage(cgroup_path, &mut stats)?;
|
||||
Self::get_per_core_usage(cgroup_path, &mut stats)?;
|
||||
|
||||
Ok(stats)
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuAcct {
|
||||
fn get_total_cpu_usage(cgroup_path: &Path, stats: &mut CpuUsage) -> Result<()> {
|
||||
let stat_file_path = cgroup_path.join(CGROUP_CPUACCT_STAT);
|
||||
let stat_file_content = common::read_cgroup_file(&stat_file_path)?;
|
||||
|
||||
// the first two entries of the file should look like this
|
||||
// user 746908
|
||||
// system 213896
|
||||
let parts: Vec<&str> = stat_file_content.split_whitespace().collect();
|
||||
|
||||
if parts.len() < 4 {
|
||||
bail!(
|
||||
"{} contains less than the expected number of entries",
|
||||
stat_file_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
if !parts[0].eq("user") {
|
||||
bail!(
|
||||
"{} does not contain user mode cpu usage",
|
||||
stat_file_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
if !parts[2].eq("system") {
|
||||
bail!(
|
||||
"{} does not contain kernel mode cpu usage",
|
||||
stat_file_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
stats.usage_user = parts[1]
|
||||
.parse()
|
||||
.context("failed to parse user mode cpu usage")?;
|
||||
stats.usage_kernel = parts[3]
|
||||
.parse()
|
||||
.context("failed to parse kernel mode cpu usage")?;
|
||||
|
||||
let total = common::read_cgroup_file(cgroup_path.join(CGROUP_CPUACCT_USAGE))?;
|
||||
stats.usage_total = total
|
||||
.trim()
|
||||
.parse()
|
||||
.context("failed to parse total cpu usage")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_per_core_usage(cgroup_path: &Path, stats: &mut CpuUsage) -> Result<()> {
|
||||
let all_content = common::read_cgroup_file(cgroup_path.join(CGROUP_CPUACCT_USAGE_ALL))?;
|
||||
// first line is header, skip it
|
||||
for entry in all_content.lines().skip(1) {
|
||||
let entry_parts: Vec<&str> = entry.split_ascii_whitespace().collect();
|
||||
if entry_parts.len() != 3 {
|
||||
continue;
|
||||
}
|
||||
|
||||
stats.per_core_usage_user.push(
|
||||
entry_parts[1]
|
||||
.parse()
|
||||
.context("failed to parse per core user mode cpu usage")?,
|
||||
);
|
||||
stats.per_core_usage_kernel.push(
|
||||
entry_parts[2]
|
||||
.parse()
|
||||
.context("failed to parse per core kernel mode cpu usage")?,
|
||||
);
|
||||
}
|
||||
|
||||
let percpu_content = common::read_cgroup_file(cgroup_path.join(CGROUP_CPUACCT_PERCPU))?;
|
||||
stats.per_core_usage_total = percpu_content
|
||||
.split_ascii_whitespace()
|
||||
.map(|v| v.parse())
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.context("failed to parse per core cpu usage")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs;
|
||||
|
|
Loading…
Reference in New Issue