From 66356e133db8eee843bdffae29303e3e62fc4e7e Mon Sep 17 00:00:00 2001 From: Yang Zhichao Date: Fri, 19 Sep 2025 21:55:13 +0800 Subject: [PATCH] Add support for `/proc/stat` and `/proc/uptime` --- Cargo.lock | 2 + kernel/src/fs/procfs/mod.rs | 12 +- kernel/src/fs/procfs/stat.rs | 155 +++++++++++++++++++ kernel/src/fs/procfs/uptime.rs | 48 ++++++ test/src/syscall/gvisor/blocklists/proc_test | 4 - 5 files changed, 216 insertions(+), 5 deletions(-) mode change 100644 => 100755 kernel/src/fs/procfs/mod.rs create mode 100644 kernel/src/fs/procfs/stat.rs create mode 100644 kernel/src/fs/procfs/uptime.rs diff --git a/Cargo.lock b/Cargo.lock index 9b056389b..d0a8b1612 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] diff --git a/kernel/src/fs/procfs/mod.rs b/kernel/src/fs/procfs/mod.rs old mode 100644 new mode 100755 index 533c4ab70..5076e647a --- a/kernel/src/fs/procfs/mod.rs +++ b/kernel/src/fs/procfs/mod.rs @@ -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::() { @@ -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() { diff --git a/kernel/src/fs/procfs/stat.rs b/kernel/src/fs/procfs/stat.rs new file mode 100644 index 000000000..62c1c9306 --- /dev/null +++ b/kernel/src/fs/procfs/stat.rs @@ -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: + +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) -> Arc { + 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 + 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 = 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> { + // Implementation to gather and format the statistics + let output = Self::collect_stats(); + Ok(output.into_bytes()) + } +} diff --git a/kernel/src/fs/procfs/uptime.rs b/kernel/src/fs/procfs/uptime.rs new file mode 100644 index 000000000..4a7238f7b --- /dev/null +++ b/kernel/src/fs/procfs/uptime.rs @@ -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: + +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) -> Arc { + 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> { + let output = Self::collect_uptime(); + Ok(output.into_bytes()) + } +} diff --git a/test/src/syscall/gvisor/blocklists/proc_test b/test/src/syscall/gvisor/blocklists/proc_test index f34bdd599..1aefdbfd3 100644 --- a/test/src/syscall/gvisor/blocklists/proc_test +++ b/test/src/syscall/gvisor/blocklists/proc_test @@ -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