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
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 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,
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::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::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),
}
}
}