Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* [Added] Env column for Windows (environment variables)
* [Added] RecvBytes and SendBytes columns for Windows (network I/O rate; needs Windows 11 or later)
* [Added] WorkDir column for Windows (current working directory)
* [Added] WorkDir column for macOS (current working directory)
* [Added] `--thread` support for Windows
* [Changed] Group and Gid columns for Windows read the process token only when the column is displayed
* [Fixed] ReadBytes / WriteBytes divided by a mis-scaled interval (seconds added to milliseconds)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ The first `[[columns]]` is shown at left side, and the last is shown at right si
| VmTotal | -not supported- | Total virtual memory size | *[^*] | o | *[^*] | *[^*] |
| VoluntaryContextSw | -not supported- | Voluntary context switch count | o | | | o |
| Wchan | wchan | Process sleeping kernel function | o | | | o |
| WorkDir | -not supported- | Current working directory | o | | o | |
| WorkDir | -not supported- | Current working directory | o | o | o | |
| WriteByte | -not supported- | Write bytes to storage | o | o | o | o |

[^*]: Alias for VmRss on these platforms
Expand Down
13 changes: 12 additions & 1 deletion src/columns/os_macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub mod user_saved;
pub mod vm_rss;
pub mod vm_size;
pub mod vm_total;
pub mod work_dir;
pub mod write_bytes;

pub use self::arch::Arch;
Expand Down Expand Up @@ -92,6 +93,7 @@ pub use self::user_saved::UserSaved;
pub use self::vm_rss::VmRss;
pub use self::vm_size::VmSize;
pub use self::vm_total::VmTotal;
pub use self::work_dir::WorkDir;
pub use self::write_bytes::WriteBytes;

use crate::column::Column;
Expand Down Expand Up @@ -152,6 +154,7 @@ pub enum ConfigColumnKind {
VmRss,
VmSize,
VmTotal,
WorkDir,
WriteBytes,
}

Expand All @@ -166,7 +169,7 @@ pub fn gen_column(
separator: &str,
abbr_sid: bool,
tree_symbols: &[String; 5],
_procfs: Option<PathBuf>,
procfs: Option<PathBuf>,
) -> Box<dyn Column> {
match kind {
ConfigColumnKind::Arch => Box::new(Arch::new(header)),
Expand Down Expand Up @@ -218,6 +221,7 @@ pub fn gen_column(
ConfigColumnKind::VmRss => Box::new(VmRss::new(header)),
ConfigColumnKind::VmSize => Box::new(VmSize::new(header)),
ConfigColumnKind::VmTotal => Box::new(VmTotal::new(header)),
ConfigColumnKind::WorkDir => Box::new(WorkDir::new(header, procfs)),
ConfigColumnKind::WriteBytes => Box::new(WriteBytes::new(header)),
}
}
Expand Down Expand Up @@ -323,6 +327,10 @@ pub static KIND_LIST: Lazy<BTreeMap<ConfigColumnKind, (&'static str, &'static st
ConfigColumnKind::VmTotal,
("VmTotal", "Total footprint size"),
),
(
ConfigColumnKind::WorkDir,
("WorkDir", "Current working directory"),
),
(
ConfigColumnKind::WriteBytes,
("WriteBytes", "Write bytes to storage"),
Expand Down Expand Up @@ -661,6 +669,9 @@ style = "ByUnit"
kind = "VmSize"
style = "ByUnit"
[[columns]]
kind = "WorkDir"
style = "White"
[[columns]]
kind = "WriteBytes"
style = "White"
"#;
48 changes: 28 additions & 20 deletions src/columns/work_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use std::cmp;
use std::collections::HashMap;
use std::path::PathBuf;

#[cfg(target_os = "macos")]
use libproc::libproc::proc_pid::{PIDInfo, PidInfoFlavor, pidinfo};
#[cfg(target_os = "windows")]
use windows_sys::Win32::Foundation::HANDLE;

Expand All @@ -13,7 +15,6 @@ pub struct WorkDir {
fmt_contents: HashMap<i32, String>,
raw_contents: HashMap<i32, String>,
width: usize,
#[allow(dead_code)]
procfs: Option<PathBuf>,
}

Expand All @@ -32,18 +33,9 @@ impl WorkDir {
}
}

#[cfg(any(target_os = "linux", target_os = "android"))]
impl Column for WorkDir {
fn add(&mut self, proc: &ProcessInfo) {
let fmt_content = if let Ok(proc) = crate::util::process_new(proc.pid, &self.procfs) {
if let Ok(path) = proc.cwd() {
path.to_string_lossy().to_string()
} else {
String::from("")
}
} else {
String::from("")
};
let fmt_content = work_dir_of(proc.pid, &self.procfs).unwrap_or_default();
let raw_content = fmt_content.clone();

self.fmt_contents.insert(proc.pid, fmt_content);
Expand All @@ -53,17 +45,33 @@ impl Column for WorkDir {
column_default!(String, false);
}

#[cfg(target_os = "windows")]
impl Column for WorkDir {
fn add(&mut self, proc: &ProcessInfo) {
let fmt_content = work_dir_of(proc.pid).unwrap_or_default();
let raw_content = fmt_content.clone();
#[cfg(any(target_os = "linux", target_os = "android"))]
fn work_dir_of(pid: i32, procfs: &Option<PathBuf>) -> Option<String> {
let proc = crate::util::process_new(pid, procfs).ok()?;
Some(proc.cwd().ok()?.to_string_lossy().into_owned())
}

self.fmt_contents.insert(proc.pid, fmt_content);
self.raw_contents.insert(proc.pid, raw_content);
/// libproc's `pidinfo` passes `&mut T` to `proc_pidinfo` sized by
/// `size_of::<T>()`, so this must keep the exact layout of
/// `proc_vnodepathinfo`.
#[cfg(target_os = "macos")]
#[repr(transparent)]
struct VnodePathInfo(libc::proc_vnodepathinfo);

#[cfg(target_os = "macos")]
impl PIDInfo for VnodePathInfo {
fn flavor() -> PidInfoFlavor {
PidInfoFlavor::VNodePathInfo
}
}

column_default!(String, false);
/// Processes owned by other users yield `None` unless running as root.
#[cfg(target_os = "macos")]
fn work_dir_of(pid: i32, _procfs: &Option<PathBuf>) -> Option<String> {
let info = pidinfo::<VnodePathInfo>(pid, 0).ok()?;
// libc declares `vip_path` as `[[c_char; 32]; 32]`, not `[c_char; MAXPATHLEN]`.
let path = crate::util::ptr_to_cstr(info.0.pvi_cdir.vip_path.as_flattened()).ok()?;
Some(path.to_string_lossy().into_owned())
}

/// Reads the current working directory of `pid` from its PEB.
Expand All @@ -74,7 +82,7 @@ impl Column for WorkDir {
/// `PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ`, so protected
/// processes (e.g. PPL) yield `None`.
#[cfg(target_os = "windows")]
fn work_dir_of(pid: i32) -> Option<String> {
fn work_dir_of(pid: i32, _procfs: &Option<PathBuf>) -> Option<String> {
use windows_sys::Win32::Foundation::{CloseHandle, FALSE, HANDLE};
use windows_sys::Win32::System::Threading::{
OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ,
Expand Down
4 changes: 1 addition & 3 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,7 @@ thread_local! {
pub static USERS_CACHE: std::cell::RefCell<UsersCache> = UsersCache::new().into();
}

#[cfg(target_os = "freebsd")]
// std::ffi::FromBytesUntilNulError is missing until Rust 1.73.0
// https://github.com/rust-lang/rust/pull/113701
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
pub fn ptr_to_cstr(
x: &[std::os::raw::c_char],
) -> Result<&std::ffi::CStr, core::ffi::FromBytesUntilNulError> {
Expand Down