Skip to content

Commit 4023ad3

Browse files
hawkwseanmonstar
andauthored
feat: emit and show self-wake counts for tasks (console-rs#112)
This counts all wakes of a task that happen while the task is "currently polled". This could be from `task::yield_now()`, from `tokio::coop`, or any other pattern that triggers the task to wake even though it's currently being polled. This implementation builds upon @seanmonstar's branch console-rs#55. However, rather than detecting self-wakes by simply looking at _whether_ the task is being polled at all, we instead detect them by checking whether the task's span is on the current thread's stack of entered spans. This approach should _not_ produce self-positives when a task is woken by another thread while it is currently entered (as discussed in tokio-rs/console#55 (comment)). Testing this with the "burn" subcommand for the example app, which spawns one task that does a lot of self-wakes via `task::yield`, indicates that it is, in fact, working properly. Here are the stats for the `burn` task: ![image](https://user-images.githubusercontent.com/2796466/132136922-2d0d9fbd-e8eb-475c-8ab8-855f8fce5127.png) Note the very large number of self-wakes. In comparison, here are the task stats for a "normal" task, which does not wake itself (and is instead woken by timers): ![image](https://user-images.githubusercontent.com/2796466/132136964-514d7560-074a-41b1-9d6b-621c0dfac935.png) Note that no self-wakes stat is displayed. Closes console-rs#55. Signed-off-by: Eliza Weisman <[email protected]> Co-authored-by: Sean McArthur <[email protected]>
1 parent 598b498 commit 4023ad3

File tree

7 files changed

+63
-7
lines changed

7 files changed

+63
-7
lines changed

console-subscriber/src/aggregator/mod.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ struct TaskStats {
184184
wakes: u64,
185185
waker_clones: u64,
186186
waker_drops: u64,
187+
self_wakes: u64,
187188
last_wake: Option<SystemTime>,
188189

189190
poll_times_histogram: Histogram<u64>,
@@ -273,6 +274,7 @@ impl Default for TaskStats {
273274
wakes: 0,
274275
waker_clones: 0,
275276
waker_drops: 0,
277+
self_wakes: 0,
276278
last_wake: None,
277279
// significant figures should be in the [0-5] range and memory usage
278280
// grows exponentially with higher a sigfig
@@ -663,10 +665,16 @@ impl Aggregator {
663665
// "wasted" waker ops, but we'll leave that for another time.
664666
if let Some(mut task_stats) = self.task_stats.update(&id) {
665667
match op {
666-
WakeOp::Wake | WakeOp::WakeByRef => {
668+
WakeOp::Wake { self_wake } | WakeOp::WakeByRef { self_wake } => {
667669
task_stats.wakes += 1;
668670
task_stats.last_wake = Some(at);
669671

672+
// If the task has woken itself, increment the
673+
// self-wake count.
674+
if self_wake {
675+
task_stats.self_wakes += 1;
676+
}
677+
670678
// Note: `Waker::wake` does *not* call the `drop`
671679
// implementation, so waking by value doesn't
672680
// trigger a drop event. so, count this as a `drop`
@@ -675,7 +683,7 @@ impl Aggregator {
675683
//
676684
// see
677685
// https://github.com/rust-lang/rust/blob/673d0db5e393e9c64897005b470bfeb6d5aec61b/library/core/src/task/wake.rs#L211-L212
678-
if let WakeOp::Wake = op {
686+
if let WakeOp::Wake { .. } = op {
679687
task_stats.waker_drops += 1;
680688
}
681689
}
@@ -866,6 +874,7 @@ impl ToProto for TaskStats {
866874
total_time: total_time(self.created_at, self.closed_at).map(Into::into),
867875
wakes: self.wakes,
868876
waker_clones: self.waker_clones,
877+
self_wakes: self.self_wakes,
869878
waker_drops: self.waker_drops,
870879
last_wake: self.last_wake.map(Into::into),
871880
}

console-subscriber/src/lib.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,8 @@ enum AttributeUpdateOp {
174174

175175
#[derive(Clone, Debug, Copy, Serialize)]
176176
enum WakeOp {
177-
Wake,
178-
WakeByRef,
177+
Wake { self_wake: bool },
178+
WakeByRef { self_wake: bool },
179179
Clone,
180180
Drop,
181181
}
@@ -398,7 +398,17 @@ where
398398
let at = SystemTime::now();
399399
let mut visitor = WakerVisitor::default();
400400
event.record(&mut visitor);
401-
if let Some((id, op)) = visitor.result() {
401+
if let Some((id, mut op)) = visitor.result() {
402+
if op.is_wake() {
403+
// Are we currently inside the task's span? If so, the task
404+
// has woken itself.
405+
let self_wake = self
406+
.current_spans
407+
.get()
408+
.map(|spans| spans.borrow().iter().any(|span| span == &id))
409+
.unwrap_or(false);
410+
op = op.self_wake(self_wake);
411+
}
402412
self.send(Event::Waker { id, op, at });
403413
}
404414
// else unknown waker event... what to do? can't trace it from here...
@@ -618,3 +628,18 @@ impl proto::instrument::instrument_server::Instrument for Server {
618628
Ok(tonic::Response::new(proto::instrument::ResumeResponse {}))
619629
}
620630
}
631+
632+
impl WakeOp {
633+
/// Returns `true` if `self` is a `Wake` or `WakeByRef` event.
634+
fn is_wake(self) -> bool {
635+
matches!(self, Self::Wake { .. } | Self::WakeByRef { .. })
636+
}
637+
638+
fn self_wake(self, self_wake: bool) -> Self {
639+
match self {
640+
Self::Wake { .. } => Self::Wake { self_wake },
641+
Self::WakeByRef { .. } => Self::WakeByRef { self_wake },
642+
x => x,
643+
}
644+
}
645+
}

console-subscriber/src/stack.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ impl SpanStack {
4949
false
5050
}
5151

52+
pub(crate) fn iter(&self) -> impl Iterator<Item = &Id> {
53+
self.stack
54+
.iter()
55+
.filter_map(|ContextId { id, duplicate }| if *duplicate { None } else { Some(id) })
56+
}
57+
5258
pub(crate) fn stack(&self) -> &Vec<ContextId> {
5359
&self.stack
5460
}

console-subscriber/src/visitors.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,8 @@ impl Visit for WakerVisitor {
243243
fn record_str(&mut self, field: &tracing_core::Field, value: &str) {
244244
if field.name() == "op" {
245245
self.op = Some(match value {
246-
Self::WAKE => WakeOp::Wake,
247-
Self::WAKE_BY_REF => WakeOp::WakeByRef,
246+
Self::WAKE => WakeOp::Wake { self_wake: false },
247+
Self::WAKE_BY_REF => WakeOp::WakeByRef { self_wake: false },
248248
Self::CLONE => WakeOp::Clone,
249249
Self::DROP => WakeOp::Drop,
250250
_ => return,

console/src/tasks.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ struct Stats {
101101

102102
/// The timestamp of when the task was last woken.
103103
last_wake: Option<SystemTime>,
104+
/// Total number of times the task has woken itself.
105+
self_wakes: u64,
104106
}
105107

106108
#[derive(Debug)]
@@ -372,6 +374,11 @@ impl Task {
372374
self.stats.wakes
373375
}
374376

377+
/// Returns the total number of times this task has woken itself.
378+
pub(crate) fn self_wakes(&self) -> u64 {
379+
self.stats.self_wakes
380+
}
381+
375382
fn update(&mut self) {
376383
let completed = self.stats.total.is_some() && self.completed_for == 0;
377384
if completed {
@@ -420,6 +427,7 @@ impl From<proto::tasks::Stats> for Stats {
420427
waker_clones: pb.waker_clones,
421428
waker_drops: pb.waker_drops,
422429
last_wake: pb.last_wake.map(|v| v.try_into().unwrap()),
430+
self_wakes: pb.self_wakes,
423431
}
424432
}
425433
}

console/src/view/task.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ impl TaskView {
131131
Span::from(format!("{} times", task.wakes())),
132132
];
133133

134+
if task.self_wakes() > 0 {
135+
wakeups.push(Span::raw(", "));
136+
wakeups.push(bold("Self Wakes: "));
137+
wakeups.push(Span::from(format!("{} times", task.self_wakes())));
138+
}
139+
134140
// If the task has been woken, add the time since wake to its stats as well.
135141
if let Some(since) = task.since_wake(now) {
136142
wakeups.push(Span::raw(", "));

proto/tasks.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,6 @@ message Stats {
104104
optional google.protobuf.Timestamp last_wake = 6;
105105
// Contains task poll statistics.
106106
common.PollStats poll_stats = 7;
107+
// The total number of times this task has woken itself.
108+
uint64 self_wakes = 8;
107109
}

0 commit comments

Comments
 (0)