mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
Remove legacy panic handling (#37947)
@maxdeviant We can eventually turn down the panic telemetry endpoint, but should probably leave it up while there's still a bunch of stable users hitting it. @maxbrunsfeld We're optimistic that this change also fixed the macos crashed-thread misreporting. We think it was because the `CrashContext::exception` was getting set to `None` only on macos, while on linux it was getting a real exception value from the sigtrap. Now we've unified and it uses `SIGABRT` on both platforms (I need to double check that this works as expected for windows). We unconditionally set `RUST_BACKTRACE=1` for the current process so that we see backtraces when running in a terminal by default. This should be fine but I just wanted to note it since it's a bit abnormal. Release Notes: - N/A --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
parent
116c6549f6
commit
a33af4e9c0
10 changed files with 69 additions and 544 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -2853,9 +2853,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
|
|
@ -4040,6 +4040,7 @@ name = "crashes"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"cfg-if",
|
||||
"crash-handler",
|
||||
"log",
|
||||
"mach2 0.5.0",
|
||||
|
|
@ -13502,9 +13503,7 @@ dependencies = [
|
|||
"askpass",
|
||||
"assistant_tool",
|
||||
"assistant_tools",
|
||||
"backtrace",
|
||||
"cargo_toml",
|
||||
"chrono",
|
||||
"clap",
|
||||
"client",
|
||||
"clock",
|
||||
|
|
@ -13549,7 +13548,6 @@ dependencies = [
|
|||
"shellexpand 2.1.2",
|
||||
"smol",
|
||||
"sysinfo",
|
||||
"telemetry_events",
|
||||
"thiserror 2.0.12",
|
||||
"toml 0.8.20",
|
||||
"unindent",
|
||||
|
|
@ -20512,7 +20510,6 @@ dependencies = [
|
|||
"breadcrumbs",
|
||||
"call",
|
||||
"channel",
|
||||
"chrono",
|
||||
"clap",
|
||||
"cli",
|
||||
"client",
|
||||
|
|
@ -20563,7 +20560,6 @@ dependencies = [
|
|||
"language_selector",
|
||||
"language_tools",
|
||||
"languages",
|
||||
"libc",
|
||||
"line_ending_selector",
|
||||
"log",
|
||||
"markdown",
|
||||
|
|
|
|||
|
|
@ -472,6 +472,7 @@ blake3 = "1.5.3"
|
|||
bytes = "1.0"
|
||||
cargo_metadata = "0.19"
|
||||
cargo_toml = "0.21"
|
||||
cfg-if = "1.0.3"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
ciborium = "0.2"
|
||||
circular-buffer = "1.0"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ license = "GPL-3.0-or-later"
|
|||
|
||||
[dependencies]
|
||||
bincode.workspace = true
|
||||
cfg-if.workspace = true
|
||||
crash-handler.workspace = true
|
||||
log.workspace = true
|
||||
minidumper.workspace = true
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crash_handler::CrashHandler;
|
||||
use crash_handler::{CrashEventResult, CrashHandler};
|
||||
use log::info;
|
||||
use minidumper::{Client, LoopAction, MinidumpBinary};
|
||||
use release_channel::{RELEASE_CHANNEL, ReleaseChannel};
|
||||
|
|
@ -10,7 +10,7 @@ use std::{
|
|||
env,
|
||||
fs::{self, File},
|
||||
io,
|
||||
panic::Location,
|
||||
panic::{self, PanicHookInfo},
|
||||
path::{Path, PathBuf},
|
||||
process::{self, Command},
|
||||
sync::{
|
||||
|
|
@ -33,7 +33,16 @@ static PANIC_THREAD_ID: AtomicU32 = AtomicU32::new(0);
|
|||
|
||||
pub async fn init(crash_init: InitCrashHandler) {
|
||||
if *RELEASE_CHANNEL == ReleaseChannel::Dev && env::var("ZED_GENERATE_MINIDUMPS").is_err() {
|
||||
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
|
||||
std::process::exit(1);
|
||||
}));
|
||||
return;
|
||||
} else {
|
||||
panic::set_hook(Box::new(panic_hook));
|
||||
}
|
||||
|
||||
let exe = env::current_exe().expect("unable to find ourselves");
|
||||
|
|
@ -71,7 +80,7 @@ pub async fn init(crash_init: InitCrashHandler) {
|
|||
.unwrap();
|
||||
|
||||
let client = Arc::new(client);
|
||||
let handler = crash_handler::CrashHandler::attach(unsafe {
|
||||
let handler = CrashHandler::attach(unsafe {
|
||||
let client = client.clone();
|
||||
crash_handler::make_crash_event(move |crash_context: &crash_handler::CrashContext| {
|
||||
// only request a minidump once
|
||||
|
|
@ -87,7 +96,7 @@ pub async fn init(crash_init: InitCrashHandler) {
|
|||
} else {
|
||||
true
|
||||
};
|
||||
crash_handler::CrashEventResult::Handled(res)
|
||||
CrashEventResult::Handled(res)
|
||||
})
|
||||
})
|
||||
.expect("failed to attach signal handler");
|
||||
|
|
@ -257,8 +266,16 @@ impl minidumper::ServerHandler for CrashServer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn handle_panic(message: String, span: Option<&Location>) {
|
||||
let span = span
|
||||
pub fn panic_hook(info: &PanicHookInfo) {
|
||||
let message = info
|
||||
.payload()
|
||||
.downcast_ref::<&str>()
|
||||
.map(|s| s.to_string())
|
||||
.or_else(|| info.payload().downcast_ref::<String>().cloned())
|
||||
.unwrap_or_else(|| "Box<Any>".to_string());
|
||||
|
||||
let span = info
|
||||
.location()
|
||||
.map(|loc| format!("{}:{}", loc.file(), loc.line()))
|
||||
.unwrap_or_default();
|
||||
|
||||
|
|
@ -281,11 +298,15 @@ pub fn handle_panic(message: String, span: Option<&Location>) {
|
|||
Ordering::SeqCst,
|
||||
);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
CrashHandler.simulate_signal(crash_handler::Signal::Trap as u32);
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
CrashHandler.simulate_exception(None);
|
||||
break;
|
||||
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)
|
||||
break;
|
||||
} else {
|
||||
std::process::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
thread::sleep(retry_frequency);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ message GetCrashFiles {
|
|||
|
||||
message GetCrashFilesResponse {
|
||||
repeated CrashReport crashes = 1;
|
||||
repeated string legacy_panics = 2;
|
||||
reserved 2; // old panics
|
||||
}
|
||||
|
||||
message CrashReport {
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ test-support = ["fs/test-support"]
|
|||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
askpass.workspace = true
|
||||
backtrace = "0.3"
|
||||
chrono.workspace = true
|
||||
clap.workspace = true
|
||||
client.workspace = true
|
||||
dap_adapters.workspace = true
|
||||
|
|
@ -61,7 +59,6 @@ settings.workspace = true
|
|||
shellexpand.workspace = true
|
||||
smol.workspace = true
|
||||
sysinfo.workspace = true
|
||||
telemetry_events.workspace = true
|
||||
util.workspace = true
|
||||
watch.workspace = true
|
||||
worktree.workspace = true
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::HeadlessProject;
|
||||
use crate::headless_project::HeadlessAppState;
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use chrono::Utc;
|
||||
use client::{ProxySettings, telemetry};
|
||||
use client::ProxySettings;
|
||||
use util::ResultExt;
|
||||
|
||||
use extension::ExtensionHostProxy;
|
||||
use fs::{Fs, RealFs};
|
||||
|
|
@ -29,26 +29,23 @@ use reqwest_client::ReqwestClient;
|
|||
use rpc::proto::{self, Envelope, REMOTE_SERVER_PROJECT_ID};
|
||||
use rpc::{AnyProtoClient, TypedEnvelope};
|
||||
use settings::{Settings, SettingsStore, watch_config_file};
|
||||
use smol::Async;
|
||||
use smol::channel::{Receiver, Sender};
|
||||
use smol::io::AsyncReadExt;
|
||||
|
||||
use smol::Async;
|
||||
use smol::{net::unix::UnixListener, stream::StreamExt as _};
|
||||
use std::ffi::OsStr;
|
||||
use std::ops::ControlFlow;
|
||||
use std::process::ExitStatus;
|
||||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
use std::{env, thread};
|
||||
use std::{
|
||||
env,
|
||||
ffi::OsStr,
|
||||
fs::File,
|
||||
io::Write,
|
||||
mem,
|
||||
ops::ControlFlow,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
process::ExitStatus,
|
||||
str::FromStr,
|
||||
sync::{Arc, LazyLock},
|
||||
};
|
||||
use telemetry_events::LocationData;
|
||||
use thiserror::Error;
|
||||
use util::ResultExt;
|
||||
|
||||
pub static VERSION: LazyLock<&str> = LazyLock::new(|| match *RELEASE_CHANNEL {
|
||||
ReleaseChannel::Stable | ReleaseChannel::Preview => env!("ZED_PKG_VERSION"),
|
||||
|
|
@ -71,12 +68,12 @@ fn init_logging_proxy() {
|
|||
|
||||
fn init_logging_server(log_file_path: PathBuf) -> Result<Receiver<Vec<u8>>> {
|
||||
struct MultiWrite {
|
||||
file: std::fs::File,
|
||||
file: File,
|
||||
channel: Sender<Vec<u8>>,
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl std::io::Write for MultiWrite {
|
||||
impl Write for MultiWrite {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
let written = self.file.write(buf)?;
|
||||
self.buffer.extend_from_slice(&buf[..written]);
|
||||
|
|
@ -120,87 +117,6 @@ fn init_logging_server(log_file_path: PathBuf) -> Result<Receiver<Vec<u8>>> {
|
|||
Ok(rx)
|
||||
}
|
||||
|
||||
fn init_panic_hook(session_id: String) {
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
let payload = info
|
||||
.payload()
|
||||
.downcast_ref::<&str>()
|
||||
.map(|s| s.to_string())
|
||||
.or_else(|| info.payload().downcast_ref::<String>().cloned())
|
||||
.unwrap_or_else(|| "Box<Any>".to_string());
|
||||
|
||||
crashes::handle_panic(payload.clone(), info.location());
|
||||
|
||||
let backtrace = backtrace::Backtrace::new();
|
||||
let mut backtrace = backtrace
|
||||
.frames()
|
||||
.iter()
|
||||
.flat_map(|frame| {
|
||||
frame
|
||||
.symbols()
|
||||
.iter()
|
||||
.filter_map(|frame| Some(format!("{:#}", frame.name()?)))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Strip out leading stack frames for rust panic-handling.
|
||||
if let Some(ix) = backtrace
|
||||
.iter()
|
||||
.position(|name| name == "rust_begin_unwind")
|
||||
{
|
||||
backtrace.drain(0..=ix);
|
||||
}
|
||||
|
||||
let thread = thread::current();
|
||||
let thread_name = thread.name().unwrap_or("<unnamed>");
|
||||
|
||||
log::error!(
|
||||
"panic occurred: {}\nBacktrace:\n{}",
|
||||
&payload,
|
||||
backtrace.join("\n")
|
||||
);
|
||||
|
||||
let panic_data = telemetry_events::Panic {
|
||||
thread: thread_name.into(),
|
||||
payload,
|
||||
location_data: info.location().map(|location| LocationData {
|
||||
file: location.file().into(),
|
||||
line: location.line(),
|
||||
}),
|
||||
app_version: format!("remote-server-{}", *VERSION),
|
||||
app_commit_sha: option_env!("ZED_COMMIT_SHA").map(|sha| sha.into()),
|
||||
release_channel: RELEASE_CHANNEL.dev_name().into(),
|
||||
target: env!("TARGET").to_owned().into(),
|
||||
os_name: telemetry::os_name(),
|
||||
os_version: Some(telemetry::os_version()),
|
||||
architecture: env::consts::ARCH.into(),
|
||||
panicked_on: Utc::now().timestamp_millis(),
|
||||
backtrace,
|
||||
system_id: None, // Set on SSH client
|
||||
installation_id: None, // Set on SSH client
|
||||
|
||||
// used on this end to associate panics with minidumps, but will be replaced on the SSH client
|
||||
session_id: session_id.clone(),
|
||||
};
|
||||
|
||||
if let Some(panic_data_json) = serde_json::to_string(&panic_data).log_err() {
|
||||
let timestamp = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string();
|
||||
let panic_file_path = paths::logs_dir().join(format!("zed-{timestamp}.panic"));
|
||||
let panic_file = std::fs::OpenOptions::new()
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(&panic_file_path)
|
||||
.log_err();
|
||||
if let Some(mut panic_file) = panic_file {
|
||||
writeln!(&mut panic_file, "{panic_data_json}").log_err();
|
||||
panic_file.flush().log_err();
|
||||
}
|
||||
}
|
||||
|
||||
std::process::abort();
|
||||
}));
|
||||
}
|
||||
|
||||
fn handle_crash_files_requests(project: &Entity<HeadlessProject>, client: &AnyProtoClient) {
|
||||
client.add_request_handler(
|
||||
project.downgrade(),
|
||||
|
|
@ -249,10 +165,7 @@ fn handle_crash_files_requests(project: &Entity<HeadlessProject>, client: &AnyPr
|
|||
}
|
||||
}
|
||||
|
||||
anyhow::Ok(proto::GetCrashFilesResponse {
|
||||
crashes,
|
||||
legacy_panics,
|
||||
})
|
||||
anyhow::Ok(proto::GetCrashFilesResponse { crashes })
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -434,13 +347,12 @@ pub fn execute_run(
|
|||
let id = std::process::id().to_string();
|
||||
app.background_executor()
|
||||
.spawn(crashes::init(crashes::InitCrashHandler {
|
||||
session_id: id.clone(),
|
||||
session_id: id,
|
||||
zed_version: VERSION.to_owned(),
|
||||
release_channel: release_channel::RELEASE_CHANNEL_NAME.clone(),
|
||||
commit_sha: option_env!("ZED_COMMIT_SHA").unwrap_or("no_sha").to_owned(),
|
||||
}))
|
||||
.detach();
|
||||
init_panic_hook(id);
|
||||
let log_rx = init_logging_server(log_file)?;
|
||||
log::info!(
|
||||
"starting up. pid_file: {:?}, stdin_socket: {:?}, stdout_socket: {:?}, stderr_socket: {:?}",
|
||||
|
|
@ -627,13 +539,12 @@ pub(crate) fn execute_proxy(
|
|||
|
||||
let id = std::process::id().to_string();
|
||||
smol::spawn(crashes::init(crashes::InitCrashHandler {
|
||||
session_id: id.clone(),
|
||||
session_id: id,
|
||||
zed_version: VERSION.to_owned(),
|
||||
release_channel: release_channel::RELEASE_CHANNEL_NAME.clone(),
|
||||
commit_sha: option_env!("ZED_COMMIT_SHA").unwrap_or("no_sha").to_owned(),
|
||||
}))
|
||||
.detach();
|
||||
init_panic_hook(id);
|
||||
|
||||
log::info!("starting proxy process. PID: {}", std::process::id());
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ bincode.workspace = true
|
|||
breadcrumbs.workspace = true
|
||||
call.workspace = true
|
||||
channel.workspace = true
|
||||
chrono.workspace = true
|
||||
clap.workspace = true
|
||||
cli.workspace = true
|
||||
client.workspace = true
|
||||
|
|
@ -93,7 +92,6 @@ language_selector.workspace = true
|
|||
language_tools.workspace = true
|
||||
languages = { workspace = true, features = ["load-grammars"] }
|
||||
line_ending_selector.workspace = true
|
||||
libc.workspace = true
|
||||
log.workspace = true
|
||||
markdown.workspace = true
|
||||
markdown_preview.workspace = true
|
||||
|
|
|
|||
|
|
@ -278,13 +278,6 @@ pub fn main() {
|
|||
.unwrap_or_else(|| "no sha".to_owned()),
|
||||
}))
|
||||
.detach();
|
||||
reliability::init_panic_hook(
|
||||
app_version,
|
||||
app_commit_sha.clone(),
|
||||
system_id.as_ref().map(|id| id.to_string()),
|
||||
installation_id.as_ref().map(|id| id.to_string()),
|
||||
session_id.clone(),
|
||||
);
|
||||
|
||||
let (open_listener, mut open_rx) = OpenListener::new();
|
||||
|
||||
|
|
@ -542,8 +535,6 @@ pub fn main() {
|
|||
reliability::init(
|
||||
client.http_client(),
|
||||
system_id.as_ref().map(|id| id.to_string()),
|
||||
installation_id.clone().map(|id| id.to_string()),
|
||||
session_id.clone(),
|
||||
cx,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,226 +1,32 @@
|
|||
use crate::stdout_is_a_pty;
|
||||
use anyhow::{Context as _, Result};
|
||||
use backtrace::{self, Backtrace};
|
||||
use chrono::Utc;
|
||||
use client::{
|
||||
TelemetrySettings,
|
||||
telemetry::{self, MINIDUMP_ENDPOINT},
|
||||
};
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use client::{TelemetrySettings, telemetry::MINIDUMP_ENDPOINT};
|
||||
use futures::AsyncReadExt;
|
||||
use gpui::{App, AppContext as _, SemanticVersion};
|
||||
use http_client::{self, HttpClient, HttpClientWithUrl, HttpRequestExt, Method};
|
||||
use paths::{crashes_dir, crashes_retired_dir};
|
||||
use gpui::{App, AppContext as _};
|
||||
use http_client::{self, HttpClient, HttpClientWithUrl};
|
||||
use project::Project;
|
||||
use proto::{CrashReport, GetCrashFilesResponse};
|
||||
use release_channel::{AppCommitSha, RELEASE_CHANNEL, ReleaseChannel};
|
||||
use reqwest::multipart::{Form, Part};
|
||||
use settings::Settings;
|
||||
use smol::stream::StreamExt;
|
||||
use std::{
|
||||
env,
|
||||
ffi::{OsStr, c_void},
|
||||
fs,
|
||||
io::Write,
|
||||
panic,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicU32, Ordering},
|
||||
},
|
||||
thread,
|
||||
};
|
||||
use telemetry_events::{LocationData, Panic, PanicRequest};
|
||||
use url::Url;
|
||||
use std::{ffi::OsStr, fs, sync::Arc};
|
||||
use util::ResultExt;
|
||||
|
||||
static PANIC_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
pub fn init_panic_hook(
|
||||
app_version: SemanticVersion,
|
||||
app_commit_sha: Option<AppCommitSha>,
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: String,
|
||||
) {
|
||||
let is_pty = stdout_is_a_pty();
|
||||
|
||||
panic::set_hook(Box::new(move |info| {
|
||||
let prior_panic_count = PANIC_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
if prior_panic_count > 0 {
|
||||
// Give the panic-ing thread time to write the panic file
|
||||
loop {
|
||||
thread::yield_now();
|
||||
}
|
||||
}
|
||||
|
||||
let payload = info
|
||||
.payload()
|
||||
.downcast_ref::<&str>()
|
||||
.map(|s| s.to_string())
|
||||
.or_else(|| info.payload().downcast_ref::<String>().cloned())
|
||||
.unwrap_or_else(|| "Box<Any>".to_string());
|
||||
|
||||
if *release_channel::RELEASE_CHANNEL != ReleaseChannel::Dev
|
||||
|| env::var("ZED_GENERATE_MINIDUMPS").is_ok()
|
||||
{
|
||||
crashes::handle_panic(payload.clone(), info.location());
|
||||
}
|
||||
|
||||
let thread = thread::current();
|
||||
let thread_name = thread.name().unwrap_or("<unnamed>");
|
||||
|
||||
if *release_channel::RELEASE_CHANNEL == ReleaseChannel::Dev {
|
||||
let location = info.location().unwrap();
|
||||
let backtrace = Backtrace::new();
|
||||
eprintln!(
|
||||
"Thread {:?} panicked with {:?} at {}:{}:{}\n{}{:?}",
|
||||
thread_name,
|
||||
payload,
|
||||
location.file(),
|
||||
location.line(),
|
||||
location.column(),
|
||||
match app_commit_sha.as_ref() {
|
||||
Some(commit_sha) => format!(
|
||||
"https://github.com/zed-industries/zed/blob/{}/{}#L{} \
|
||||
(may not be uploaded, line may be incorrect if files modified)\n",
|
||||
commit_sha.full(),
|
||||
location.file(),
|
||||
location.line()
|
||||
),
|
||||
None => "".to_string(),
|
||||
},
|
||||
backtrace,
|
||||
);
|
||||
if MINIDUMP_ENDPOINT.is_none() {
|
||||
std::process::exit(-1);
|
||||
}
|
||||
}
|
||||
let main_module_base_address = get_main_module_base_address();
|
||||
|
||||
let backtrace = Backtrace::new();
|
||||
let mut symbols = backtrace
|
||||
.frames()
|
||||
.iter()
|
||||
.flat_map(|frame| {
|
||||
let base = frame
|
||||
.module_base_address()
|
||||
.unwrap_or(main_module_base_address);
|
||||
frame.symbols().iter().map(move |symbol| {
|
||||
format!(
|
||||
"{}+{}",
|
||||
symbol
|
||||
.name()
|
||||
.as_ref()
|
||||
.map_or("<unknown>".to_owned(), <_>::to_string),
|
||||
(frame.ip() as isize).saturating_sub(base as isize)
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Strip out leading stack frames for rust panic-handling.
|
||||
if let Some(ix) = symbols
|
||||
.iter()
|
||||
.position(|name| name == "rust_begin_unwind" || name == "_rust_begin_unwind")
|
||||
{
|
||||
symbols.drain(0..=ix);
|
||||
}
|
||||
|
||||
let panic_data = telemetry_events::Panic {
|
||||
thread: thread_name.into(),
|
||||
payload,
|
||||
location_data: info.location().map(|location| LocationData {
|
||||
file: location.file().into(),
|
||||
line: location.line(),
|
||||
}),
|
||||
app_version: app_version.to_string(),
|
||||
app_commit_sha: app_commit_sha.as_ref().map(|sha| sha.full()),
|
||||
release_channel: RELEASE_CHANNEL.dev_name().into(),
|
||||
target: env!("TARGET").to_owned().into(),
|
||||
os_name: telemetry::os_name(),
|
||||
os_version: Some(telemetry::os_version()),
|
||||
architecture: env::consts::ARCH.into(),
|
||||
panicked_on: Utc::now().timestamp_millis(),
|
||||
backtrace: symbols,
|
||||
system_id: system_id.clone(),
|
||||
installation_id: installation_id.clone(),
|
||||
session_id: session_id.clone(),
|
||||
};
|
||||
|
||||
if let Some(panic_data_json) = serde_json::to_string_pretty(&panic_data).log_err() {
|
||||
log::error!("{}", panic_data_json);
|
||||
}
|
||||
zlog::flush();
|
||||
|
||||
if (!is_pty || MINIDUMP_ENDPOINT.is_some())
|
||||
&& let Some(panic_data_json) = serde_json::to_string(&panic_data).log_err()
|
||||
{
|
||||
let timestamp = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string();
|
||||
let panic_file_path = paths::logs_dir().join(format!("zed-{timestamp}.panic"));
|
||||
let panic_file = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(&panic_file_path)
|
||||
.log_err();
|
||||
if let Some(mut panic_file) = panic_file {
|
||||
writeln!(&mut panic_file, "{panic_data_json}").log_err();
|
||||
panic_file.flush().log_err();
|
||||
}
|
||||
}
|
||||
|
||||
std::process::abort();
|
||||
}));
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn get_main_module_base_address() -> *mut c_void {
|
||||
let mut dl_info = libc::Dl_info {
|
||||
dli_fname: std::ptr::null(),
|
||||
dli_fbase: std::ptr::null_mut(),
|
||||
dli_sname: std::ptr::null(),
|
||||
dli_saddr: std::ptr::null_mut(),
|
||||
};
|
||||
unsafe {
|
||||
libc::dladdr(get_main_module_base_address as _, &mut dl_info);
|
||||
}
|
||||
dl_info.dli_fbase
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn get_main_module_base_address() -> *mut c_void {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
pub fn init(
|
||||
http_client: Arc<HttpClientWithUrl>,
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: String,
|
||||
cx: &mut App,
|
||||
) {
|
||||
pub fn init(http_client: Arc<HttpClientWithUrl>, installation_id: Option<String>, cx: &mut App) {
|
||||
#[cfg(target_os = "macos")]
|
||||
monitor_main_thread_hangs(http_client.clone(), installation_id.clone(), cx);
|
||||
|
||||
let Some(panic_report_url) = http_client
|
||||
.build_zed_api_url("/telemetry/panics", &[])
|
||||
.log_err()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
upload_panics_and_crashes(
|
||||
http_client.clone(),
|
||||
panic_report_url.clone(),
|
||||
installation_id.clone(),
|
||||
cx,
|
||||
);
|
||||
if client::TelemetrySettings::get_global(cx).diagnostics {
|
||||
let client = http_client.clone();
|
||||
let id = installation_id.clone();
|
||||
cx.background_spawn(async move {
|
||||
upload_previous_minidumps(client, id).await.warn_on_err();
|
||||
})
|
||||
.detach()
|
||||
}
|
||||
|
||||
cx.observe_new(move |project: &mut Project, _, cx| {
|
||||
let http_client = http_client.clone();
|
||||
let panic_report_url = panic_report_url.clone();
|
||||
let session_id = session_id.clone();
|
||||
let installation_id = installation_id.clone();
|
||||
let system_id = system_id.clone();
|
||||
|
||||
let Some(remote_client) = project.remote_client() else {
|
||||
return;
|
||||
|
|
@ -231,19 +37,7 @@ pub fn init(
|
|||
}
|
||||
let request = client.proto_client().request(proto::GetCrashFiles {});
|
||||
cx.background_spawn(async move {
|
||||
let GetCrashFilesResponse {
|
||||
legacy_panics,
|
||||
crashes,
|
||||
} = request.await?;
|
||||
|
||||
for panic in legacy_panics {
|
||||
if let Some(mut panic) = serde_json::from_str::<Panic>(&panic).log_err() {
|
||||
panic.session_id = session_id.clone();
|
||||
panic.system_id = system_id.clone();
|
||||
panic.installation_id = installation_id.clone();
|
||||
upload_panic(&http_client, &panic_report_url, panic, &mut None).await?;
|
||||
}
|
||||
}
|
||||
let GetCrashFilesResponse { crashes } = request.await?;
|
||||
|
||||
let Some(endpoint) = MINIDUMP_ENDPOINT.as_ref() else {
|
||||
return Ok(());
|
||||
|
|
@ -297,6 +91,7 @@ pub fn monitor_main_thread_hangs(
|
|||
use parking_lot::Mutex;
|
||||
|
||||
use http_client::Method;
|
||||
use release_channel::ReleaseChannel;
|
||||
use std::{
|
||||
ffi::c_int,
|
||||
sync::{OnceLock, mpsc},
|
||||
|
|
@ -440,7 +235,7 @@ pub fn monitor_main_thread_hangs(
|
|||
app_version: Some(app_version),
|
||||
os_name: os_name.clone(),
|
||||
os_version: Some(os_version.clone()),
|
||||
architecture: env::consts::ARCH.into(),
|
||||
architecture: std::env::consts::ARCH.into(),
|
||||
installation_id: installation_id.clone(),
|
||||
};
|
||||
|
||||
|
|
@ -477,86 +272,6 @@ pub fn monitor_main_thread_hangs(
|
|||
.detach()
|
||||
}
|
||||
|
||||
fn upload_panics_and_crashes(
|
||||
http: Arc<HttpClientWithUrl>,
|
||||
panic_report_url: Url,
|
||||
installation_id: Option<String>,
|
||||
cx: &App,
|
||||
) {
|
||||
if !client::TelemetrySettings::get_global(cx).diagnostics {
|
||||
return;
|
||||
}
|
||||
cx.background_spawn(async move {
|
||||
upload_previous_minidumps(http.clone(), installation_id.clone())
|
||||
.await
|
||||
.warn_on_err();
|
||||
let most_recent_panic = upload_previous_panics(http.clone(), &panic_report_url)
|
||||
.await
|
||||
.log_err()
|
||||
.flatten();
|
||||
upload_previous_crashes(http, most_recent_panic, installation_id)
|
||||
.await
|
||||
.log_err();
|
||||
})
|
||||
.detach()
|
||||
}
|
||||
|
||||
/// Uploads panics via `zed.dev`.
|
||||
async fn upload_previous_panics(
|
||||
http: Arc<HttpClientWithUrl>,
|
||||
panic_report_url: &Url,
|
||||
) -> anyhow::Result<Option<(i64, String)>> {
|
||||
let mut children = smol::fs::read_dir(paths::logs_dir()).await?;
|
||||
|
||||
let mut most_recent_panic = None;
|
||||
|
||||
while let Some(child) = children.next().await {
|
||||
let child = child?;
|
||||
let child_path = child.path();
|
||||
|
||||
if child_path.extension() != Some(OsStr::new("panic")) {
|
||||
continue;
|
||||
}
|
||||
let filename = if let Some(filename) = child_path.file_name() {
|
||||
filename.to_string_lossy()
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if !filename.starts_with("zed") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let panic_file_content = smol::fs::read_to_string(&child_path)
|
||||
.await
|
||||
.context("error reading panic file")?;
|
||||
|
||||
let panic: Option<Panic> = serde_json::from_str(&panic_file_content)
|
||||
.log_err()
|
||||
.or_else(|| {
|
||||
panic_file_content
|
||||
.lines()
|
||||
.next()
|
||||
.and_then(|line| serde_json::from_str(line).ok())
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
log::error!("failed to deserialize panic file {:?}", panic_file_content);
|
||||
None
|
||||
});
|
||||
|
||||
if let Some(panic) = panic
|
||||
&& upload_panic(&http, panic_report_url, panic, &mut most_recent_panic).await?
|
||||
{
|
||||
// We've done what we can, delete the file
|
||||
fs::remove_file(child_path)
|
||||
.context("error removing panic")
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(most_recent_panic)
|
||||
}
|
||||
|
||||
pub async fn upload_previous_minidumps(
|
||||
http: Arc<HttpClientWithUrl>,
|
||||
installation_id: Option<String>,
|
||||
|
|
@ -728,109 +443,3 @@ impl FormExt for Form {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn upload_panic(
|
||||
http: &Arc<HttpClientWithUrl>,
|
||||
panic_report_url: &Url,
|
||||
panic: telemetry_events::Panic,
|
||||
most_recent_panic: &mut Option<(i64, String)>,
|
||||
) -> Result<bool> {
|
||||
*most_recent_panic = Some((panic.panicked_on, panic.payload.clone()));
|
||||
|
||||
let json_bytes = serde_json::to_vec(&PanicRequest { panic }).unwrap();
|
||||
|
||||
let Some(checksum) = client::telemetry::calculate_json_checksum(&json_bytes) else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
let Ok(request) = http_client::Request::builder()
|
||||
.method(Method::POST)
|
||||
.uri(panic_report_url.as_ref())
|
||||
.header("x-zed-checksum", checksum)
|
||||
.body(json_bytes.into())
|
||||
else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
let response = http.send(request).await.context("error sending panic")?;
|
||||
if !response.status().is_success() {
|
||||
log::error!("Error uploading panic to server: {}", response.status());
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
const LAST_CRASH_UPLOADED: &str = "LAST_CRASH_UPLOADED";
|
||||
|
||||
/// upload crashes from apple's diagnostic reports to our server.
|
||||
/// (only if telemetry is enabled)
|
||||
async fn upload_previous_crashes(
|
||||
http: Arc<HttpClientWithUrl>,
|
||||
most_recent_panic: Option<(i64, String)>,
|
||||
installation_id: Option<String>,
|
||||
) -> Result<()> {
|
||||
let last_uploaded = KEY_VALUE_STORE
|
||||
.read_kvp(LAST_CRASH_UPLOADED)?
|
||||
.unwrap_or("zed-2024-01-17-221900.ips".to_string()); // don't upload old crash reports from before we had this.
|
||||
let mut uploaded = last_uploaded.clone();
|
||||
|
||||
let crash_report_url = http.build_zed_api_url("/telemetry/crashes", &[])?;
|
||||
|
||||
// Crash directories are only set on macOS.
|
||||
for dir in [crashes_dir(), crashes_retired_dir()]
|
||||
.iter()
|
||||
.filter_map(|d| d.as_deref())
|
||||
{
|
||||
let mut children = smol::fs::read_dir(&dir).await?;
|
||||
while let Some(child) = children.next().await {
|
||||
let child = child?;
|
||||
let Some(filename) = child
|
||||
.path()
|
||||
.file_name()
|
||||
.map(|f| f.to_string_lossy().to_lowercase())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if !filename.starts_with("zed-") || !filename.ends_with(".ips") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if filename <= last_uploaded {
|
||||
continue;
|
||||
}
|
||||
|
||||
let body = smol::fs::read_to_string(&child.path())
|
||||
.await
|
||||
.context("error reading crash file")?;
|
||||
|
||||
let mut request = http_client::Request::post(&crash_report_url.to_string())
|
||||
.follow_redirects(http_client::RedirectPolicy::FollowAll)
|
||||
.header("Content-Type", "text/plain");
|
||||
|
||||
if let Some((panicked_on, payload)) = most_recent_panic.as_ref() {
|
||||
request = request
|
||||
.header("x-zed-panicked-on", format!("{panicked_on}"))
|
||||
.header("x-zed-panic", payload)
|
||||
}
|
||||
if let Some(installation_id) = installation_id.as_ref() {
|
||||
request = request.header("x-zed-installation-id", installation_id);
|
||||
}
|
||||
|
||||
let request = request.body(body.into())?;
|
||||
|
||||
let response = http.send(request).await.context("error sending crash")?;
|
||||
if !response.status().is_success() {
|
||||
log::error!("Error uploading crash to server: {}", response.status());
|
||||
}
|
||||
|
||||
if uploaded < filename {
|
||||
uploaded.clone_from(&filename);
|
||||
KEY_VALUE_STORE
|
||||
.write_kvp(LAST_CRASH_UPLOADED.to_string(), filename)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue