diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 5c5d35944672a36733d6da50505f8d5bacad74f3..e2b0cca6659e4d92818d484943b99442008d6b5f 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -159,6 +159,7 @@ vim_mode_setting.workspace = true watch.workspace = true web_search.workspace = true web_search_providers.workspace = true +which.workspace = true which_key.workspace = true workspace.workspace = true zed_actions.workspace = true diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 392a57520d28be021e55cbd890d2eb968370c2f7..268895865ad00fc4d0c55d393ccd4e9a9029d097 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -139,6 +139,10 @@ actions!( /// audio system (including yourself) on the current call in a tar file /// in the current working directory. CaptureRecentAudio, + /// Starts Tracy profiling by launching tracy-profiler if available. + /// Tracy will connect to this Zed instance and begin capturing performance data. + /// Requires Zed to be built with tracy support (ZTRACING=1). + StartTracing, ] ); @@ -1148,6 +1152,9 @@ fn register_actions( }) .register_action(|workspace, _: &CaptureRecentAudio, window, cx| { capture_recent_audio(workspace, window, cx); + }) + .register_action(|workspace, _: &StartTracing, window, cx| { + start_tracing(workspace, window, cx); }); #[cfg(not(target_os = "windows"))] @@ -2211,6 +2218,117 @@ fn capture_recent_audio(workspace: &mut Workspace, _: &mut Window, cx: &mut Cont ); } +fn start_tracing(workspace: &mut Workspace, _window: &mut Window, cx: &mut Context) { + struct StartTracingNotification { + focus_handle: gpui::FocusHandle, + status: TracingStatus, + _spawn_task: Option>, + } + + enum TracingStatus { + Starting, + TracyLaunched, + TracyNotFound, + TracyLaunchFailed(String), + ZtracingNotEnabled, + } + + impl gpui::EventEmitter for StartTracingNotification {} + impl gpui::EventEmitter for StartTracingNotification {} + impl gpui::Focusable for StartTracingNotification { + fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle { + self.focus_handle.clone() + } + } + impl workspace::notifications::Notification for StartTracingNotification {} + + impl Render for StartTracingNotification { + fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + let (title, message) = match &self.status { + TracingStatus::Starting => ( + "Starting Tracy", + "Launching tracy-profiler...".to_string(), + ), + TracingStatus::TracyLaunched => ( + "Tracy Profiler Ready", + "Tracy profiler has been launched. It should automatically connect to this Zed instance and begin capturing performance data.".to_string(), + ), + TracingStatus::TracyNotFound => ( + "Tracy Not Found", + "Could not find `tracy-profiler` on your PATH. Please install Tracy profiler and ensure it's available in your PATH.\n\nOn macOS: brew install tracy\nOn Linux: Install from your package manager or build from source".to_string(), + ), + TracingStatus::TracyLaunchFailed(error) => ( + "Tracy Launch Failed", + format!("Failed to launch tracy-profiler: {}", error), + ), + TracingStatus::ZtracingNotEnabled => ( + "Tracy Support Not Enabled", + "This build of Zed was not compiled with Tracy support. To enable tracing, rebuild Zed with `cargo build --features tracy`.".to_string(), + ), + }; + + let show_debug_warning = + cfg!(debug_assertions) && matches!(self.status, TracingStatus::TracyLaunched); + + NotificationFrame::new() + .with_title(Some(title)) + .show_suppress_button(false) + .on_close(cx.listener(|_, _, _, cx| { + cx.emit(DismissEvent); + })) + .with_content(message) + } + } + + impl StartTracingNotification { + fn new(cx: &mut Context) -> Self { + if !ztracing::is_enabled() { + return Self { + focus_handle: cx.focus_handle(), + status: TracingStatus::ZtracingNotEnabled, + _spawn_task: None, + }; + } + + let spawn_task = cx.spawn(async move |this, cx| { + let tracy_path = cx + .background_spawn(async { which::which("tracy-profiler").ok() }) + .await; + + let status = match tracy_path { + Some(path) => { + let spawn_result = smol::process::Command::new(&path).spawn(); + + match spawn_result { + Ok(_child) => TracingStatus::TracyLaunched, + Err(error) => TracingStatus::TracyLaunchFailed(error.to_string()), + } + } + None => TracingStatus::TracyNotFound, + }; + + this.update(cx, |this, cx| { + this.status = status; + cx.notify(); + }) + .ok(); + }); + + Self { + focus_handle: cx.focus_handle(), + status: TracingStatus::Starting, + _spawn_task: Some(spawn_task), + } + } + } + + workspace.show_notification( + NotificationId::unique::(), + cx, + |cx| cx.new(StartTracingNotification::new), + ); +} + /// Eagerly loads the active theme and icon theme based on the selections in the /// theme settings. /// diff --git a/crates/ztracing/build.rs b/crates/ztracing/build.rs index dc0d0ad704d49c4c0ab639d769024330e10d2481..9233dfa361dd85ef12f5e891a8e03ed4b88d7342 100644 --- a/crates/ztracing/build.rs +++ b/crates/ztracing/build.rs @@ -1,9 +1,3 @@ -use std::env; - fn main() { - if env::var_os("ZTRACING").is_some() { - println!(r"cargo::rustc-cfg=ztracing"); - } println!("cargo::rerun-if-changed=build.rs"); - println!("cargo::rerun-if-env-changed=ZTRACING"); } diff --git a/crates/ztracing/src/lib.rs b/crates/ztracing/src/lib.rs index c9007be1ed43150ef877d51c882aee77845e5bd6..ec5138203fe33e2445ea630f26bd83241500525f 100644 --- a/crates/ztracing/src/lib.rs +++ b/crates/ztracing/src/lib.rs @@ -1,28 +1,28 @@ pub use tracing::{Level, field}; -#[cfg(ztracing)] +#[cfg(feature = "tracy")] pub use tracing::{ Span, debug_span, error_span, event, info_span, instrument, span, trace_span, warn_span, }; -#[cfg(not(ztracing))] +#[cfg(not(feature = "tracy"))] pub use ztracing_macro::instrument; -#[cfg(not(ztracing))] +#[cfg(not(feature = "tracy"))] pub use __consume_all_tokens as trace_span; -#[cfg(not(ztracing))] +#[cfg(not(feature = "tracy"))] pub use __consume_all_tokens as info_span; -#[cfg(not(ztracing))] +#[cfg(not(feature = "tracy"))] pub use __consume_all_tokens as debug_span; -#[cfg(not(ztracing))] +#[cfg(not(feature = "tracy"))] pub use __consume_all_tokens as warn_span; -#[cfg(not(ztracing))] +#[cfg(not(feature = "tracy"))] pub use __consume_all_tokens as error_span; -#[cfg(not(ztracing))] +#[cfg(not(feature = "tracy"))] pub use __consume_all_tokens as event; -#[cfg(not(ztracing))] +#[cfg(not(feature = "tracy"))] pub use __consume_all_tokens as span; -#[cfg(not(ztracing))] +#[cfg(not(feature = "tracy"))] #[macro_export] macro_rules! __consume_all_tokens { ($($t:tt)*) => { @@ -30,10 +30,10 @@ macro_rules! __consume_all_tokens { }; } -#[cfg(not(ztracing))] +#[cfg(not(feature = "tracy"))] pub struct Span; -#[cfg(not(ztracing))] +#[cfg(not(feature = "tracy"))] impl Span { pub fn current() -> Self { Self @@ -44,7 +44,7 @@ impl Span { pub fn record(&self, _t: T, _s: S) {} } -#[cfg(ztracing)] +#[cfg(feature = "tracy")] pub fn init() { zlog::info!("Starting tracy subscriber, you can now connect the profiler"); use tracing_subscriber::prelude::*; @@ -54,5 +54,23 @@ pub fn init() { .expect("setup tracy layer"); } -#[cfg(not(ztracing))] +#[cfg(not(feature = "tracy"))] pub fn init() {} + +/// Returns true if this build was compiled with Tracy profiling support. +/// +/// When true, `init()` will set up the Tracy subscriber and the application +/// can be profiled by connecting Tracy profiler to it. +#[cfg(feature = "tracy")] +pub const fn is_enabled() -> bool { + true +} + +/// Returns true if this build was compiled with Tracy profiling support. +/// +/// When true, `init()` will set up the Tracy subscriber and the application +/// can be profiled by connecting Tracy profiler to it. +#[cfg(not(feature = "tracy"))] +pub const fn is_enabled() -> bool { + false +}