euv_ui/hook/profiler/struct.rs
1use super::*;
2
3/// A single recorded measurement.
4///
5/// Pushed onto the `ProfilerHandle`'s entries signal every time
6/// the user calls `ProfilerHandle::measure(label, f)` (or
7/// manually `begin` / `end`). Cheap to `Clone` (the inner
8/// strings are small and `f64` is `Copy`).
9///
10/// Field-level semantics:
11///
12/// - `label`: free-form identifier — typically the call site name
13/// (`"render-list"`, `"fetch-posts"`). Empty strings are
14/// allowed but render as an empty chip in the UI, which makes
15/// misconfigured measurements obvious in a profiler readout.
16/// - `elapsed_ms`: wall-clock time between `begin()` and the
17/// matching `end()` (or the duration of the measured closure),
18/// in milliseconds. Always `>= 0.0` — `begin` is captured
19/// before any user code runs, so the subtraction cannot
20/// underflow.
21/// - `timestamp_ms`: the wall-clock `now_ms()` value at the
22/// instant the entry was recorded (NOT the start of the
23/// measurement). This lets the UI sort / filter entries by
24/// when they were committed, not by when the user started
25/// the timer — which matters when entries are kept around
26/// for "last N measurements" readouts.
27#[derive(Clone, Data, Debug, New, PartialEq)]
28pub struct ProfileEntry {
29 /// The free-form label passed to `measure` / `begin`.
30 pub label: String,
31 /// Duration of the measured operation, in milliseconds.
32 #[get(type(copy))]
33 pub elapsed_ms: f64,
34 /// Wall-clock time at which the entry was recorded.
35 #[get(type(copy))]
36 pub timestamp_ms: f64,
37}
38
39/// A handle to the profiler registered against the current
40/// hook context.
41///
42/// The handle owns the entries signal; calling
43/// `ProfilerHandle::entries()` returns that signal so any
44/// reactive read (`Signal::get()`) inside a closure subscribes
45/// the enclosing render to new entries. The matching
46/// measurement API is `ProfilerHandle::measure(label, f)`
47/// (push-on-exit) or `ProfilerHandle::begin(label)` /
48/// `ProfilerHandle::end()` (split-timer API for code paths
49/// that don't fit inside a single closure).
50///
51/// # Lifecycle
52///
53/// The handle is obtained via `App::use_profiler()` (or
54/// directly via `HookContext::profiler()`), which slots it into
55/// the current hook context. On every render at the same hook
56/// index, the same handle is returned — so measurements
57/// recorded from a previous render remain visible in
58/// `entries()`.
59///
60/// On hook-context teardown (component unmount, match-arm
61/// switch, or explicit `clear()`), the handle is dropped and
62/// its entries signal goes with it. If you need to keep
63/// measurements alive past the lifetime of the component,
64/// clone the entries vector out before the context is cleared.
65#[derive(Clone, Data, New)]
66pub struct ProfilerHandle {
67 /// The reactive log of measurements. Every measurement
68 /// pushes a fresh `ProfileEntry` into this vector via
69 /// `.set(...)` — the `set` triggers the reactive update
70 /// path, so any subscriber re-renders.
71 pub(crate) entries: Signal<Vec<ProfileEntry>>,
72}
73
74/// A `begin()` marker — RAII guard that records the start
75/// timestamp and the label so the matching `end()` call can
76/// compute the elapsed time.
77///
78/// Created by `ProfilerHandle::begin(label)`. Consume with
79/// `end()` to push a `ProfileEntry` into the entries signal.
80/// Dropping the marker without calling `end()` discards the
81/// measurement silently (we don't have a place to push a
82/// half-finished entry, and panicking on drop is hostile).
83#[derive(Data, New)]
84pub struct ProfilerMark {
85 /// The label this marker was created with. Copied out of
86 /// the `&str` at construction time so the marker does not
87 /// outlive any borrowed string.
88 pub(crate) label: String,
89 /// Wall-clock timestamp captured at `begin()`. Subtracted
90 /// from the `end()` timestamp to compute `elapsed_ms`.
91 pub(crate) started_ms: f64,
92 /// Back-reference to the entries signal. Cloned (cheap —
93 /// `Signal<T>` is `Copy`-by-pointer) so `end()` can push
94 /// without re-borrowing the handle.
95 pub(crate) entries: Signal<Vec<ProfileEntry>>,
96}