diff --git a/Cargo.lock b/Cargo.lock index 83ed064e60..bc58b8e48f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5368,9 +5368,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libgit2-sys" @@ -7170,17 +7170,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "procinfo" -version = "0.1.0" -source = "git+https://github.com/zed-industries/wezterm?rev=0c13436f4fa8b126f46dd4a20106419b41666897#0c13436f4fa8b126f46dd4a20106419b41666897" -dependencies = [ - "libc", - "log", - "ntapi", - "winapi 0.3.9", -] - [[package]] name = "profiling" version = "1.0.15" @@ -9609,9 +9598,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.29.10" +version = "0.30.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a18d114d420ada3a891e6bc8e96a2023402203296a47cdd65083377dad18ba5" +checksum = "0c385888ef380a852a16209afc8cfad22795dd8873d69c9a14d2e2088f118d18" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys 0.8.6", @@ -9619,7 +9608,7 @@ dependencies = [ "ntapi", "once_cell", "rayon", - "winapi 0.3.9", + "windows 0.52.0", ] [[package]] @@ -9756,7 +9745,6 @@ dependencies = [ "futures 0.3.28", "gpui", "libc", - "procinfo", "rand 0.8.5", "schemars", "serde", @@ -9764,6 +9752,7 @@ dependencies = [ "serde_json", "settings", "smol", + "sysinfo", "task", "theme", "thiserror", @@ -11998,18 +11987,37 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets 0.52.4", +] + [[package]] name = "windows" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538" dependencies = [ - "windows-core", + "windows-core 0.53.0", "windows-implement", "windows-interface", "windows-targets 0.52.4", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + [[package]] name = "windows-core" version = "0.53.0" diff --git a/Cargo.toml b/Cargo.toml index 6ad0abd85b..307fafc3bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -259,7 +259,7 @@ smallvec = { version = "1.6", features = ["union"] } smol = "1.2" strum = { version = "0.25.0", features = ["derive"] } subtle = "2.5.0" -sysinfo = "0.29.10" +sysinfo = "0.30.7" tempfile = "3.9.0" thiserror = "1.0.29" tiktoken-rs = "0.5.7" diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 475da1658c..d769bcaa5c 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -12,9 +12,7 @@ use settings::{Settings, SettingsStore}; use sha2::{Digest, Sha256}; use std::io::Write; use std::{env, mem, path::PathBuf, sync::Arc, time::Duration}; -use sysinfo::{ - CpuRefreshKind, Pid, PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt, -}; +use sysinfo::{CpuRefreshKind, MemoryRefreshKind, Pid, ProcessRefreshKind, RefreshKind, System}; use telemetry_events::{ ActionEvent, AppEvent, AssistantEvent, AssistantKind, CallEvent, CopilotEvent, CpuEvent, EditEvent, EditorEvent, Event, EventRequestBody, EventWrapper, MemoryEvent, SettingEvent, @@ -175,7 +173,7 @@ impl Telemetry { cx.spawn(|_| async move { // Avoiding calling `System::new_all()`, as there have been crashes related to it let refresh_kind = RefreshKind::new() - .with_memory() // For memory usage + .with_memory(MemoryRefreshKind::everything()) // For memory usage .with_processes(ProcessRefreshKind::everything()) // For process usage .with_cpu(CpuRefreshKind::everything()); // For core count diff --git a/crates/feedback/src/system_specs.rs b/crates/feedback/src/system_specs.rs index 2e4535d189..e1cbdcbad6 100644 --- a/crates/feedback/src/system_specs.rs +++ b/crates/feedback/src/system_specs.rs @@ -3,7 +3,7 @@ use human_bytes::human_bytes; use release_channel::{AppVersion, ReleaseChannel}; use serde::Serialize; use std::{env, fmt::Display}; -use sysinfo::{RefreshKind, System, SystemExt}; +use sysinfo::{MemoryRefreshKind, RefreshKind, System}; #[derive(Clone, Debug, Serialize)] pub struct SystemSpecs { @@ -20,7 +20,9 @@ impl SystemSpecs { let app_version = AppVersion::global(cx).to_string(); let release_channel = ReleaseChannel::global(cx).display_name(); let os_name = cx.app_metadata().os_name; - let system = System::new_with_specifics(RefreshKind::new().with_memory()); + let system = System::new_with_specifics( + RefreshKind::new().with_memory(MemoryRefreshKind::everything()), + ); let memory = system.total_memory(); let architecture = env::consts::ARCH; let os_version = cx diff --git a/crates/terminal/Cargo.toml b/crates/terminal/Cargo.toml index 61a5fd8929..0bba4f3458 100644 --- a/crates/terminal/Cargo.toml +++ b/crates/terminal/Cargo.toml @@ -22,13 +22,13 @@ dirs = "4.0.0" futures.workspace = true gpui.workspace = true libc = "0.2" -procinfo = { git = "https://github.com/zed-industries/wezterm", rev = "0c13436f4fa8b126f46dd4a20106419b41666897", default-features = false } task.workspace = true schemars.workspace = true serde.workspace = true serde_derive.workspace = true serde_json.workspace = true settings.workspace = true +sysinfo.workspace = true smol.workspace = true theme.workspace = true thiserror.workspace = true diff --git a/crates/terminal/src/pty_info.rs b/crates/terminal/src/pty_info.rs new file mode 100644 index 0000000000..8de4067ca7 --- /dev/null +++ b/crates/terminal/src/pty_info.rs @@ -0,0 +1,134 @@ +use alacritty_terminal::tty::Pty; +#[cfg(target_os = "windows")] +use std::num::NonZeroU32; +#[cfg(unix)] +use std::os::fd::AsRawFd; +use std::path::PathBuf; + +#[cfg(target_os = "windows")] +use windows::Win32::{Foundation::HANDLE, System::Threading::GetProcessId}; + +use sysinfo::{Pid, Process, ProcessRefreshKind, RefreshKind, System, UpdateKind}; + +struct ProcessIdGetter { + handle: i32, + fallback_pid: u32, +} + +#[cfg(unix)] +impl ProcessIdGetter { + fn new(pty: &Pty) -> ProcessIdGetter { + ProcessIdGetter { + handle: pty.file().as_raw_fd(), + fallback_pid: pty.child().id(), + } + } + + fn pid(&self) -> Option { + let pid = unsafe { libc::tcgetpgrp(self.handle) }; + if pid < 0 { + return Some(Pid::from_u32(self.fallback_pid)); + } + Some(Pid::from_u32(pid as u32)) + } +} + +#[cfg(windows)] +impl ProcessIdGetter { + fn new(pty: &Pty) -> ProcessIdGetter { + let child = pty.child_watcher(); + let handle = child.raw_handle(); + let fallback_pid = child + .pid() + .unwrap_or_else(|| unsafe { NonZeroU32::new_unchecked(GetProcessId(HANDLE(handle))) }); + + ProcessIdGetter { + handle: handle as i32, + fallback_pid: u32::from(fallback_pid), + } + } + + fn pid(&self) -> Option { + let pid = unsafe { GetProcessId(HANDLE(self.handle as _)) }; + // the GetProcessId may fail and returns zero, which will lead to a stack overflow issue + if pid == 0 { + // in the builder process, there is a small chance, almost negligible, + // that this value could be zero, which means child_watcher returns None, + // GetProcessId returns 0. + if self.fallback_pid == 0 { + return None; + } + return Some(Pid::from_u32(self.fallback_pid)); + } + Some(Pid::from_u32(pid as u32)) + } +} + +#[derive(Clone, Debug)] +pub struct ProcessInfo { + pub name: String, + pub cwd: PathBuf, + pub argv: Vec, +} + +/// Fetches Zed-relevant Pseudo-Terminal (PTY) process information +pub struct PtyProcessInfo { + system: System, + refresh_kind: ProcessRefreshKind, + pid_getter: ProcessIdGetter, + pub current: Option, +} + +impl PtyProcessInfo { + pub fn new(pty: &Pty) -> PtyProcessInfo { + let process_refresh_kind = ProcessRefreshKind::new() + .with_cmd(UpdateKind::Always) + .with_cwd(UpdateKind::Always) + .with_exe(UpdateKind::Always); + let refresh_kind = RefreshKind::new().with_processes(process_refresh_kind); + let system = System::new_with_specifics(refresh_kind); + + PtyProcessInfo { + system, + refresh_kind: process_refresh_kind, + pid_getter: ProcessIdGetter::new(pty), + current: None, + } + } + + fn refresh(&mut self) -> Option<&Process> { + let pid = self.pid_getter.pid()?; + self.system.refresh_processes_specifics(self.refresh_kind); + self.system.process(pid) + } + + fn load(&mut self) -> Option { + let process = self.refresh()?; + let cwd = process + .cwd() + .take() + .map_or(PathBuf::new(), |p| p.to_owned()); + + let info = ProcessInfo { + name: process.name().to_owned(), + cwd, + argv: process.cmd().to_vec(), + }; + self.current = Some(info.clone()); + Some(info) + } + + /// Updates the cached process info, returns whether the Zed-relevant info has changed + pub fn has_changed(&mut self) -> bool { + let current = self.load(); + let has_changed = match (self.current.as_ref(), current.as_ref()) { + (None, None) => false, + (Some(prev), Some(now)) => prev.cwd != now.cwd || prev.name != now.name, + _ => true, + }; + if has_changed { + self.current = current; + } + has_changed + } +} diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index c2c9f92eee..95d9a33265 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -2,6 +2,7 @@ pub mod mappings; pub use alacritty_terminal; +mod pty_info; pub mod terminal_settings; use alacritty_terminal::{ @@ -34,18 +35,14 @@ use mappings::mouse::{ use collections::{HashMap, VecDeque}; use futures::StreamExt; -use procinfo::LocalProcessInfo; +use pty_info::PtyProcessInfo; use serde::{Deserialize, Serialize}; use settings::Settings; use smol::channel::{Receiver, Sender}; -#[cfg(target_os = "windows")] -use std::num::NonZeroU32; use task::TaskId; use terminal_settings::{AlternateScroll, Shell, TerminalBlink, TerminalSettings}; use theme::{ActiveTheme, Theme}; use util::truncate_and_trailoff; -#[cfg(target_os = "windows")] -use windows::Win32::{Foundation::HANDLE, System::Threading::GetProcessId}; use std::{ cmp::{self, min}, @@ -57,9 +54,6 @@ use std::{ }; use thiserror::Error; -#[cfg(unix)] -use std::os::unix::prelude::AsRawFd; - use gpui::{ actions, black, px, AnyWindowHandle, AppContext, Bounds, ClipboardItem, EventEmitter, Hsla, Keystroke, ModelContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, @@ -401,19 +395,7 @@ impl TerminalBuilder { } }; - #[cfg(unix)] - let (fd, shell_pid) = (pty.file().as_raw_fd(), pty.child().id()); - - // todo("windows") - #[cfg(windows)] - let (fd, shell_pid) = { - let child = pty.child_watcher(); - let handle = child.raw_handle(); - let pid = child.pid().unwrap_or_else(|| unsafe { - NonZeroU32::new_unchecked(GetProcessId(HANDLE(handle))) - }); - (handle, u32::from(pid)) - }; + let pty_info = PtyProcessInfo::new(&pty); //And connect them together let event_loop = EventLoop::new( @@ -441,9 +423,7 @@ impl TerminalBuilder { last_mouse: None, matches: Vec::new(), selection_head: None, - shell_fd: fd as u32, - shell_pid, - foreground_process_info: None, + pty_info, breadcrumb_text: String::new(), scroll_px: px(0.), last_mouse_position: None, @@ -598,9 +578,7 @@ pub struct Terminal { pub last_content: TerminalContent, pub selection_head: Option, pub breadcrumb_text: String, - shell_pid: u32, - shell_fd: u32, - pub foreground_process_info: Option, + pub pty_info: PtyProcessInfo, scroll_px: Pixels, next_link_id: usize, selection_phase: SelectionPhase, @@ -660,7 +638,7 @@ impl Terminal { AlacTermEvent::Wakeup => { cx.emit(Event::Wakeup); - if self.update_process_info() { + if self.pty_info.has_changed() { cx.emit(Event::TitleChanged); } } @@ -675,56 +653,8 @@ impl Terminal { self.selection_phase == SelectionPhase::Selecting } - /// Updates the cached process info, returns whether the Zed-relevant info has changed - fn update_process_info(&mut self) -> bool { - #[cfg(unix)] - let pid = { - let ret = unsafe { libc::tcgetpgrp(self.shell_fd as i32) }; - if ret < 0 { - self.shell_pid as i32 - } else { - ret - } - }; - - #[cfg(windows)] - let pid = { - let ret = unsafe { GetProcessId(HANDLE(self.shell_fd as _)) }; - // the GetProcessId may fail and returns zero, which will lead to a stack overflow issue - if ret == 0 { - // in the builder process, there is a small chance, almost negligible, - // that this value could be zero, which means child_watcher returns None, - // GetProcessId returns 0. - if self.shell_pid == 0 { - return false; - } - self.shell_pid - } else { - ret - } - } as i32; - - if let Some(process_info) = LocalProcessInfo::with_root_pid(pid as u32) { - let res = self - .foreground_process_info - .as_ref() - .map(|old_info| { - process_info.cwd != old_info.cwd || process_info.name != old_info.name - }) - .unwrap_or(true); - - self.foreground_process_info = Some(process_info.clone()); - - res - } else { - false - } - } - - fn get_cwd(&self) -> Option { - self.foreground_process_info - .as_ref() - .map(|info| info.cwd.clone()) + pub fn get_cwd(&self) -> Option { + self.pty_info.current.as_ref().map(|info| info.cwd.clone()) } ///Takes events from Alacritty and translates them to behavior on this view @@ -1402,7 +1332,8 @@ impl Terminal { } } None => self - .foreground_process_info + .pty_info + .current .as_ref() .map(|fpi| { let process_file = fpi @@ -1410,11 +1341,13 @@ impl Terminal { .file_name() .map(|name| name.to_string_lossy().to_string()) .unwrap_or_default(); + + let argv = fpi.argv.clone(); let process_name = format!( "{}{}", fpi.name, - if fpi.argv.len() >= 1 { - format!(" {}", (fpi.argv[1..]).join(" ")) + if argv.len() >= 1 { + format!(" {}", (argv[1..]).join(" ")) } else { "".to_string() } diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 53978b33bb..fede824f89 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -455,9 +455,7 @@ fn subscribe_for_terminal_events( cx.emit(ItemEvent::UpdateTab); let terminal = this.terminal().read(cx); if terminal.task().is_none() { - if let Some(foreground_info) = &terminal.foreground_process_info { - let cwd = foreground_info.cwd.clone(); - + if let Some(cwd) = terminal.get_cwd() { let item_id = cx.entity_id(); let workspace_id = this.workspace_id; cx.background_executor()