diff --git a/kernel/libk/src/task/process.rs b/kernel/libk/src/task/process.rs index 0f2ff258..5f378387 100644 --- a/kernel/libk/src/task/process.rs +++ b/kernel/libk/src/task/process.rs @@ -135,7 +135,8 @@ impl Process { io: IrqSafeSpinlock::new(ProcessIo::new()), }); - *process.sysfs_node.write() = Some(add_sysfs_node(&process)); + let sysfs_node = add_sysfs_node(&process); + *process.sysfs_node.write() = Some(sysfs_node.clone()); // Add a child if parent specified if let Some(parent) = parent { @@ -144,6 +145,7 @@ impl Process { // Create "main" thread let thread = Thread::new_uthread(process.id, Some(name), info.space, info.context); + thread.add_sysfs_node(&sysfs_node); process.inner.write().register_thread(thread.clone()); MANAGER.register_process(process.clone()); @@ -218,6 +220,9 @@ impl Process { let thread = Thread::new_uthread(self.id, None, space.clone(), context); let id = thread.id; + if let Some(sysfs_node) = self.sysfs_node.read().as_ref() { + thread.add_sysfs_node(&sysfs_node); + } inner.register_thread(thread.clone()); thread.enqueue(); diff --git a/kernel/libk/src/task/thread.rs b/kernel/libk/src/task/thread.rs index 8baf1abe..7b4b644a 100644 --- a/kernel/libk/src/task/thread.rs +++ b/kernel/libk/src/task/thread.rs @@ -9,6 +9,7 @@ use core::{ use alloc::{ collections::BTreeMap, + format, string::String, sync::{Arc, Weak}, }; @@ -36,6 +37,10 @@ use yggdrasil_abi::{ }; use crate::{ + fs::sysfs::{ + attribute::{StringAttribute, StringAttributeOps}, + object::KObject, + }, task::{ mem::ForeignPointer, sched::CpuQueue, @@ -95,6 +100,7 @@ pub struct Thread { // inner: IrqSafeSpinlock, info: IrqSafeRwLock, signal_queue: SegQueue, + sysfs_node: IrqSafeRwLock>>>>, pub events: ThreadEvents, pub kill: BoolEvent, @@ -154,6 +160,7 @@ impl Thread { signal_queue: SegQueue::new(), events: ThreadEvents::new(), kill: BoolEvent::new(), + sysfs_node: IrqSafeRwLock::new(None), affinity: ThreadAffinity::any_cpu(), }); @@ -453,6 +460,18 @@ impl Thread { self.enqueue(); } + fn cleanup(&self) { + // Can detach debugger now + { + let mut debug = self.debug.lock(); + debug.tracer = None; + debug.single_step = false; + self.trace_flags.store(0, Ordering::Release); + } + + self.drop_sysfs_node(); + } + /// Returns the current thread on the CPU. /// /// # Panics @@ -591,6 +610,40 @@ impl Thread { } } } + + // sysfs operations + + pub(super) fn add_sysfs_node(self: &Arc, parent: &Arc>>) { + let ThreadId::User(id) = self.id else { + return; + }; + + struct Name; + + impl StringAttributeOps for Name { + type Data = Weak; + const NAME: &'static str = "name"; + + fn read(state: &Self::Data) -> Result { + let thread = state.upgrade().ok_or(Error::ProcessNotFound)?; + let name = thread.name.read(); + Ok(name.clone()) + } + } + + let object = KObject::new(Arc::downgrade(self)); + + object.add_attribute(StringAttribute::from(Name)).ok(); + + parent.add_object(format!("{id}"), object.clone()).ok(); + *self.sysfs_node.write() = Some(object); + } + + fn drop_sysfs_node(&self) { + if let Some(node) = self.sysfs_node.write().take() { + node.unlink(); + } + } } impl GlobalThreadList { @@ -672,13 +725,7 @@ impl CurrentThread { /// Terminate the current thread pub fn exit(&self, code: ExitCode) -> ! { - // Can detach debugger now - { - let mut debug = self.debug.lock(); - debug.tracer = None; - debug.single_step = false; - self.trace_flags.store(0, Ordering::Release); - } + self.cleanup(); if let Some(process) = self.try_get_process() { process.handle_thread_exit(self.id, code); diff --git a/lib/runtime/src/process/thread.rs b/lib/runtime/src/process/thread.rs index 8f4d49d8..9e3842aa 100644 --- a/lib/runtime/src/process/thread.rs +++ b/lib/runtime/src/process/thread.rs @@ -249,6 +249,7 @@ impl ThreadHandle { } } Err(Error::Interrupted) => continue, + Err(Error::ProcessNotFound) => (), Err(error) => panic!("wait_thread syscall returned error: {error:?}"), } return self.into_result(); diff --git a/userspace/sysutils/src/ps.rs b/userspace/sysutils/src/ps.rs index b47d89e9..bcbce26f 100644 --- a/userspace/sysutils/src/ps.rs +++ b/userspace/sysutils/src/ps.rs @@ -1,14 +1,33 @@ -use std::{fs, io, path::PathBuf, process::ExitCode}; +use std::{ + fs, io, + path::{Path, PathBuf}, + process::ExitCode, +}; + +use clap::Parser; + +#[derive(Debug, Parser)] +struct Args { + #[clap(short, long, help = "Display threads of processes")] + threads: bool, +} + +#[derive(Debug)] +struct ThreadInfo { + tid: u32, + name: String, +} #[derive(Debug)] struct ProcessInfo { pid: u32, name: String, + threads: Vec, } -fn list_processes() -> io::Result> { +fn list_ids>(path: P) -> io::Result> { let mut list = vec![]; - for entry in fs::read_dir("/sys/proc")? { + for entry in fs::read_dir(path)? { let Ok(entry) = entry else { continue; }; @@ -24,33 +43,67 @@ fn list_processes() -> io::Result> { Ok(list) } -fn process_info(pid: u32) -> io::Result { +fn process_info(pid: u32, args: &Args) -> io::Result { let base = PathBuf::from("/sys/proc").join(format!("{pid}")); let name = fs::read_to_string(base.join("name"))?; - Ok(ProcessInfo { pid, name }) + let threads = if args.threads { + threads(base).unwrap_or_default() + } else { + vec![] + }; + Ok(ProcessInfo { pid, name, threads }) } -fn processes() -> io::Result> { - let pids = list_processes()?; +fn thread_info>(pid_path: P, tid: u32) -> io::Result { + let base = pid_path.as_ref().join(format!("{tid}")); + let name = fs::read_to_string(base.join("name"))?; + Ok(ThreadInfo { tid, name }) +} + +fn threads>(pid_path: P) -> io::Result> { + let pid_path = pid_path.as_ref(); + let tids = list_ids(pid_path)?; + let threads = tids + .into_iter() + .filter_map(|tid| thread_info(pid_path, tid).ok()) + .collect(); + Ok(threads) +} + +fn processes(args: &Args) -> io::Result> { + let pids = list_ids("/sys/proc")?; let processes = pids .into_iter() - .filter_map(|pid| process_info(pid).ok()) + .filter_map(|pid| process_info(pid, args).ok()) .collect(); Ok(processes) } -fn run() -> io::Result<()> { - let mut processes = processes()?; +fn run(args: &Args) -> io::Result<()> { + let mut processes = processes(args)?; processes.sort_by(|a, b| Ord::cmp(&a.pid, &b.pid)); - println!("{:>4} NAME", "PID"); - for process in processes { - println!("{:4} {}", process.pid, process.name.trim()); + if args.threads { + println!("{:>4} {:>4} NAME", "PID", "TID"); + for process in processes { + println!("{:4} {:4} {}", process.pid, "", process.name.trim()); + let count = process.threads.len(); + for (i, thread) in process.threads.into_iter().enumerate() { + let ch = if i == count - 1 { '└' } else { '├' }; + println!("{:>4} {:4} {}", ch, thread.tid, thread.name.trim()); + } + } + } else { + println!("{:>4} NAME", "PID"); + for process in processes { + println!("{:4} {}", process.pid, process.name.trim()); + } } Ok(()) } fn main() -> ExitCode { - match run() { + let args = Args::parse(); + match run(&args) { Ok(()) => ExitCode::SUCCESS, Err(error) => { eprintln!("{error}"); diff --git a/userspace/sysutils/src/tst.rs b/userspace/sysutils/src/tst.rs index f77cc528..4712eec5 100644 --- a/userspace/sysutils/src/tst.rs +++ b/userspace/sysutils/src/tst.rs @@ -1,5 +1,23 @@ +use std::{thread, time::Duration}; + fn main() { - for i in 0..8 { - println!("\x1B[3{i}mHello\x1B[1molleH\x1B[0m"); + let mut threads = vec![]; + for i in 0..4 { + let jh = thread::Builder::new() + .name(format!("tst-thread-{i}")) + .spawn(move || { + let current = thread::current(); + for _ in 0..10 { + println!("Hi from thread {:?}", current.name()); + thread::sleep(Duration::from_secs(1)); + } + }) + .unwrap(); + + threads.push(jh); + } + + for thread in threads.into_iter() { + thread.join().unwrap(); } }