Commit d5f4dc1
authored
feat(executor): instrument tasks for tokio-console (#987)
* feat(executor): give tasks a runtime.spawn span
tokio-console collects its data through tracing spans and events that
follow a fixed naming convention; it is not tied to tokio's internals, so
any executor emitting the same spans and events can be observed with it.
Behind a new `console` feature, give every task a `runtime.spawn` span,
entered while the task is polled. That is enough for the console to report
poll counts, busy/idle/scheduled times and the poll time histograms.
The instrumentation lives in the new `console` module, which has an
enabled and a disabled variant. The disabled one is what is compiled
without the feature: `TaskSpan` becomes zero-sized, so the task header
keeps its layout, and every method an empty inlined function.
The span sits in that header, so it is dropped along with the task
allocation, during a panic as well. `drop_future` leaks the future while
unwinding, since dropping it could panic a second time, but leaking the
span would leave the task running in the console forever. The subscriber is
therefore reentered while unwinding, where a panic inside it aborts.
Since the span records where the task was spawned, `Executor::spawn`
becomes `#[track_caller]`. Wrappers around it want the console to blame
their own caller instead of themselves, so also add `spawn_at`, taking the
`SpawnMeta` to attribute the task to.
Adding `tracing` to the workspace also lets `compio-log` take it from there
rather than pinning a version of its own.
* feat(executor): emit runtime::waker events
Emit a `runtime::waker` event from every waker operation of a task, which
is what the console needs to report waker counts and to run its self-wake
and lost-waker lints.
The `op` values the console expects are collected in the `console` module,
next to a note on why `Waker::wake` must not report a drop of its own.
* feat(executor): instrument the future blocked on
The future passed to `block_on` is driven by the runtime instead of being
a task of the executor, so it is invisible to the console although it is
usually the most interesting future of the application.
Add `console::instrument_block_on`, wrapping it into a future that owns a
`block_on` task span. Its waker belongs to the caller of `block_on` rather
than to a task, so wrap it too, in a shim reporting the waker operations
the console expects.
Without the `console` feature the wrapper is the identity function.
* test(executor): assert the console instrumentation
Record the spans and events with a subscriber doing what
`console-subscriber` does, and assert on what it saw: the fields of the
task spans, the poll counts, and that the waker operations of a task
balance out, which is what the console's lost-waker lint looks at.
Run the new test in CI, under miri as well, since it exercises the waker
vtables of both the tasks and the `block_on` shim.
* feat(executor): report blocking closures as blocking tasks
The console treats tasks whose `kind` is `blocking` or `block_on` as not
being driven by a future, and skips the four lints that only make sense for
one: self-wake ratio, lost waker, never-yielded and large future.
`spawn_blocking` produces exactly the kind of task those lints misjudge, and
reports the wrong times on top of that: the task is the future waiting for
the pool, so all of the time in the closure counts as idle and none as busy.
Instrument the closure instead of the future waiting for it. The span is
created on the spawning thread, so the wait for a worker is reported as idle
time, and entered around the closure, so its time is reported as busy. The
future is then left unreported, since it stands for work that is already
accounted for.
* feat(executor): let a task be named
The console gives `task.name` a column of its own, and leaves it empty for
the tasks that do not have it. It is worth setting for the tasks a user did
not spawn themselves, since the location of those points into compio rather
than at the code that asked for the work.
That is the case for every wrapper around `spawn` that is an `async fn`,
since `#[track_caller]` is a no-op on those and a `SpawnMeta` cannot be
forwarded through them, so note that in the limitations as well.
Add `SpawnMeta::named` for the tasks that can make up for it. The crates
that spawn tasks of their own name them in the commits that follow, one
per crate.
* test(executor): assert the console variants present one surface
The `console` module has an enabled and a disabled variant of every type it
exports, and only one of them is ever compiled. The disabled one is what
nearly every build uses, so a difference between the two surfaces reaches
whoever turns the feature on, in code written long after the difference.
Assert that they match, by coercing each item to a function pointer, which
pins its whole signature, and by naming the traits the rest of the crate
relies on.
The guard returned by entering a task's span needs more than a signature:
the enabled one borrows the span, since the console measures the time the
span is entered as the busy time of the task. A disabled guard that owned
itself would let code hold it past the span it is timing and compile, and
only fail once the feature is turned on. Both variants therefore name the
guard through a `EnterGuard<'a>` alias, which an owned guard cannot fill.
* feat(executor): let the task a runtime blocks on be named
`instrument_block_on` captured the caller itself, so a `block_on` task could
only ever be reported by the location of the call. That is enough for the
runtime a user blocks on themselves, but not for the ones started on their
behalf, which all report the same line inside compio.
Take a `SpawnMeta` like the other spawns do, so that a caller can pass one.
* docs(executor): correct what the console does with a task's kind
The console does not treat every kind other than `task` as one it does not
drive itself: it knows `blocking` and `block_on` by name, and lints a task
of any other kind, including one it does not know, as a future of its own.
Record the nightly escape hatch for the attribution of an `async fn` too.
* feat(executor): instrument the future a compatibility layer executes
`compio-compat`'s `execute` drives the executor from a foreign event loop,
the way `block_on` drives it from a loop of its own. It is the same kind of
task, so report it as one, under a name that says what it instruments.1 parent c87c320 commit d5f4dc1
11 files changed
Lines changed: 1211 additions & 9 deletions
File tree
- compio-executor
- src
- console
- task
- tests
- compio-log
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
| 75 | + | |
75 | 76 | | |
76 | 77 | | |
77 | 78 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
23 | 26 | | |
24 | 27 | | |
25 | 28 | | |
26 | 29 | | |
27 | 30 | | |
| 31 | + | |
28 | 32 | | |
29 | 33 | | |
30 | 34 | | |
| |||
33 | 37 | | |
34 | 38 | | |
35 | 39 | | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
36 | 43 | | |
37 | 44 | | |
38 | 45 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 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 | + | |
0 commit comments