1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
use std::fmt;
/// Which bus carried a publication.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Bus {
/// Every window hears it.
Global,
/// One window's own.
Window,
}
/// How a stored value changed.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum StoreOp {
Set,
Delete,
/// Everything under the path went at once.
DeletePrefix,
}
/// An observable point, with what identifies it.
///
/// Names are short type names: `processes::Kill`, not a crate path.
#[derive(Clone, Debug, PartialEq)]
pub enum Point {
/// The UI asked a feature for something.
Action { message: &'static str },
/// A message was queued for an actor.
Send {
actor: &'static str,
message: &'static str,
},
/// An actor handled a message.
Handle {
actor: &'static str,
message: &'static str,
},
/// An actor started background work whose result will come back as
/// `output`. `actor` is the address it will come back to, by the id the
/// snapshot lists it under: what a task belongs to is what that actor
/// belongs to.
Spawn {
actor: &'static str,
actor_id: u64,
output: &'static str,
},
/// Background work finished, and its result is on its way to the actor.
Settled {
actor: &'static str,
actor_id: u64,
output: &'static str,
took_us: u64,
},
/// Background work was dropped where it last awaited, because the actor
/// that started it is gone. Nothing comes back.
Cancelled {
actor: &'static str,
actor_id: u64,
output: &'static str,
took_us: u64,
},
/// An actor opened a source whose items will come to it as `output`.
/// Recorded under what opened it, which is done with it once it is open:
/// the items are not its work.
Source {
actor: &'static str,
actor_id: u64,
output: &'static str,
},
/// An item came from a source, as `output`. A root, as a timer's tick
/// is; `source` is the id of the [`Point::Source`] it came from.
Arrived {
actor: &'static str,
actor_id: u64,
output: &'static str,
source: u64,
},
/// A source ended: it ran dry, or `gone` - the actor that opened it is
/// gone and the source was dropped where it last awaited.
Closed {
actor: &'static str,
actor_id: u64,
output: &'static str,
took_us: u64,
gone: bool,
},
/// An event went out.
Publish {
event: &'static str,
bus: Bus,
subscribers: usize,
},
/// An event reached a subscriber that is not an actor.
Deliver { event: &'static str, bus: Bus },
/// A reducer was changed.
Push { reducer: &'static str },
/// A router moved.
Navigate { root: String, to: String },
/// A timer fired; `timer` is its id, as the application's timers list
/// it.
Tick { timer: u64 },
/// A persisted value changed.
Store {
op: StoreOp,
path: String,
/// The declared field the path belongs to, `Settings.theme`, when
/// the store knows it.
field: Option<String>,
/// Whether the change came from outside the process, an edited file.
outside: bool,
},
/// A page or layout drew itself, and how long that took. Recorded only
/// for the frames worth looking at - see `devtools::rendering`.
Render {
segment: &'static str,
took_us: u64,
},
/// An ordinary `tracing` event the application wrote.
Log {
level: tracing::Level,
target: &'static str,
/// Where it was written, as the compiler named the file: relative to
/// the workspace root for the application's own crates.
file: Option<&'static str>,
line: Option<u32>,
module: Option<&'static str>,
/// The message, then the other fields as `name=value`.
text: String,
},
/// A `tracing` span the application opened - an `#[instrument]`ed
/// function, say. Open until the span closes, and current while it is
/// entered; what it took is the time it was entered, not the time it
/// waited in between.
Span {
name: &'static str,
/// The level it was opened at, as [`Point::Log`]'s.
level: tracing::Level,
target: &'static str,
/// Where it was written; see [`Point::Log`].
file: Option<&'static str>,
line: Option<u32>,
module: Option<&'static str>,
/// Its fields as `name=value`, as they were when it opened.
fields: String,
},
/// Anything else worth a line.
Note(String),
}
impl Point {
/// A short name for the kind of point, for filtering.
pub fn kind(&self) -> &'static str {
match self {
Point::Action { .. } => "action",
Point::Send { .. } => "send",
Point::Handle { .. } => "handle",
Point::Spawn { .. } => "spawn",
Point::Settled { .. } => "settled",
Point::Cancelled { .. } => "cancelled",
Point::Source { .. } => "source",
Point::Arrived { .. } => "arrived",
Point::Closed { .. } => "closed",
Point::Publish { .. } => "publish",
Point::Deliver { .. } => "deliver",
Point::Push { .. } => "push",
Point::Navigate { .. } => "navigate",
Point::Tick { .. } => "tick",
Point::Store { .. } => "store",
Point::Render { .. } => "render",
Point::Log { .. } => "log",
Point::Span { .. } => "span",
Point::Note(_) => "note",
}
}
}
impl fmt::Display for Bus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Bus::Global => "global",
Bus::Window => "window",
})
}
}
impl fmt::Display for StoreOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
StoreOp::Set => "set",
StoreOp::Delete => "delete",
StoreOp::DeletePrefix => "delete_prefix",
})
}
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Point::Action { message } => write!(f, "action {message}"),
Point::Send { actor, message } => write!(f, "send {message} → {actor}"),
Point::Handle { actor, message, .. } => write!(f, "{actor} handles {message}"),
Point::Spawn { actor, output, .. } => write!(f, "{actor} starts work for {output}"),
Point::Settled {
actor,
output,
took_us,
..
} => write!(
f,
"{actor} has its {output} after {:.1} ms",
*took_us as f64 / 1000.0
),
Point::Cancelled {
actor,
output,
took_us,
..
} => write!(
f,
"{actor} is gone: {output} cancelled after {:.1} ms",
*took_us as f64 / 1000.0
),
Point::Source { actor, output, .. } => write!(f, "{actor} opens a source of {output}"),
Point::Arrived { actor, output, .. } => write!(f, "{output} arrives at {actor}"),
Point::Closed {
actor,
output,
took_us,
gone: true,
..
} => write!(
f,
"{actor} is gone: its source of {output} closed after {:.1} ms",
*took_us as f64 / 1000.0
),
Point::Closed {
actor,
output,
took_us,
..
} => write!(
f,
"{actor}'s source of {output} ran dry after {:.1} ms",
*took_us as f64 / 1000.0
),
Point::Publish {
event,
bus,
subscribers,
} => write!(f, "publish {event} on the {bus} bus to {subscribers}"),
Point::Deliver { event, bus } => write!(f, "deliver {event} from the {bus} bus"),
Point::Push { reducer } => write!(f, "push into {reducer}"),
Point::Navigate { root, to } => write!(f, "{root} navigates to {to}"),
Point::Tick { timer } => write!(f, "timer #{timer}"),
Point::Render { segment, took_us } => {
write!(f, "{segment} drew itself in {:.1} ms", *took_us as f64 / 1000.0)
}
Point::Store {
op,
path,
field,
outside,
} => {
let who = if *outside { "disk" } else { "store" };
let verb = match op {
StoreOp::Set => "sets",
StoreOp::Delete => "deletes",
StoreOp::DeletePrefix => "clears",
};
write!(f, "{who} {verb} {path}")?;
match field {
Some(field) => write!(f, " ({field})"),
None => Ok(()),
}
}
Point::Log {
level,
target,
text,
..
} => write!(f, "{level} {target}: {text}"),
Point::Span { name, fields, .. } if fields.is_empty() => f.write_str(name),
Point::Span { name, fields, .. } => write!(f, "{name} {fields}"),
Point::Note(text) => f.write_str(text),
}
}
}