1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 13:21:38 +03:00

mux: remove dep on sysinfo; we now do our own win32 stuff

This also removes the sysinfo cache; will replace it with
something else in a follow up commit.
This commit is contained in:
Wez Furlong 2021-12-31 19:20:51 -07:00
parent 2b38c3dd32
commit 2aa491af1a
6 changed files with 400 additions and 134 deletions

17
Cargo.lock generated
View File

@ -2292,6 +2292,7 @@ dependencies = [
"luahelper", "luahelper",
"metrics", "metrics",
"mlua", "mlua",
"ntapi",
"portable-pty", "portable-pty",
"promise", "promise",
"rangeset", "rangeset",
@ -2299,7 +2300,6 @@ dependencies = [
"regex", "regex",
"serde", "serde",
"smol", "smol",
"sysinfo",
"terminfo", "terminfo",
"termwiz", "termwiz",
"textwrap 0.14.2", "textwrap 0.14.2",
@ -3944,21 +3944,6 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "sysinfo"
version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b645b59c59114c25d3d554f781b0a1f1f01545d1d02f271bfb1c897bdfdfdcf3"
dependencies = [
"cfg-if 1.0.0",
"core-foundation-sys 0.8.3",
"libc",
"ntapi",
"once_cell",
"rayon",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "tabout" name = "tabout"
version = "0.3.0" version = "0.3.0"

View File

@ -39,10 +39,13 @@ wezterm-ssh = { path = "../wezterm-ssh" }
wezterm-term = { path = "../term", features=["use_serde"] } wezterm-term = { path = "../term", features=["use_serde"] }
[target."cfg(windows)".dependencies] [target."cfg(windows)".dependencies]
sysinfo = "0.22" ntapi = "0.3"
winapi = { version = "0.3", features = [ winapi = { version = "0.3", features = [
"handleapi", "handleapi",
"processthreadsapi" "memoryapi",
"psapi",
"processthreadsapi",
"tlhelp32",
]} ]}
[dev-dependencies] [dev-dependencies]

View File

@ -32,7 +32,6 @@ pub mod pane;
pub mod procinfo; pub mod procinfo;
pub mod renderable; pub mod renderable;
pub mod ssh; pub mod ssh;
mod sysinfo;
pub mod tab; pub mod tab;
pub mod termwiztermtab; pub mod termwiztermtab;
pub mod tmux; pub mod tmux;

View File

@ -844,6 +844,12 @@ impl LocalPane {
return LocalProcessInfo::with_root_pid_linux(*pid); return LocalProcessInfo::with_root_pid_linux(*pid);
} }
#[cfg(windows)]
if let ProcessState::Running { pid: Some(pid), .. } = &*self.process.borrow() {
return dbg!(LocalProcessInfo::with_root_pid_windows(*pid));
}
/*
#[cfg(windows)] #[cfg(windows)]
if let ProcessState::Running { pid: Some(pid), .. } = &*self.process.borrow() { if let ProcessState::Running { pid: Some(pid), .. } = &*self.process.borrow() {
return LocalProcessInfo::with_root_pid( return LocalProcessInfo::with_root_pid(
@ -855,6 +861,7 @@ impl LocalPane {
*pid, *pid,
); );
} }
*/
None None
} }

View File

@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::path::PathBuf; use std::path::PathBuf;
use std::time::{Duration, SystemTime};
#[derive(Debug, Serialize, Deserialize, Copy, Clone)] #[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub enum LocalProcessStatus { pub enum LocalProcessStatus {
@ -18,26 +19,6 @@ pub enum LocalProcessStatus {
Unknown, Unknown,
} }
#[cfg(windows)]
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,
}
}
}
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
impl From<&str> for LocalProcessStatus { impl From<&str> for LocalProcessStatus {
fn from(s: &str) -> Self { fn from(s: &str) -> Self {
@ -81,7 +62,7 @@ pub struct LocalProcessInfo {
pub cwd: PathBuf, pub cwd: PathBuf,
pub status: LocalProcessStatus, pub status: LocalProcessStatus,
pub children: HashMap<u32, LocalProcessInfo>, pub children: HashMap<u32, LocalProcessInfo>,
pub start_time: u64, pub start_time: SystemTime,
} }
luahelper::impl_lua_conversion!(LocalProcessInfo); luahelper::impl_lua_conversion!(LocalProcessInfo);
@ -160,6 +141,7 @@ impl LocalProcessInfo {
fn cwd_for_pid(pid: pid_t) -> PathBuf { fn cwd_for_pid(pid: pid_t) -> PathBuf {
std::fs::read_link(format!("/proc/{}/cwd", pid)).unwrap_or_else(|_| PathBuf::new()) std::fs::read_link(format!("/proc/{}/cwd", pid)).unwrap_or_else(|_| PathBuf::new())
} }
fn parse_cmdline(pid: pid_t) -> Vec<String> { fn parse_cmdline(pid: pid_t) -> Vec<String> {
let data = match std::fs::read(format!("/proc/{}/cmdline", pid)) { let data = match std::fs::read(format!("/proc/{}/cmdline", pid)) {
Ok(data) => data, Ok(data) => data,
@ -213,6 +195,390 @@ impl LocalProcessInfo {
} }
} }
#[cfg(windows)]
impl LocalProcessInfo {
pub(crate) fn with_root_pid_windows(pid: u32) -> Option<Self> {
use ntapi::ntpebteb::PEB;
use ntapi::ntpsapi::{
NtQueryInformationProcess, ProcessBasicInformation, ProcessWow64Information,
PROCESS_BASIC_INFORMATION,
};
use ntapi::ntrtl::RTL_USER_PROCESS_PARAMETERS;
use ntapi::ntwow64::RTL_USER_PROCESS_PARAMETERS32;
use std::ffi::OsString;
use std::mem::MaybeUninit;
use std::os::windows::ffi::OsStringExt;
use winapi::shared::minwindef::{FILETIME, HMODULE, LPVOID, MAX_PATH};
use winapi::shared::ntdef::{FALSE, NT_SUCCESS};
use winapi::um::handleapi::CloseHandle;
use winapi::um::memoryapi::ReadProcessMemory;
use winapi::um::processthreadsapi::{GetProcessTimes, OpenProcess};
use winapi::um::psapi::{EnumProcessModulesEx, GetModuleFileNameExW, LIST_MODULES_ALL};
use winapi::um::shellapi::CommandLineToArgvW;
use winapi::um::tlhelp32::*;
use winapi::um::winbase::LocalFree;
use winapi::um::winnt::{HANDLE, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ};
struct Snapshot(HANDLE);
impl Snapshot {
pub fn new() -> Option<Self> {
let handle = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) };
if handle.is_null() {
None
} else {
Some(Self(handle))
}
}
pub fn iter(&self) -> ProcIter {
ProcIter {
snapshot: &self,
first: true,
}
}
}
impl Drop for Snapshot {
fn drop(&mut self) {
unsafe { CloseHandle(self.0) };
}
}
struct ProcIter<'a> {
snapshot: &'a Snapshot,
first: bool,
}
impl<'a> Iterator for ProcIter<'a> {
type Item = PROCESSENTRY32W;
fn next(&mut self) -> Option<Self::Item> {
let mut entry: PROCESSENTRY32W = unsafe { std::mem::zeroed() };
entry.dwSize = std::mem::size_of::<PROCESSENTRY32W>() as _;
let res = if self.first {
self.first = false;
unsafe { Process32FirstW(self.snapshot.0, &mut entry) }
} else {
unsafe { Process32NextW(self.snapshot.0, &mut entry) }
};
if res == 0 {
None
} else {
Some(entry)
}
}
}
let snapshot = Snapshot::new()?;
let procs: Vec<_> = snapshot.iter().collect();
fn wstr_to_path(slice: &[u16]) -> PathBuf {
match slice.iter().position(|&c| c == 0) {
Some(nul) => OsString::from_wide(&slice[..nul]),
None => OsString::from_wide(slice),
}
.into()
}
fn wstr_to_string(slice: &[u16]) -> String {
wstr_to_path(slice).to_string_lossy().into_owned()
}
struct ProcParams {
argv: Vec<String>,
cwd: PathBuf,
}
struct ProcHandle(HANDLE);
impl ProcHandle {
fn new(pid: u32) -> Option<Self> {
let options = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
let handle = unsafe { OpenProcess(options, FALSE as _, pid) };
if handle.is_null() {
return None;
}
Some(Self(handle))
}
fn hmodule(&self) -> Option<HMODULE> {
let mut needed = 0;
let mut hmod = [0 as HMODULE];
let size = std::mem::size_of_val(&hmod);
let res = unsafe {
EnumProcessModulesEx(
self.0,
hmod.as_mut_ptr(),
size as _,
&mut needed,
LIST_MODULES_ALL,
)
};
if res == 0 {
None
} else {
Some(hmod[0])
}
}
fn executable(&self) -> Option<PathBuf> {
let hmod = self.hmodule()?;
let mut buf = [0u16; MAX_PATH + 1];
let res =
unsafe { GetModuleFileNameExW(self.0, hmod, buf.as_mut_ptr(), buf.len() as _) };
if res == 0 {
None
} else {
Some(wstr_to_path(&buf))
}
}
fn get_peb32_addr(&self) -> Option<LPVOID> {
let mut peb32_addr = MaybeUninit::<LPVOID>::uninit();
let res = unsafe {
NtQueryInformationProcess(
self.0,
ProcessWow64Information,
peb32_addr.as_mut_ptr() as _,
std::mem::size_of::<LPVOID>() as _,
std::ptr::null_mut(),
)
};
if !NT_SUCCESS(res) {
return None;
}
let peb32_addr = unsafe { peb32_addr.assume_init() };
if peb32_addr.is_null() {
None
} else {
Some(peb32_addr)
}
}
fn get_params(&self) -> Option<ProcParams> {
match self.get_peb32_addr() {
Some(peb32) => self.get_params_32(peb32),
None => self.get_params_64(),
}
}
fn get_basic_info(&self) -> Option<PROCESS_BASIC_INFORMATION> {
let mut info = MaybeUninit::<PROCESS_BASIC_INFORMATION>::uninit();
let res = unsafe {
NtQueryInformationProcess(
self.0,
ProcessBasicInformation,
info.as_mut_ptr() as _,
std::mem::size_of::<PROCESS_BASIC_INFORMATION>() as _,
std::ptr::null_mut(),
)
};
if !NT_SUCCESS(res) {
return None;
}
let info = unsafe { info.assume_init() };
Some(info)
}
fn read_struct<T>(&self, addr: LPVOID) -> Option<T> {
let mut data = MaybeUninit::<T>::uninit();
let res = unsafe {
ReadProcessMemory(
self.0,
addr as _,
data.as_mut_ptr() as _,
std::mem::size_of::<T>() as _,
std::ptr::null_mut(),
)
};
if res == 0 {
return None;
}
let data = unsafe { data.assume_init() };
Some(data)
}
fn get_peb(&self, info: &PROCESS_BASIC_INFORMATION) -> Option<PEB> {
self.read_struct(info.PebBaseAddress as _)
}
fn get_proc_params(&self, peb: &PEB) -> Option<RTL_USER_PROCESS_PARAMETERS> {
self.read_struct(peb.ProcessParameters as _)
}
fn get_params_64(&self) -> Option<ProcParams> {
let info = self.get_basic_info()?;
let peb = self.get_peb(&info)?;
let params = self.get_proc_params(&peb)?;
let cmdline = self.read_process_wchar(
params.CommandLine.Buffer as _,
params.CommandLine.Length as _,
)?;
let cwd = self.read_process_wchar(
params.CurrentDirectory.DosPath.Buffer as _,
params.CurrentDirectory.DosPath.Length as _,
)?;
Some(ProcParams {
argv: cmd_line_to_argv(&cmdline),
cwd: wstr_to_path(&cwd),
})
}
fn get_proc_params_32(&self, peb32: LPVOID) -> Option<RTL_USER_PROCESS_PARAMETERS32> {
self.read_struct(peb32)
}
fn get_params_32(&self, peb32: LPVOID) -> Option<ProcParams> {
let params = self.get_proc_params_32(peb32)?;
let cmdline = self.read_process_wchar(
params.CommandLine.Buffer as _,
params.CommandLine.Length as _,
)?;
let cwd = self.read_process_wchar(
params.CurrentDirectory.DosPath.Buffer as _,
params.CurrentDirectory.DosPath.Length as _,
)?;
Some(ProcParams {
argv: cmd_line_to_argv(&cmdline),
cwd: wstr_to_path(&cwd),
})
}
fn read_process_wchar(&self, ptr: LPVOID, size: usize) -> Option<Vec<u16>> {
let mut buf = vec![0u16; size / 2];
let res = unsafe {
ReadProcessMemory(
self.0,
ptr as _,
buf.as_mut_ptr() as _,
size,
std::ptr::null_mut(),
)
};
if res == 0 {
return None;
}
Some(buf)
}
fn start_time(&self) -> Option<SystemTime> {
let mut start = FILETIME {
dwLowDateTime: 0,
dwHighDateTime: 0,
};
let mut exit = FILETIME {
dwLowDateTime: 0,
dwHighDateTime: 0,
};
let mut kernel = FILETIME {
dwLowDateTime: 0,
dwHighDateTime: 0,
};
let mut user = FILETIME {
dwLowDateTime: 0,
dwHighDateTime: 0,
};
let res = unsafe {
GetProcessTimes(self.0, &mut start, &mut exit, &mut kernel, &mut user)
};
if res == 0 {
return None;
}
// Units are 100 nanoseconds
let start = (start.dwHighDateTime as u64) << 32 | start.dwLowDateTime as u64;
let start = Duration::from_nanos(start * 100);
// Difference between the windows epoch and the unix epoch
const WINDOWS_EPOCH: Duration = Duration::from_secs(11_644_473_600);
Some(SystemTime::UNIX_EPOCH + start - WINDOWS_EPOCH)
}
}
fn cmd_line_to_argv(buf: &[u16]) -> Vec<String> {
let mut argc = 0;
let argvp = unsafe { CommandLineToArgvW(buf.as_ptr(), &mut argc) };
if argvp.is_null() {
return vec![];
}
let argv = unsafe { std::slice::from_raw_parts(argvp, argc as usize) };
let mut args = vec![];
for &arg in argv {
let len = unsafe { libc::wcslen(arg) };
let arg = unsafe { std::slice::from_raw_parts(arg, len) };
args.push(wstr_to_string(arg));
}
unsafe { LocalFree(argvp as _) };
args
}
impl Drop for ProcHandle {
fn drop(&mut self) {
unsafe { CloseHandle(self.0) };
}
}
fn build_proc(info: &PROCESSENTRY32W, procs: &[PROCESSENTRY32W]) -> LocalProcessInfo {
let mut children = HashMap::new();
for kid in procs {
if kid.th32ParentProcessID == info.th32ProcessID {
children.insert(kid.th32ProcessID, build_proc(kid, procs));
}
}
let mut executable = wstr_to_path(&info.szExeFile);
let name = match executable.file_name() {
Some(name) => name.to_string_lossy().into_owned(),
None => String::new(),
};
let mut start_time = SystemTime::now();
let mut cwd = PathBuf::new();
let mut argv = vec![];
if let Some(proc) = ProcHandle::new(info.th32ProcessID) {
if let Some(exe) = proc.executable() {
executable = exe;
}
if let Some(params) = proc.get_params() {
cwd = params.cwd;
argv = params.argv;
}
if let Some(start) = proc.start_time() {
start_time = start;
}
}
LocalProcessInfo {
pid: info.th32ProcessID,
ppid: info.th32ParentProcessID,
name,
executable,
cwd,
argv,
start_time,
status: LocalProcessStatus::Run,
children,
}
}
if let Some(info) = procs.iter().find(|info| info.th32ProcessID == pid) {
Some(build_proc(info, &procs))
} else {
None
}
}
}
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
impl LocalProcessInfo { impl LocalProcessInfo {
pub(crate) fn with_root_pid_macos(pid: u32) -> Option<Self> { pub(crate) fn with_root_pid_macos(pid: u32) -> Option<Self> {
@ -410,40 +776,3 @@ impl LocalProcessInfo {
} }
} }
} }
#[cfg(windows)]
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))
}
}

