diff options
Diffstat (limited to 'profiling')
-rw-r--r-- | profiling/Cargo.toml | 15 | ||||
-rw-r--r-- | profiling/README.md | 61 | ||||
-rw-r--r-- | profiling/src/lib.rs | 100 |
3 files changed, 176 insertions, 0 deletions
diff --git a/profiling/Cargo.toml b/profiling/Cargo.toml new file mode 100644 index 00000000..29ee8c9c --- /dev/null +++ b/profiling/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "iced_profiling" +authors = ["Bingus <shankern@protonmail.com>"] +version = "0.1.0" +edition = "2021" +description = "Profiling backend implementations for Iced" + +[dependencies] +tracing = { version = "0.1.37", default-features = false, features = ["std"] } +tracing-core = "0.1.30" +tracing-subscriber = { version = "0.3.16", features = ["registry", "env-filter"] } + +[dependencies.tracing-chrome] +version = "0.7.0" +optional = true diff --git a/profiling/README.md b/profiling/README.md new file mode 100644 index 00000000..f150bcec --- /dev/null +++ b/profiling/README.md @@ -0,0 +1,61 @@ +# `iced_profiling` +[] +[](https://crates.io/crates/iced_profiling) +[](https://github.com/iced-rs/iced/blob/master/LICENSE) +[](https://discord.gg/3xZJ65GAhd) + +`iced_profiling` is a crate which implements various tracing backends for Iced. + +It relies on the [tracing](https://crates.io/crates/tracing) crate to collect diagnostics. We currently only support +tracing with `tracing`'s `info_span!` macro, but will consider different logging levels in the future. PRs welcome! + +## Trace backends + +We currently support only Chrome JSON traces using the [tracing-chrome](https://crates.io/crates/tracing-chrome) crate. + +There are plans to add support for [Tracy](https://github.com/wolfpld/tracy) in the near future! + +## Generating a trace file + +### Using Iced's `Application` + +Simply enable your tracing backend of choice (e.g. `trace_chrome`) feature in Iced & run your project. + +```shell +cargo run --features iced/trace_chrome +``` +### Standalone dependency + +You can enable tracing by enabling your tracing backend of choice as a feature of `iced_profiling`. + +```toml +iced_profiling = { version = "0.1.0", features = ["tracing-chrome"]} +``` + +Doing so will require you to initialize the profiler manually like so: + +```rust +let _guard = iced_profiling::init(); +``` + +This reference must be kept alive for the entire duration of your application that you wish to profile. + +## Chrome + +By default, Chrome trace files will be generated in the current working directory: +```shell +path/to/your/project/project_trace_{timestamp}.json +``` + +You also set a specific path by setting the `CHROME_TRACE_FILE` env variable: +```shell +CHROME_TRACE_FILE = ~/Desktop/trace.json cargo run +``` + +If you cannot find your trace file, there may have been a permission issue when trying to generate your file. Be sure to check your cargo manifest directory! + +Once your file is generated, you can view it in Google Chrome at either [ui.perfetto.dev](ui.perfetto.dev) (new) or [chrome://trace](chrome://trace) (old). + +<p align="center"> + <img alt="The native target" src="../docs/images/perfetto.png" width="80%"> +</p>
\ No newline at end of file diff --git a/profiling/src/lib.rs b/profiling/src/lib.rs new file mode 100644 index 00000000..8fc58fb8 --- /dev/null +++ b/profiling/src/lib.rs @@ -0,0 +1,100 @@ +use std::time::Duration; +use tracing_subscriber::prelude::*; +use tracing_subscriber::Registry; + +#[cfg(feature = "tracing-chrome")] +use { + tracing_chrome::FlushGuard, + tracing_subscriber::fmt::{format::DefaultFields, FormattedFields}, +}; + +pub use tracing::{info_span, instrument}; + +/// Profiler state. This will likely need to be updated or reworked when adding new tracing backends. +pub struct Profiler { + #[cfg(feature = "tracing-chrome")] + /// [`FlushGuard`] must not be dropped until the application scope is dropped for accurate tracing. + _guard: FlushGuard, +} + +pub fn init() -> Profiler { + // Registry stores the spans & generates unique span IDs + let subscriber = Registry::default(); + + #[cfg(feature = "tracing-chrome")] + let (chrome_layer, guard) = { + let mut layer = tracing_chrome::ChromeLayerBuilder::new(); + + // Optional configurable env var: CHROME_TRACE_FILE=/path/to/trace_file/file.json, + // for uploading to chrome://tracing (old) or ui.perfetto.dev (new). + if let Ok(path) = std::env::var("CHROME_TRACE_FILE") { + layer = layer.file(path); + } else if let Ok(current_dir) = std::env::current_dir() { + let time = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or(Duration::from_millis(0)) + .as_millis(); + + let trace_file_name = current_dir + .file_name() + .map(|file_dir| { + format!( + "{}_trace_{}.json", + file_dir.to_str().unwrap_or("trace"), + time + ) + }) + .unwrap_or_else(|| "trace.json".to_string()); + + let path = format!( + "{}/{}", + current_dir.to_str().expect("Invalid path"), + trace_file_name + ); + + layer = layer.file(path); + } else { + layer = layer.file(env!("CARGO_MANIFEST_DIR")) + } + + let (chrome_layer, guard) = layer + .name_fn(Box::new(|event_or_span| match event_or_span { + tracing_chrome::EventOrSpan::Event(event) => { + event.metadata().name().into() + } + tracing_chrome::EventOrSpan::Span(span) => { + if let Some(fields) = span + .extensions() + .get::<FormattedFields<DefaultFields>>() + { + format!( + "{}: {}", + span.metadata().name(), + fields.fields.as_str() + ) + } else { + span.metadata().name().into() + } + } + })) + .build(); + + (chrome_layer, guard) + }; + + let fmt_layer = tracing_subscriber::fmt::Layer::default(); + let subscriber = subscriber.with(fmt_layer); + + #[cfg(feature = "tracing-chrome")] + let subscriber = subscriber.with(chrome_layer); + + // create dispatcher which will forward span events to the subscriber + // this can only be set once or will panic + tracing::subscriber::set_global_default(subscriber) + .expect("Profiler could not set the global default subscriber."); + + Profiler { + #[cfg(feature = "tracing-chrome")] + _guard: guard, + } +} |