use futures::{StreamExt, TryStreamExt}; use heim::process::{self as process, Process, ProcessResult}; use heim::units::{information, ratio, time, Ratio}; use std::usize; use nu_errors::ShellError; use nu_protocol::{TaggedDictBuilder, UntaggedValue, Value}; use nu_source::Tag; use num_bigint::BigInt; use std::time::Duration; #[derive(Default)] pub struct Ps; impl Ps { pub fn new() -> Ps { Ps } } async fn usage(process: Process) -> ProcessResult<(process::Process, Ratio, process::Memory)> { let usage_1 = process.cpu_usage().await?; futures_timer::Delay::new(Duration::from_millis(100)).await; let usage_2 = process.cpu_usage().await?; let memory = process.memory().await?; Ok((process, usage_2 - usage_1, memory)) } pub async fn ps(tag: Tag, long: bool) -> Result, ShellError> { let processes = process::processes() .await .map_err(|_| { ShellError::labeled_error( "Unabled to get process list", "could not load process list", tag.span, ) })? .map_ok(|process| { // Note that there is no `.await` here, // as we want to pass the returned future // into the `.try_buffer_unordered`. usage(process) }) .try_buffer_unordered(usize::MAX); futures::pin_mut!(processes); let mut output = vec![]; while let Some(res) = processes.next().await { if let Ok((process, usage, memory)) = res { let mut dict = TaggedDictBuilder::new(&tag); dict.insert_untagged("pid", UntaggedValue::int(process.pid())); if let Ok(name) = process.name().await { dict.insert_untagged("name", UntaggedValue::string(name)); } if let Ok(status) = process.status().await { dict.insert_untagged("status", UntaggedValue::string(format!("{:?}", status))); } dict.insert_untagged( "cpu", UntaggedValue::decimal_from_float(usage.get::() as f64, tag.span), ); dict.insert_untagged( "mem", UntaggedValue::filesize(memory.rss().get::()), ); dict.insert_untagged( "virtual", UntaggedValue::filesize(memory.vms().get::()), ); if long { if let Ok(cpu_time) = process.cpu_time().await { let user_time = cpu_time.user().get::().round() as i64; let system_time = cpu_time.system().get::().round() as i64; dict.insert_untagged( "cpu_time", UntaggedValue::duration(BigInt::from(user_time + system_time)), ) } if let Ok(parent_pid) = process.parent_pid().await { dict.insert_untagged("parent", UntaggedValue::int(parent_pid)) } if let Ok(exe) = process.exe().await { dict.insert_untagged("exe", UntaggedValue::string(exe.to_string_lossy())) } #[cfg(not(windows))] { if let Ok(command) = process.command().await { dict.insert_untagged( "command", UntaggedValue::string(command.to_os_string().to_string_lossy()), ); } } } output.push(dict.into_value()); } } Ok(output) }