View File

@ -1,57 +0,0 @@
#![cfg(windows)]
use std::sync::{Mutex, MutexGuard};
use std::time::{Duration, Instant};
use sysinfo::{ProcessRefreshKind, RefreshKind, System, SystemExt};
lazy_static::lazy_static! {
static ref SYSTEM: Mutex<CachedSystemInfo> = Mutex::new(CachedSystemInfo::new());
}
pub struct CachedSystemInfo {
last_update: Instant,
system: sysinfo::System,
}
impl std::ops::Deref for CachedSystemInfo {
type Target = sysinfo::System;
fn deref(&self) -> &sysinfo::System {
&self.system
}
}
impl CachedSystemInfo {
pub fn new() -> Self {
Self {
system: System::new_with_specifics(
RefreshKind::new().with_processes(ProcessRefreshKind::new()),
),
last_update: Instant::now(),
}
}
pub fn refresh_now(&mut self) {
self.system
.refresh_processes_specifics(ProcessRefreshKind::new());
self.last_update = Instant::now();
}
pub fn check_refresh(&mut self) {
if self.last_update.elapsed() < Duration::from_millis(300) {
return;
}
self.refresh_now();
}
}
pub fn get() -> MutexGuard<'static, CachedSystemInfo> {
let mut guard = SYSTEM.lock().unwrap();
guard.check_refresh();
guard
}
pub fn get_with_forced_refresh() -> MutexGuard<'static, CachedSystemInfo> {
let mut guard = SYSTEM.lock().unwrap();
guard.refresh_now();
guard
}