Add dev::StartTracing action to launch Tracy profiler

- Add is_enabled() function to ztracing crate to detect if tracy support is compiled in
- Add StartTracing action that launches tracy-profiler if found on PATH
- Show appropriate notifications for success/failure states
- Show warning with icon when running debug build (profiling results won't be accurate)
- Simplify ztracing to use just --features tracy instead of requiring ZTRACING env var
This commit is contained in:
Richard Feldman
2025-12-24 11:50:01 -05:00
parent 4b56fec971
commit bf87da731f
5 changed files with 174 additions and 20 deletions

1
Cargo.lock generated
View File

@@ -20786,6 +20786,7 @@ dependencies = [
"watch",
"web_search",
"web_search_providers",
"which 6.0.3",
"which_key",
"windows 0.61.3",
"winresource",

View File

@@ -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

View File

@@ -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,139 @@ fn capture_recent_audio(workspace: &mut Workspace, _: &mut Window, cx: &mut Cont
);
}
fn start_tracing(workspace: &mut Workspace, _window: &mut Window, cx: &mut Context<Workspace>) {
struct StartTracingNotification {
focus_handle: gpui::FocusHandle,
status: TracingStatus,
_spawn_task: Option<Task<()>>,
}
enum TracingStatus {
Starting,
TracyLaunched,
TracyNotFound,
TracyLaunchFailed(String),
ZtracingNotEnabled,
}
impl gpui::EventEmitter<DismissEvent> for StartTracingNotification {}
impl gpui::EventEmitter<SuppressEvent> 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<Self>) -> 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(
v_flex()
.gap_2()
.child(Label::new(message).size(ui::LabelSize::Small))
.when(show_debug_warning, |this| {
this.child(
h_flex()
.gap_1()
.child(
ui::Icon::new(ui::IconName::Warning)
.size(ui::IconSize::Small)
.color(ui::Color::Warning),
)
.child(
Label::new(
"This is a debug build of Zed, which will be much slower than a release build.",
)
.size(ui::LabelSize::Small)
.color(ui::Color::Warning),
),
)
}),
)
}
}
impl StartTracingNotification {
fn new(cx: &mut Context<Self>) -> 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::<StartTracingNotification>(),
cx,
|cx| cx.new(StartTracingNotification::new),
);
}
/// Eagerly loads the active theme and icon theme based on the selections in the
/// theme settings.
///

View File

@@ -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");
}

View File

@@ -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<T, S>(&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
}