1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-24 13:52:55 +03:00

mux: refactor sysinfo. Use own fn for macos foreground process path

I noticed that sysinfo failed to yield info about 50% of the time on
macos!

Just go direct to the underlying system function; we don't need all
of the info that sysinfo collects in any case.
This commit is contained in:
Wez Furlong 2021-12-31 11:30:10 -07:00
parent 09e8d7b92f
commit 80d16574e3
3 changed files with 131 additions and 108 deletions

View File

@ -29,6 +29,7 @@ pub mod connui;
pub mod domain;
pub mod localpane;
pub mod pane;
pub mod procinfo;
pub mod renderable;
pub mod ssh;
mod sysinfo;

View File

@ -1,5 +1,6 @@
use crate::domain::DomainId;
use crate::pane::{CloseReason, Pane, PaneId, Pattern, SearchResult};
use crate::procinfo::LocalProcessInfo;
use crate::renderable::*;
use crate::tmux::{TmuxDomain, TmuxDomainState};
use crate::{Domain, Mux, MuxNotification};
@ -9,13 +10,11 @@ use config::keyassignment::ScrollbackEraseMode;
use config::{configuration, ExitBehavior};
use portable_pty::{Child, ChildKiller, ExitStatus, MasterPty, PtySize};
use rangeset::RangeSet;
use serde::{Deserialize, Serialize};
use smol::channel::{bounded, Receiver, TryRecvError};
use std::cell::{RefCell, RefMut};
use std::collections::{HashMap, HashSet};
use std::io::Result as IoResult;
use std::ops::Range;
use std::path::PathBuf;
use std::sync::Arc;
use termwiz::escape::DeviceControlMode;
use termwiz::surface::{Line, SequenceNo, SEQ_ZERO};
@ -337,7 +336,27 @@ impl Pane for LocalPane {
None
}
#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
#[cfg(target_os = "macos")]
fn get_foreground_process_name(&self) -> Option<String> {
let leader = self.pty.borrow().process_group_leader()?;
let mut buffer: Vec<u8> = Vec::with_capacity(libc::PROC_PIDPATHINFO_MAXSIZE as _);
let x = unsafe {
libc::proc_pidpath(
leader,
buffer.as_mut_ptr() as *mut _,
libc::PROC_PIDPATHINFO_MAXSIZE as _,
)
};
if x > 0 {
unsafe { buffer.set_len(x as usize) };
String::from_utf8(buffer).ok()
} else {
None
}
}
#[cfg(any(windows, target_os = "linux"))]
fn get_foreground_process_name(&self) -> Option<String> {
#[cfg(unix)]
{
@ -836,111 +855,6 @@ impl LocalPane {
}
}
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub enum LocalProcessStatus {
Idle,
Run,
Sleep,
Stop,
Zombie,
Tracing,
Dead,
Wakekill,
Waking,
Parked,
LockBlocked,
Unknown,
}
#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
impl LocalProcessStatus {
fn from_process_status(status: sysinfo::ProcessStatus) -> Self {
match status {
sysinfo::ProcessStatus::Idle => Self::Idle,
sysinfo::ProcessStatus::Run => Self::Run,
sysinfo::ProcessStatus::Sleep => Self::Sleep,
sysinfo::ProcessStatus::Stop => Self::Stop,
sysinfo::ProcessStatus::Zombie => Self::Zombie,
sysinfo::ProcessStatus::Tracing => Self::Tracing,
sysinfo::ProcessStatus::Dead => Self::Dead,
sysinfo::ProcessStatus::Wakekill => Self::Wakekill,
sysinfo::ProcessStatus::Waking => Self::Waking,
sysinfo::ProcessStatus::Parked => Self::Parked,
sysinfo::ProcessStatus::LockBlocked => Self::LockBlocked,
sysinfo::ProcessStatus::Unknown(_) => Self::Unknown,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct LocalProcessInfo {
pub pid: u32,
pub ppid: u32,
pub name: String,
pub executable: PathBuf,
pub argv: Vec<String>,
pub cwd: PathBuf,
pub status: LocalProcessStatus,
pub children: HashMap<u32, LocalProcessInfo>,
pub start_time: u64,
}
luahelper::impl_lua_conversion!(LocalProcessInfo);
impl LocalProcessInfo {
fn flatten_to_exe_names(&self) -> HashSet<String> {
let mut names = HashSet::new();
fn flatten(item: &LocalProcessInfo, names: &mut HashSet<String>) {
if let Some(exe) = item.executable.file_name() {
names.insert(exe.to_string_lossy().into_owned());
}
for proc in item.children.values() {
flatten(proc, names);
}
}
flatten(self, &mut names);
names
}
}
#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
impl LocalProcessInfo {
fn with_root_pid(system: &sysinfo::System, pid: u32) -> Option<Self> {
use sysinfo::{AsU32, Pid, Process, ProcessExt, SystemExt};
fn build_proc(proc: &Process, processes: &HashMap<Pid, Process>) -> LocalProcessInfo {
// Process has a `tasks` field but it does not correspond to child processes,
// so we need to repeatedly walk the full process list and establish that
// linkage for ourselves here
let mut children = HashMap::new();
let pid = proc.pid();
for (child_pid, child_proc) in processes {
if child_proc.parent() == Some(pid) {
children.insert(child_pid.as_u32(), build_proc(child_proc, processes));
}
}
LocalProcessInfo {
pid: proc.pid().as_u32(),
ppid: proc.parent().map(|pid| pid.as_u32()).unwrap_or(1),
name: proc.name().to_string(),
executable: proc.exe().to_path_buf(),
cwd: proc.cwd().to_path_buf(),
argv: proc.cmd().to_vec(),
start_time: proc.start_time(),
status: LocalProcessStatus::from_process_status(proc.status()),
children,
}
}
let proc = system.process(pid as Pid)?;
let procs = system.processes();
Some(build_proc(&proc, &procs))
}
}
impl Drop for LocalPane {
fn drop(&mut self) {
// Avoid lingering zombies if we can, but don't block forever.

108
mux/src/procinfo.rs Normal file
View File

@ -0,0 +1,108 @@
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub enum LocalProcessStatus {
Idle,
Run,
Sleep,
Stop,
Zombie,
Tracing,
Dead,
Wakekill,
Waking,
Parked,
LockBlocked,
Unknown,
}
#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
impl LocalProcessStatus {
fn from_process_status(status: sysinfo::ProcessStatus) -> Self {
match status {
sysinfo::ProcessStatus::Idle => Self::Idle,
sysinfo::ProcessStatus::Run => Self::Run,
sysinfo::ProcessStatus::Sleep => Self::Sleep,
sysinfo::ProcessStatus::Stop => Self::Stop,
sysinfo::ProcessStatus::Zombie => Self::Zombie,
sysinfo::ProcessStatus::Tracing => Self::Tracing,
sysinfo::ProcessStatus::Dead => Self::Dead,
sysinfo::ProcessStatus::Wakekill => Self::Wakekill,
sysinfo::ProcessStatus::Waking => Self::Waking,
sysinfo::ProcessStatus::Parked => Self::Parked,
sysinfo::ProcessStatus::LockBlocked => Self::LockBlocked,
sysinfo::ProcessStatus::Unknown(_) => Self::Unknown,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct LocalProcessInfo {
pub pid: u32,
pub ppid: u32,
pub name: String,
pub executable: PathBuf,
pub argv: Vec<String>,
pub cwd: PathBuf,
pub status: LocalProcessStatus,
pub children: HashMap<u32, LocalProcessInfo>,
pub start_time: u64,
}
luahelper::impl_lua_conversion!(LocalProcessInfo);
impl LocalProcessInfo {
pub fn flatten_to_exe_names(&self) -> HashSet<String> {
let mut names = HashSet::new();
fn flatten(item: &LocalProcessInfo, names: &mut HashSet<String>) {
if let Some(exe) = item.executable.file_name() {
names.insert(exe.to_string_lossy().into_owned());
}
for proc in item.children.values() {
flatten(proc, names);
}
}
flatten(self, &mut names);
names
}
}
#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
impl LocalProcessInfo {
pub(crate) fn with_root_pid(system: &sysinfo::System, pid: u32) -> Option<Self> {
use sysinfo::{AsU32, Pid, Process, ProcessExt, SystemExt};
fn build_proc(proc: &Process, processes: &HashMap<Pid, Process>) -> LocalProcessInfo {
// Process has a `tasks` field but it does not correspond to child processes,
// so we need to repeatedly walk the full process list and establish that
// linkage for ourselves here
let mut children = HashMap::new();
let pid = proc.pid();
for (child_pid, child_proc) in processes {
if child_proc.parent() == Some(pid) {
children.insert(child_pid.as_u32(), build_proc(child_proc, processes));
}
}
LocalProcessInfo {
pid: proc.pid().as_u32(),
ppid: proc.parent().map(|pid| pid.as_u32()).unwrap_or(1),
name: proc.name().to_string(),
executable: proc.exe().to_path_buf(),
cwd: proc.cwd().to_path_buf(),
argv: proc.cmd().to_vec(),
start_time: proc.start_time(),
status: LocalProcessStatus::from_process_status(proc.status()),
children,
}
}
let proc = system.process(pid as Pid)?;
let procs = system.processes();
Some(build_proc(&proc, &procs))
}
}