Initial profiling support for Iced.
This commit is contained in:
parent
ba20ac8e49
commit
c5cd236b73
20 changed files with 357 additions and 35 deletions
15
profiling/Cargo.toml
Normal file
15
profiling/Cargo.toml
Normal file
|
|
@ -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
|
||||
61
profiling/README.md
Normal file
61
profiling/README.md
Normal file
|
|
@ -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>
|
||||
100
profiling/src/lib.rs
Normal file
100
profiling/src/lib.rs
Normal file
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue