Add support for `/proc/stat` and `/proc/uptime`
This commit is contained in:
parent
a13d2703fb
commit
66356e133d
|
|
@ -288,6 +288,7 @@ dependencies = [
|
|||
name = "aster-softirq"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aster-util",
|
||||
"component",
|
||||
"intrusive-collections",
|
||||
"ostd",
|
||||
|
|
@ -325,6 +326,7 @@ dependencies = [
|
|||
"aster-rights",
|
||||
"aster-rights-proc",
|
||||
"inherit-methods-macro",
|
||||
"osdk-heap-allocator",
|
||||
"ostd",
|
||||
"typeflags-util",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -12,11 +12,12 @@ use self::{
|
|||
sys::SysDirOps,
|
||||
template::{DirOps, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps},
|
||||
thread_self::ThreadSelfSymOps,
|
||||
uptime::UptimeFileOps,
|
||||
};
|
||||
use crate::{
|
||||
events::Observer,
|
||||
fs::{
|
||||
procfs::filesystems::FileSystemsFileOps,
|
||||
procfs::{filesystems::FileSystemsFileOps, stat::StatFileOps},
|
||||
registry::{FsProperties, FsType},
|
||||
utils::{mkmod, DirEntryVecExt, FileSystem, FsFlags, Inode, SuperBlock, NAME_MAX},
|
||||
},
|
||||
|
|
@ -34,9 +35,11 @@ mod loadavg;
|
|||
mod meminfo;
|
||||
mod pid;
|
||||
mod self_;
|
||||
mod stat;
|
||||
mod sys;
|
||||
mod template;
|
||||
mod thread_self;
|
||||
mod uptime;
|
||||
|
||||
pub(super) fn init() {
|
||||
super::registry::register(&ProcFsType).unwrap();
|
||||
|
|
@ -156,6 +159,10 @@ impl DirOps for RootDirOps {
|
|||
LoadAvgFileOps::new_inode(this_ptr.clone())
|
||||
} else if name == "cpuinfo" {
|
||||
CpuInfoFileOps::new_inode(this_ptr.clone())
|
||||
} else if name == "uptime" {
|
||||
UptimeFileOps::new_inode(this_ptr.clone())
|
||||
} else if name == "stat" {
|
||||
StatFileOps::new_inode(this_ptr.clone())
|
||||
} else if name == "cmdline" {
|
||||
CmdLineFileOps::new_inode(this_ptr.clone())
|
||||
} else if let Ok(pid) = name.parse::<Pid>() {
|
||||
|
|
@ -188,6 +195,9 @@ impl DirOps for RootDirOps {
|
|||
.put_entry_if_not_found("loadavg", || LoadAvgFileOps::new_inode(this_ptr.clone()));
|
||||
cached_children
|
||||
.put_entry_if_not_found("cpuinfo", || CpuInfoFileOps::new_inode(this_ptr.clone()));
|
||||
cached_children
|
||||
.put_entry_if_not_found("uptime", || UptimeFileOps::new_inode(this_ptr.clone()));
|
||||
cached_children.put_entry_if_not_found("stat", || StatFileOps::new_inode(this_ptr.clone()));
|
||||
cached_children
|
||||
.put_entry_if_not_found("cmdline", || CmdLineFileOps::new_inode(this_ptr.clone()));
|
||||
for process in process_table::process_table_mut().iter() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,155 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! This module offers `/proc/stat` file support, which provides
|
||||
//! information about kernel system statistics.
|
||||
//!
|
||||
//! Reference: <https://man7.org/linux/man-pages/man5/proc_stat.5.html>
|
||||
|
||||
use core::fmt::Write;
|
||||
|
||||
use aster_softirq::{
|
||||
iter_irq_counts_across_all_cpus, iter_softirq_counts_across_all_cpus, softirq_id::*,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
fs::{
|
||||
procfs::template::{FileOps, ProcFileBuilder},
|
||||
utils::{Inode, InodeMode},
|
||||
},
|
||||
prelude::*,
|
||||
process::count_total_forks,
|
||||
sched::nr_queued_and_running,
|
||||
thread::collect_context_switch_count,
|
||||
time::{cpu_time_stats::CpuTimeStatsManager, SystemTime, START_TIME},
|
||||
};
|
||||
|
||||
/// Represents the inode at `/proc/stat`.
|
||||
pub struct StatFileOps;
|
||||
|
||||
impl StatFileOps {
|
||||
pub fn new_inode(parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
|
||||
ProcFileBuilder::new(Self, InodeMode::from_bits_truncate(0o444))
|
||||
.parent(parent)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn collect_stats() -> String {
|
||||
let mut stat_output = String::new();
|
||||
let stats_manager = CpuTimeStatsManager::singleton();
|
||||
|
||||
// Global CPU statistics:
|
||||
// cpu <user> <nice> <system> <idle> <iowait> <irq> <softirq> <steal> <guest> <guest_nice>
|
||||
let global_stats = stats_manager.collect_stats_on_all_cpus();
|
||||
writeln!(
|
||||
stat_output,
|
||||
"cpu {} {} {} {} {} {} {} {} {} {}",
|
||||
global_stats.user.as_u64(),
|
||||
global_stats.nice.as_u64(),
|
||||
global_stats.system.as_u64(),
|
||||
global_stats.idle.as_u64(),
|
||||
global_stats.iowait.as_u64(),
|
||||
global_stats.irq.as_u64(),
|
||||
global_stats.softirq.as_u64(),
|
||||
global_stats.steal.as_u64(),
|
||||
global_stats.guest.as_u64(),
|
||||
global_stats.guest_nice.as_u64()
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Per-CPU lines
|
||||
for cpu_id in ostd::cpu::all_cpus() {
|
||||
let cpu_stats = stats_manager.collect_stats_on_cpu(cpu_id);
|
||||
writeln!(
|
||||
stat_output,
|
||||
"cpu{} {} {} {} {} {} {} {} {} {} {}",
|
||||
cpu_id.as_usize(),
|
||||
cpu_stats.user.as_u64(),
|
||||
cpu_stats.nice.as_u64(),
|
||||
cpu_stats.system.as_u64(),
|
||||
cpu_stats.idle.as_u64(),
|
||||
cpu_stats.iowait.as_u64(),
|
||||
cpu_stats.irq.as_u64(),
|
||||
cpu_stats.softirq.as_u64(),
|
||||
cpu_stats.steal.as_u64(),
|
||||
cpu_stats.guest.as_u64(),
|
||||
cpu_stats.guest_nice.as_u64()
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// IRQ statistics: the total count followed by per-IRQ counts
|
||||
let irq_stats = iter_irq_counts_across_all_cpus();
|
||||
let mut total_irqs = 0usize;
|
||||
let mut irq_counts = Vec::new();
|
||||
for count in irq_stats {
|
||||
total_irqs += count;
|
||||
irq_counts.push(count);
|
||||
}
|
||||
write!(stat_output, "intr {}", total_irqs).unwrap();
|
||||
for count in irq_counts {
|
||||
write!(stat_output, " {}", count).unwrap();
|
||||
}
|
||||
|
||||
writeln!(stat_output).unwrap();
|
||||
|
||||
// Context switch count
|
||||
let context_switches: usize = collect_context_switch_count();
|
||||
writeln!(stat_output, "ctxt {}", context_switches).unwrap();
|
||||
|
||||
// Boot time (seconds since UNIX epoch)
|
||||
if let Some(start_time) = START_TIME.get() {
|
||||
let boot_time = start_time
|
||||
.duration_since(&SystemTime::UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs();
|
||||
writeln!(stat_output, "btime {}", boot_time).unwrap();
|
||||
} else {
|
||||
writeln!(stat_output, "btime {}", 0).unwrap();
|
||||
}
|
||||
|
||||
// Process count (number of forks since boot)
|
||||
writeln!(stat_output, "processes {}", count_total_forks()).unwrap();
|
||||
|
||||
// Running and blocked processes
|
||||
let (_, running_count) = nr_queued_and_running();
|
||||
writeln!(stat_output, "procs_running {}", running_count).unwrap();
|
||||
|
||||
// TODO: Blocked processes
|
||||
writeln!(stat_output, "procs_blocked {}", 0).unwrap();
|
||||
|
||||
// Softirq statistics
|
||||
let softirq_stats = iter_softirq_counts_across_all_cpus();
|
||||
let softirq_stats: Vec<usize> = softirq_stats.collect();
|
||||
let total_softirqs: usize = softirq_stats.iter().sum();
|
||||
|
||||
// We only have 5 defined softirq types; the rest are reserved.
|
||||
// Fill in zeros for the reserved types to match the expected output format.
|
||||
writeln!(
|
||||
stat_output,
|
||||
"softirq {} {} {} {} {} {} {} {} {} {} {}",
|
||||
total_softirqs,
|
||||
softirq_stats[TASKLESS_URGENT_SOFTIRQ_ID as usize], // TASKLESS_URGENT
|
||||
softirq_stats[TIMER_SOFTIRQ_ID as usize], // TIMER
|
||||
softirq_stats[TASKLESS_SOFTIRQ_ID as usize], // TASKLESS
|
||||
softirq_stats[NETWORK_TX_SOFTIRQ_ID as usize], // NETWORK_TX
|
||||
softirq_stats[NETWORK_RX_SOFTIRQ_ID as usize], // NETWORK_RX
|
||||
0usize, // Reserved
|
||||
0usize, // Reserved
|
||||
0usize, // Reserved
|
||||
0usize, // Reserved
|
||||
0usize, // Reserved
|
||||
)
|
||||
.unwrap();
|
||||
stat_output
|
||||
}
|
||||
}
|
||||
|
||||
impl FileOps for StatFileOps {
|
||||
/// Retrieve the data for `/proc/stat`.
|
||||
fn data(&self) -> Result<Vec<u8>> {
|
||||
// Implementation to gather and format the statistics
|
||||
let output = Self::collect_stats();
|
||||
Ok(output.into_bytes())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! This module offers `/proc/uptime` file support, which provides
|
||||
//! information about the system uptime and idle time.
|
||||
//!
|
||||
//! Reference: <https://man7.org/linux/man-pages/man5/proc_uptime.5.html>
|
||||
|
||||
use alloc::format;
|
||||
|
||||
use crate::{
|
||||
fs::{
|
||||
procfs::template::{FileOps, ProcFileBuilder},
|
||||
utils::{Inode, InodeMode},
|
||||
},
|
||||
prelude::*,
|
||||
time::cpu_time_stats::CpuTimeStatsManager,
|
||||
};
|
||||
|
||||
/// Represents the inode at `/proc/uptime`.
|
||||
pub struct UptimeFileOps;
|
||||
|
||||
impl UptimeFileOps {
|
||||
pub fn new_inode(parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
|
||||
ProcFileBuilder::new(Self, InodeMode::from_bits_truncate(0o444))
|
||||
.parent(parent)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn collect_uptime() -> String {
|
||||
let uptime = aster_time::read_monotonic_time().as_secs_f32();
|
||||
let cpustat = CpuTimeStatsManager::singleton();
|
||||
let idle_time = cpustat
|
||||
.collect_stats_on_all_cpus()
|
||||
.idle
|
||||
.as_duration()
|
||||
.as_secs_f32();
|
||||
format!("{:.2} {:.2}\n", uptime, idle_time)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileOps for UptimeFileOps {
|
||||
/// Retrieve the data for `/proc/uptime`.
|
||||
fn data(&self) -> Result<Vec<u8>> {
|
||||
let output = Self::collect_uptime();
|
||||
Ok(output.into_bytes())
|
||||
}
|
||||
}
|
||||
|
|
@ -16,11 +16,7 @@ ProcSelfFdInfo.Flags
|
|||
ProcCpuinfo.RequiredFieldsArePresent
|
||||
ProcCpuinfo.DeniesWriteRoot
|
||||
ProcCpuinfo.DeniesWriteNonRoot
|
||||
ProcUptime.IsPresent
|
||||
ProcMeminfo.ContainsBasicFields
|
||||
ProcStat.ContainsBasicFields
|
||||
ProcStat.EndsWithNewline
|
||||
ProcStat.Fields
|
||||
ProcSelfStat.PopulateWriteRSS
|
||||
ProcSelfStat.PopulateNoneRSS
|
||||
ProcPidStatusTest.HasBasicFields
|
||||
|
|
|
|||
Loading…
Reference in New Issue