Cleanup crashes crate (#54927)

Self-Review Checklist:

- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable

Closes #ISSUE

Release Notes:

- N/A or Added/Fixed/Improved ...
This commit is contained in:
Lukas Wirth 2026-05-04 08:30:47 +02:00 committed by GitHub
parent d4f06ee374
commit f40d7f0166
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 371 additions and 273 deletions

10
Cargo.lock generated
View file

@ -4097,9 +4097,9 @@ dependencies = [
[[package]]
name = "crash-handler"
version = "0.6.3"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2066907075af649bcb8bcb1b9b986329b243677e6918b2d920aa64b0aac5ace3"
checksum = "0df5c9639f4942eb7702b964b3f9adf03a55724a57558cc177407388a8b936e2"
dependencies = [
"cfg-if",
"crash-context",
@ -4113,15 +4113,11 @@ name = "crashes"
version = "0.1.0"
dependencies = [
"async-process",
"cfg-if",
"crash-handler",
"futures 0.3.32",
"log",
"mach2 0.5.0",
"minidumper",
"parking_lot",
"paths",
"release_channel",
"serde",
"serde_json",
"system_specs",
@ -6260,6 +6256,7 @@ dependencies = [
name = "feedback"
version = "0.1.0"
dependencies = [
"client",
"extension_host",
"gpui",
"system_specs",
@ -17561,7 +17558,6 @@ name = "system_specs"
version = "0.1.0"
dependencies = [
"anyhow",
"client",
"gpui",
"human_bytes",
"pciid-parser",

View file

@ -553,7 +553,7 @@ core-foundation = "=0.10.0"
core-foundation-sys = "0.8.6"
core-video = { version = "0.5.2", features = ["metal"] }
cpal = "0.17"
crash-handler = "0.6"
crash-handler = "0.7"
criterion = { version = "0.5", features = ["html_reports"] }
ctor = "0.4.0"
dap-types = { git = "https://github.com/zed-industries/dap-types", rev = "1b461b310481d01e02b2603c16d7144b926339f8" }
@ -913,6 +913,7 @@ wasmtime = { opt-level = 3 }
cranelift-codegen = { opt-level = 3 }
wasmtime-environ = { opt-level = 3 }
wasmtime-internal-cranelift = { opt-level = 3 }
minidumper = { opt-level = 3 }
# Build single-source-file crates with cg=1 as it helps make `cargo build` of a whole workspace a bit faster
activity_indicator = { codegen-units = 1 }
assets = { codegen-units = 1 }

View file

@ -7,14 +7,10 @@ license = "GPL-3.0-or-later"
[dependencies]
async-process.workspace = true
cfg-if.workspace = true
crash-handler.workspace = true
futures.workspace = true
log.workspace = true
minidumper.workspace = true
parking_lot.workspace = true
paths.workspace = true
release_channel.workspace = true
serde.workspace = true
serde_json.workspace = true
system_specs.workspace = true

View file

@ -1,55 +1,42 @@
use crash_handler::{CrashEventResult, CrashHandler};
use futures::future::BoxFuture;
use log::info;
use minidumper::{Client, LoopAction, MinidumpBinary, Server, SocketName};
use minidumper::{LoopAction, MinidumpBinary, Server, SocketName};
use parking_lot::Mutex;
use release_channel::{RELEASE_CHANNEL, ReleaseChannel};
use serde::{Deserialize, Serialize};
use std::mem;
use std::{panic::Location, pin::Pin};
#[cfg(not(target_os = "windows"))]
use async_process::Command;
use system_specs::GpuSpecs;
#[cfg(target_os = "macos")]
use std::sync::atomic::AtomicU32;
use std::{
env,
fs::{self, File},
io,
panic::{self, PanicHookInfo},
io, panic,
path::{Path, PathBuf},
process::{self},
sync::{
Arc, OnceLock,
Arc,
atomic::{AtomicBool, Ordering},
},
thread,
time::Duration,
};
// set once the crash handler has initialized and the client has connected to it
static CRASH_HANDLER: OnceLock<Arc<Client>> = OnceLock::new();
// set when the first minidump request is made to avoid generating duplicate crash reports
pub static REQUESTED_MINIDUMP: AtomicBool = AtomicBool::new(false);
pub use minidumper::Client;
const CRASH_HANDLER_PING_TIMEOUT: Duration = Duration::from_secs(60);
const CRASH_HANDLER_CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
static PENDING_CRASH_SERVER_MESSAGES: Mutex<Vec<CrashServerMessage>> = Mutex::new(Vec::new());
#[cfg(target_os = "macos")]
static PANIC_THREAD_ID: AtomicU32 = AtomicU32::new(0);
fn should_install_crash_handler() -> bool {
if let Ok(value) = env::var("ZED_GENERATE_MINIDUMPS") {
return value == "true" || value == "1";
}
if *RELEASE_CHANNEL == ReleaseChannel::Dev {
return false;
}
true
/// Force a backtrace to be printed on panic.
pub fn force_backtrace() {
let old_hook = panic::take_hook();
panic::set_hook(Box::new(move |info| {
unsafe { env::set_var("RUST_BACKTRACE", "1") };
old_hook(info);
// prevent the macOS crash dialog from popping up
if cfg!(target_os = "macos") {
std::process::exit(1);
}
}));
}
/// Install crash signal handlers and spawn the crash-handler subprocess.
@ -57,133 +44,125 @@ fn should_install_crash_handler() -> bool {
/// The synchronous portion (signal handlers, panic hook) runs inline.
/// The async keepalive task is passed to `spawn` so the caller decides
/// which executor to schedule it on.
pub fn init<F: Future<Output = ()> + Send + Sync + 'static>(
pub fn init<F, S, C, P>(
crash_init: InitCrashHandler,
spawn: impl FnOnce(BoxFuture<'static, ()>),
wait_timer: impl (Fn(Duration) -> F) + Send + Sync + 'static,
) {
if !should_install_crash_handler() {
let old_hook = panic::take_hook();
panic::set_hook(Box::new(move |info| {
unsafe { env::set_var("RUST_BACKTRACE", "1") };
old_hook(info);
// prevent the macOS crash dialog from popping up
if cfg!(target_os = "macos") {
std::process::exit(1);
}
}));
return;
}
panic::set_hook(Box::new(panic_hook));
let handler = CrashHandler::attach(unsafe {
crash_handler::make_crash_event(move |crash_context: &crash_handler::CrashContext| {
let Some(client) = CRASH_HANDLER.get() else {
return CrashEventResult::Handled(false);
};
// only request a minidump once
let res = if REQUESTED_MINIDUMP
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
{
#[cfg(target_os = "macos")]
suspend_all_other_threads();
// on macos this "ping" is needed to ensure that all our
// `client.send_message` calls have been processed before we trigger the
// minidump request.
client.ping().ok();
client.request_dump(crash_context).is_ok()
} else {
true
};
CrashEventResult::Handled(res)
})
})
.expect("failed to attach signal handler");
info!("crash signal handlers installed");
spawn(Box::pin(connect_and_keepalive(
crash_init, handler, wait_timer,
)));
spawn: S,
socket_path: P,
wait_timer: C,
) -> impl Future<Output = Arc<Client>> + use<F, C, S, P>
where
F: Future<Output = ()> + Send + Sync + 'static,
C: (Fn(Duration) -> F) + Send + Sync + 'static,
S: FnOnce(Pin<Box<dyn Future<Output = ()> + Send + 'static>>),
P: FnOnce(u32) -> PathBuf,
{
connect_and_keepalive(crash_init, socket_path, wait_timer, spawn)
}
/// Spawn the crash-handler subprocess, connect the IPC client, and run the
/// keepalive ping loop. Called on a background executor by [`init`].
async fn connect_and_keepalive<F: Future<Output = ()> + Send + Sync + 'static>(
fn connect_and_keepalive<F, C, S, P>(
crash_init: InitCrashHandler,
handler: CrashHandler,
wait_timer: impl (Fn(Duration) -> F) + Send + Sync + 'static,
) {
socket_path: P,
wait_timer: C,
spawn: S,
) -> impl Future<Output = Arc<Client>> + use<F, C, S, P>
where
F: Future<Output = ()> + Send + Sync + 'static,
C: (Fn(Duration) -> F) + Send + Sync + 'static,
S: FnOnce(Pin<Box<dyn Future<Output = ()> + Send + 'static>>),
P: FnOnce(u32) -> PathBuf,
{
let exe = env::current_exe().expect("unable to find ourselves");
let zed_pid = process::id();
let socket_name = paths::temp_dir().join(format!("zed-crash-handler-{zed_pid}"));
#[cfg(not(target_os = "windows"))]
let _crash_handler = Command::new(exe)
.arg("--crash-handler")
.arg(&socket_name)
.spawn()
.expect("unable to spawn server process");
#[cfg(target_os = "windows")]
spawn_crash_handler_windows(&exe, &socket_name);
let socket_path = socket_path(process::id());
let mut _crash_handler = spawn_crash_handler(&exe, &socket_path);
info!("spawning crash handler process");
send_crash_server_message(CrashServerMessage::Init(crash_init));
async move {
let mut elapsed = Duration::ZERO;
let retry_frequency = Duration::from_millis(100);
let client = loop {
if let Ok(client) = Client::with_name(SocketName::Path(&socket_path)) {
info!("connected to crash handler process after {elapsed:?}");
break client;
}
elapsed += retry_frequency;
wait_timer(retry_frequency).await;
};
let client = Arc::new(client);
let mut elapsed = Duration::ZERO;
let retry_frequency = Duration::from_millis(100);
let mut maybe_client = None;
while maybe_client.is_none() {
if let Ok(client) = Client::with_name(SocketName::Path(&socket_name)) {
maybe_client = Some(client);
info!("connected to crash handler process after {elapsed:?}");
break;
}
elapsed += retry_frequency;
wait_timer(retry_frequency).await;
}
let client = maybe_client.unwrap();
let client = Arc::new(client);
panic::set_hook({
let client = client.clone();
Box::new(move |payload| {
panic_hook(
client.clone(),
payload.payload_as_str().unwrap_or("Box<Any>"),
payload.location(),
)
})
});
info!("panic handler registered");
let handler = CrashHandler::attach(unsafe {
let client = client.clone();
let handler = move |crash_context: &crash_handler::CrashContext| {
// set when the first minidump request is made to avoid generating duplicate crash reports
static REQUESTED_MINIDUMP: AtomicBool = AtomicBool::new(false);
#[cfg(target_os = "linux")]
handler.set_ptracer(Some(_crash_handler.id()));
// only request a minidump once
let res = if REQUESTED_MINIDUMP
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
{
#[cfg(target_os = "macos")]
macos::suspend_all_other_threads();
// Publishing the client to the OnceLock makes it visible to the signal
// handler callback installed earlier.
CRASH_HANDLER.set(client.clone()).ok();
let messages: Vec<_> = mem::take(PENDING_CRASH_SERVER_MESSAGES.lock().as_mut());
for message in messages.into_iter() {
send_crash_server_message(message);
}
// mem::forget so that the drop is not called
mem::forget(handler);
info!("crash handler registered");
// on macos this "ping" is needed to ensure that all our
// `client.send_message` calls have been processed before we trigger the
// minidump request.
client.ping().ok();
let r = client.request_dump(crash_context);
if let Err(e) = &r {
eprintln!("failed to request dump: {:?}", e);
}
#[cfg(target_os = "macos")]
macos::resume_all_other_threads();
r.is_ok()
} else {
true
};
CrashEventResult::Handled(res)
};
crash_handler::make_crash_event(handler)
})
.expect("failed to attach signal handler");
loop {
client.ping().ok();
wait_timer(Duration::from_secs(10)).await;
}
}
info!("crash signal handlers installed");
send_crash_server_message(&client, CrashServerMessage::Init(crash_init));
#[cfg(target_os = "macos")]
unsafe fn suspend_all_other_threads() {
let task = unsafe { mach2::traps::current_task() };
let mut threads: mach2::mach_types::thread_act_array_t = std::ptr::null_mut();
let mut count = 0;
unsafe {
mach2::task::task_threads(task, &raw mut threads, &raw mut count);
}
let current = unsafe { mach2::mach_init::mach_thread_self() };
let panic_thread = PANIC_THREAD_ID.load(Ordering::SeqCst);
for i in 0..count {
let t = unsafe { *threads.add(i as usize) };
if t != current && t != panic_thread {
unsafe { mach2::thread_act::thread_suspend(t) };
}
#[cfg(target_os = "linux")]
handler.set_ptracer(Some(_crash_handler.id()));
info!("crash handler registered");
spawn(Box::pin({
let client = client.clone();
async move {
let _handler = { handler };
loop {
if let Err(e) = client.ping() {
#[cfg(not(target_os = "windows"))]
log::error!(
"ping failed: {:?}, process exit status: {:?}",
e,
_crash_handler.try_status()
);
#[cfg(target_os = "windows")]
log::error!("ping failed: {:?}", e,);
break;
};
wait_timer(Duration::from_secs(10)).await;
}
}
}));
client
}
}
@ -193,6 +172,7 @@ pub struct CrashServer {
active_gpu: Mutex<Option<system_specs::GpuSpecs>>,
user_info: Mutex<Option<UserInfo>>,
has_connection: Arc<AtomicBool>,
logs_dir: PathBuf,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
@ -226,11 +206,7 @@ pub struct UserInfo {
pub is_staff: Option<bool>,
}
fn send_crash_server_message(message: CrashServerMessage) {
let Some(crash_server) = CRASH_HANDLER.get() else {
PENDING_CRASH_SERVER_MESSAGES.lock().push(message);
return;
};
fn send_crash_server_message(crash_client: &Arc<Client>, message: CrashServerMessage) {
let data = match serde_json::to_vec(&message) {
Ok(data) => data,
Err(err) => {
@ -239,17 +215,17 @@ fn send_crash_server_message(message: CrashServerMessage) {
}
};
if let Err(err) = crash_server.send_message(0, data) {
if let Err(err) = crash_client.send_message(0, data) {
log::warn!("Failed to send data to crash server {:?}", err);
}
}
pub fn set_gpu_info(specs: GpuSpecs) {
send_crash_server_message(CrashServerMessage::GPUInfo(specs));
pub fn set_gpu_info(crash_client: &Arc<Client>, specs: GpuSpecs) {
send_crash_server_message(crash_client, CrashServerMessage::GPUInfo(specs));
}
pub fn set_user_info(info: UserInfo) {
send_crash_server_message(CrashServerMessage::UserInfo(info));
pub fn set_user_info(crash_client: &Arc<Client>, info: UserInfo) {
send_crash_server_message(crash_client, CrashServerMessage::UserInfo(info));
}
#[derive(Serialize, Deserialize, Debug)]
@ -262,7 +238,8 @@ enum CrashServerMessage {
impl minidumper::ServerHandler for CrashServer {
fn create_minidump_file(&self) -> Result<(File, PathBuf), io::Error> {
let dump_path = paths::logs_dir()
let dump_path = self
.logs_dir
.join(
&self
.initialization_params
@ -318,7 +295,8 @@ impl minidumper::ServerHandler for CrashServer {
user_info: self.user_info.lock().clone(),
};
let crash_data_path = paths::logs_dir()
let crash_data_path = self
.logs_dir
.join(&crash_info.init.session_id)
.with_extension("json");
@ -382,52 +360,92 @@ fn strip_user_string_from_panic(message: &str) -> String {
message.to_owned()
}
pub fn panic_hook(info: &PanicHookInfo) {
let message = strip_user_string_from_panic(info.payload_as_str().unwrap_or("Box<Any>"));
pub fn panic_hook(crash_client: Arc<Client>, message: &str, location: Option<&Location>) {
let message = strip_user_string_from_panic(message);
let span = info
.location()
let span = location
.map(|loc| format!("{}:{}", loc.file(), loc.line()))
.unwrap_or_default();
let current_thread = std::thread::current();
let thread_name = current_thread.name().unwrap_or("<unnamed>");
// wait 500ms for the crash handler process to start up
// if it's still not there just write panic info and no minidump
let retry_frequency = Duration::from_millis(100);
for _ in 0..5 {
if CRASH_HANDLER.get().is_some() {
break;
}
thread::sleep(retry_frequency);
}
let location = info
.location()
.map_or_else(|| "<unknown>".to_owned(), |location| location.to_string());
let location = location.map_or_else(|| "<unknown>".to_owned(), |location| location.to_string());
log::error!("thread '{thread_name}' panicked at {location}:\n{message}...");
send_crash_server_message(CrashServerMessage::Panic(CrashPanic { message, span }));
send_crash_server_message(
&crash_client,
CrashServerMessage::Panic(CrashPanic { message, span }),
);
log::error!("triggering a crash to generate a minidump...");
#[cfg(target_os = "macos")]
PANIC_THREAD_ID.store(
unsafe { mach2::mach_init::mach_thread_self() },
Ordering::SeqCst,
);
cfg_if::cfg_if! {
if #[cfg(target_os = "windows")] {
// https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
CrashHandler.simulate_exception(Some(234)); // (MORE_DATA_AVAILABLE)
} else {
std::process::abort();
}
macos::set_panic_thread_id();
#[cfg(target_os = "windows")]
{
// https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
CrashHandler.simulate_exception(Some(234)); // (MORE_DATA_AVAILABLE)
}
#[cfg(not(target_os = "windows"))]
{
std::process::abort();
}
}
#[cfg(target_os = "macos")]
mod macos {
static PANIC_THREAD_ID: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
pub(super) fn set_panic_thread_id() {
PANIC_THREAD_ID.store(
unsafe { mach2::mach_init::mach_thread_self() },
std::sync::atomic::Ordering::Release,
);
}
pub(super) unsafe fn suspend_all_other_threads() {
let task = unsafe { mach2::traps::current_task() };
let mut threads: mach2::mach_types::thread_act_array_t = std::ptr::null_mut();
let mut count = 0;
unsafe {
mach2::task::task_threads(task, &raw mut threads, &raw mut count);
}
let current = unsafe { mach2::mach_init::mach_thread_self() };
for i in 0..count {
let t = unsafe { *threads.add(i as usize) };
if t != current {
unsafe { mach2::thread_act::thread_suspend(t) };
}
}
}
pub(super) unsafe fn resume_all_other_threads() {
let task = unsafe { mach2::traps::current_task() };
let mut threads: mach2::mach_types::thread_act_array_t = std::ptr::null_mut();
let mut count = 0;
unsafe {
mach2::task::task_threads(task, &raw mut threads, &raw mut count);
}
let current = unsafe { mach2::mach_init::mach_thread_self() };
for i in 0..count {
let t = unsafe { *threads.add(i as usize) };
if t != current {
unsafe { mach2::thread_act::thread_resume(t) };
}
}
}
}
#[cfg(not(target_os = "windows"))]
fn spawn_crash_handler(exe: &Path, socket_name: &Path) -> async_process::Child {
async_process::Command::new(exe)
.arg("--crash-handler")
.arg(&socket_name)
.spawn()
.expect("unable to spawn server process")
}
#[cfg(target_os = "windows")]
fn spawn_crash_handler_windows(exe: &Path, socket_name: &Path) {
fn spawn_crash_handler(exe: &Path, socket_name: &Path) {
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
@ -477,7 +495,7 @@ fn spawn_crash_handler_windows(exe: &Path, socket_name: &Path) {
}
}
pub fn crash_server(socket: &Path) {
pub fn crash_server(socket: &Path, logs_dir: PathBuf) {
let Ok(mut server) = Server::with_name(SocketName::Path(socket)) else {
log::info!("Couldn't create socket, there may already be a running crash server");
return;
@ -508,6 +526,7 @@ pub fn crash_server(socket: &Path) {
user_info: Mutex::default(),
has_connection,
active_gpu: Mutex::default(),
logs_dir,
}),
&shutdown,
Some(CRASH_HANDLER_PING_TIMEOUT),

View file

@ -22,4 +22,4 @@ urlencoding.workspace = true
util.workspace = true
workspace.workspace = true
zed_actions.workspace = true
client.workspace = true

View file

@ -1,3 +1,4 @@
use client::telemetry;
use extension_host::ExtensionStore;
use gpui::{App, ClipboardItem, PromptLevel, actions};
use system_specs::{CopySystemSpecsIntoClipboard, SystemSpecs};
@ -48,7 +49,8 @@ pub fn init(cx: &mut App) {
cx.observe_new(|workspace: &mut Workspace, _, _| {
workspace
.register_action(|_, _: &CopySystemSpecsIntoClipboard, window, cx| {
let specs = SystemSpecs::new(window, cx);
let specs =
SystemSpecs::new(window, cx, telemetry::os_name(), telemetry::os_version());
cx.spawn_in(window, async move |_, cx| {
let specs = specs.await.to_string();
@ -83,7 +85,8 @@ pub fn init(cx: &mut App) {
cx.open_url(REQUEST_FEATURE_URL);
})
.register_action(move |_, _: &FileBugReport, window, cx| {
let specs = SystemSpecs::new(window, cx);
let specs =
SystemSpecs::new(window, cx, telemetry::os_name(), telemetry::os_version());
cx.spawn_in(window, async move |_, cx| {
let specs = specs.await;
cx.update(|_, cx| {
@ -94,7 +97,8 @@ pub fn init(cx: &mut App) {
.detach();
})
.register_action(move |_, _: &EmailZed, window, cx| {
let specs = SystemSpecs::new(window, cx);
let specs =
SystemSpecs::new(window, cx, telemetry::os_name(), telemetry::os_version());
cx.spawn_in(window, async move |_, cx| {
let specs = specs.await;
cx.update(|_, cx| {

View file

@ -30,7 +30,7 @@ fn main() -> anyhow::Result<()> {
}
if let Some(socket) = &cli.crash_handler {
crashes::crash_server(socket.as_path());
crashes::crash_server(socket.as_path(), paths::logs_dir().clone());
return Ok(());
}

View file

@ -462,21 +462,35 @@ pub fn execute_run(
let app = gpui_platform::headless();
let pid = std::process::id();
let id = pid.to_string();
crashes::init(
crashes::InitCrashHandler {
session_id: id,
zed_version: VERSION.to_owned(),
binary: "zed-remote-server".to_string(),
release_channel: release_channel::RELEASE_CHANNEL_NAME.clone(),
commit_sha: option_env!("ZED_COMMIT_SHA").unwrap_or("no_sha").to_owned(),
},
|task| {
app.background_executor().spawn(task).detach();
},
// we are running outside gpui
#[allow(clippy::disallowed_methods)]
|duration| FutureExt::map(Timer::after(duration), |_| ()),
);
let should_install_crash_handler = matches!(
env::var("ZED_GENERATE_MINIDUMPS").as_deref(),
Ok("true" | "1")
) || *RELEASE_CHANNEL != ReleaseChannel::Dev;
let crash_handler = if should_install_crash_handler {
Some(app.background_executor().spawn(crashes::init(
crashes::InitCrashHandler {
session_id: id,
zed_version: VERSION.to_owned(),
binary: "zed-remote-server".to_string(),
release_channel: release_channel::RELEASE_CHANNEL_NAME.clone(),
commit_sha: option_env!("ZED_COMMIT_SHA").unwrap_or("no_sha").to_owned(),
},
{
let background_executor = app.background_executor();
move |task| {
background_executor.spawn(task).detach();
}
},
|pid| paths::temp_dir().join(format!("zed-remote-server-crash-handler-{pid}")),
// we are running outside gpui
#[allow(clippy::disallowed_methods)]
|duration| FutureExt::map(Timer::after(duration), |_| ()),
)))
} else {
crashes::force_backtrace();
None
};
let log_rx = init_logging_server(&log_file)?;
log::info!(
"starting up with PID {}:\npid_file: {:?}, log_file: {:?}, stdin_socket: {:?}, stdout_socket: {:?}, stderr_socket: {:?}",
@ -515,7 +529,14 @@ pub fn execute_run(
let shell_env_loaded_rx: Option<oneshot::Receiver<()>> = None;
let git_hosting_provider_registry = Arc::new(GitHostingProviderRegistry::new());
let run = move |cx: &mut _| {
let run = move |cx: &mut App| {
if let Some(crash_handler) = crash_handler {
cx.spawn(async move |_cx| {
let _crash_handler = crash_handler.await;
// cx.update(|cx| cx.set_global(CrashHandler(crash_handler)))
})
.detach();
}
settings::init(cx);
let app_commit_sha = option_env!("ZED_COMMIT_SHA").map(|s| AppCommitSha::new(s.to_owned()));
let app_version = AppVersion::load(
@ -720,22 +741,30 @@ pub(crate) fn execute_proxy(
let server_paths = ServerPaths::new(&identifier)?;
let id = std::process::id().to_string();
crashes::init(
crashes::InitCrashHandler {
session_id: id,
zed_version: VERSION.to_owned(),
binary: "zed-remote-server".to_string(),
release_channel: release_channel::RELEASE_CHANNEL_NAME.clone(),
commit_sha: option_env!("ZED_COMMIT_SHA").unwrap_or("no_sha").to_owned(),
},
|task| {
smol::spawn(task).detach();
},
// we are running outside gpui
#[allow(clippy::disallowed_methods)]
|duration| FutureExt::map(Timer::after(duration), |_| ()),
);
let should_install_crash_handler = matches!(
env::var("ZED_GENERATE_MINIDUMPS").as_deref(),
Ok("true" | "1")
) || *RELEASE_CHANNEL != ReleaseChannel::Dev;
if should_install_crash_handler {
smol::spawn(crashes::init(
crashes::InitCrashHandler {
session_id: id,
zed_version: VERSION.to_owned(),
binary: "zed-remote-proxy".to_string(),
release_channel: release_channel::RELEASE_CHANNEL_NAME.clone(),
commit_sha: option_env!("ZED_COMMIT_SHA").unwrap_or("no_sha").to_owned(),
},
|task| {
smol::spawn(task).detach();
},
|pid| paths::temp_dir().join(format!("zed-remote-server-proxy-crash-handler-{pid}")),
// we are running outside gpui
#[allow(clippy::disallowed_methods)]
|duration| FutureExt::map(Timer::after(duration), |_| ()),
))
.detach();
};
log::info!("starting proxy process. PID: {}", std::process::id());
let server_pid = {
let server_pid = check_pid_file(&server_paths.pid_file).map_err(|source| {

View file

@ -16,7 +16,6 @@ default = []
[dependencies]
anyhow.workspace = true
client.workspace = true
gpui.workspace = true
human_bytes.workspace = true
release_channel.workspace = true

View file

@ -1,4 +1,3 @@
use client::telemetry;
pub use gpui::GpuSpecs;
use gpui::{App, AppContext as _, Task, Window, actions};
use human_bytes::human_bytes;
@ -30,10 +29,14 @@ pub struct SystemSpecs {
}
impl SystemSpecs {
pub fn new(window: &mut Window, cx: &mut App) -> Task<Self> {
pub fn new(
window: &mut Window,
cx: &mut App,
os_name: String,
os_version: String,
) -> Task<Self> {
let app_version = AppVersion::global(cx).to_string();
let release_channel = ReleaseChannel::global(cx);
let os_name = telemetry::os_name();
let system = System::new_with_specifics(
RefreshKind::nothing().with_memory(MemoryRefreshKind::everything()),
);
@ -55,7 +58,6 @@ impl SystemSpecs {
});
cx.background_spawn(async move {
let os_version = telemetry::os_version();
SystemSpecs {
app_version,
release_channel: release_channel.display_name(),
@ -74,9 +76,9 @@ impl SystemSpecs {
app_version: Version,
app_commit_sha: Option<AppCommitSha>,
release_channel: ReleaseChannel,
os_name: String,
os_version: String,
) -> Self {
let os_name = telemetry::os_name();
let os_version = telemetry::os_version();
let system = System::new_with_specifics(
RefreshKind::nothing().with_memory(MemoryRefreshKind::everything()),
);

View file

@ -23,6 +23,7 @@ use git::GitHostingProviderRegistry;
use git_ui::clone::clone_and_open;
use gpui::{
App, AppContext, Application, AsyncApp, Focusable as _, QuitMode, Task, UpdateGlobal as _,
block_on,
};
use gpui_platform;
@ -43,6 +44,7 @@ use recent_projects::{RemoteSettings, open_remote_project};
use release_channel::{AppCommitSha, AppVersion, ReleaseChannel};
use session::{AppSession, Session};
use settings::{BaseKeymap, Settings, SettingsStore, watch_config_file};
use smol::future::poll_once;
use std::{
cell::RefCell,
env,
@ -67,7 +69,7 @@ use zed::{
handle_keymap_file_changes, initialize_workspace, open_paths_with_positions,
};
use crate::zed::{OpenRequestKind, eager_load_active_theme_and_icon_theme};
use crate::zed::{CrashHandler, OpenRequestKind, eager_load_active_theme_and_icon_theme};
#[cfg(feature = "mimalloc")]
#[global_allocator]
@ -195,7 +197,7 @@ fn main() {
// `zed --crash-handler` Makes zed operate in minidump crash handler mode
if let Some(socket) = &args.crash_handler {
crashes::crash_server(socket.as_path());
crashes::crash_server(socket.as_path(), paths::logs_dir().clone());
return;
}
@ -299,6 +301,8 @@ fn main() {
app_version,
app_commit_sha,
*release_channel::RELEASE_CHANNEL,
client::telemetry::os_name(),
client::telemetry::os_version(),
);
println!("Zed System Specs (from CLI):\n{}", system_specs);
return;
@ -338,28 +342,6 @@ fn main() {
KeyValueStore::from_app_db(&app_db),
));
let background_executor = app.background_executor();
crashes::init(
InitCrashHandler {
session_id,
// strip the build and channel information from the version string, we send them separately
zed_version: semver::Version::new(
app_version.major,
app_version.minor,
app_version.patch,
)
.to_string(),
binary: "zed".to_string(),
release_channel: release_channel::RELEASE_CHANNEL_NAME.clone(),
commit_sha: app_commit_sha
.as_ref()
.map(|sha| sha.full())
.unwrap_or_else(|| "no sha".to_owned()),
},
|task| {
app.background_executor().spawn(task).detach();
},
move |duration| background_executor.timer(duration),
);
let (open_listener, mut open_rx) = OpenListener::new();
@ -389,6 +371,46 @@ fn main() {
return;
}
let should_install_crash_handler = matches!(
env::var("ZED_GENERATE_MINIDUMPS").as_deref(),
Ok("true" | "1")
) || *release_channel::RELEASE_CHANNEL
!= ReleaseChannel::Dev;
let crash_handler = if should_install_crash_handler {
Some(
app.background_executor().spawn(crashes::init(
InitCrashHandler {
session_id,
// strip the build and channel information from the version string, we send them separately
zed_version: semver::Version::new(
app_version.major,
app_version.minor,
app_version.patch,
)
.to_string(),
binary: "zed".to_string(),
release_channel: release_channel::RELEASE_CHANNEL_NAME.clone(),
commit_sha: app_commit_sha
.as_ref()
.map(|sha| sha.full())
.unwrap_or_else(|| "no sha".to_owned()),
},
{
let background_executor1 = app.background_executor();
move |task| {
background_executor1.spawn(task).detach();
}
},
|pid| paths::temp_dir().join(format!("zed-crash-handler-{pid}")),
move |duration| background_executor.timer(duration),
)),
)
} else {
crashes::force_backtrace();
None
};
let git_hosting_provider_registry = Arc::new(GitHostingProviderRegistry::new());
let git_binary_path =
if cfg!(target_os = "macos") && option_env!("ZED_BUNDLE").as_deref() == Some("true") {
@ -417,7 +439,7 @@ fn main() {
util::load_login_shell_environment().await.log_err();
shell_env_loaded_tx.send(()).ok();
})
.detach()
.detach();
} else {
drop(shell_env_loaded_tx)
}
@ -573,12 +595,17 @@ fn main() {
);
cx.subscribe(&user_store, {
let telemetry = telemetry.clone();
move |_, evt: &client::user::Event, _| match evt {
move |_, evt: &client::user::Event, cx| match evt {
client::user::Event::PrivateUserInfoUpdated => {
crashes::set_user_info(crashes::UserInfo {
metrics_id: telemetry.metrics_id().map(|s| s.to_string()),
is_staff: telemetry.is_staff(),
});
if let Some(crash_client) = cx.try_global::<CrashHandler>() {
crashes::set_user_info(
&crash_client.0,
crashes::UserInfo {
metrics_id: telemetry.metrics_id().map(|s| s.to_string()),
is_staff: telemetry.is_staff(),
},
);
}
}
_ => {}
}
@ -810,6 +837,25 @@ fn main() {
let menus = app_menus(cx);
cx.set_menus(menus);
if let Some(mut crash_handler) = crash_handler {
let crash_handler2 = block_on(poll_once(&mut crash_handler));
match crash_handler2 {
Some(crash_handler) => {
cx.set_global(CrashHandler(crash_handler));
}
None => {
cx.spawn(async move |cx| {
let client1 = crash_handler.await;
cx.update(|cx| {
cx.set_global(CrashHandler(client1));
});
})
.detach();
}
}
}
initialize_workspace(app_state.clone(), cx);
cx.activate(true);

View file

@ -100,6 +100,10 @@ use zed_actions::{
OpenZedUrl, Quit,
};
pub struct CrashHandler(pub Arc<crashes::Client>);
impl gpui::Global for CrashHandler {}
actions!(
zed,
[
@ -515,7 +519,9 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut App) {
if let Some(specs) = window.gpu_specs() {
log::info!("Using GPU: {:?}", specs);
show_software_emulation_warning_if_needed(specs.clone(), window, cx);
crashes::set_gpu_info(specs);
if let Some(crash_client) = cx.try_global::<CrashHandler>() {
crashes::set_gpu_info(&crash_client.0, specs);
}
}
let edit_prediction_menu_handle = PopoverMenuHandle::default();