aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/trace2runtime.go
diff options
context:
space:
mode:
authorMichael Anthony Knyszek <mknyszek@google.com>2024-02-20 21:16:33 +0000
committerGopher Robot <gobot@golang.org>2024-03-22 16:12:01 +0000
commitc9c88d73f5cb58d0e40cb1b0481c102e6b8b24f1 (patch)
treea5b170d5fbb4fb4ee00d72eb14bffdcc6e6bf027 /src/runtime/trace2runtime.go
parent27f41bb15391668fa8ba18561efe364bab9b8312 (diff)
downloadgo-c9c88d73f5cb58d0e40cb1b0481c102e6b8b24f1.tar.xz
runtime: add tracing for iter.Pull
This change resolves a TODO in the coroutine switch implementation (used exclusively by iter.Pull at the moment) to enable tracing. This was blocked on eliminating the atomic load in the tracer's "off" path (completed in the previous CL in this series) and the addition of new tracer events to minimize the overhead of tracing in this circumstance. This change introduces 3 new event types to support coroutine switches: GoCreateBlocked, GoSwitch, and GoSwitchDestroy. GoCreateBlocked needs to be introduced because the goroutine created for the coroutine starts out in a blocked state. There's no way to represent this in the tracer right now, so we need a new event for it. GoSwitch represents the actual coroutine switch, which conceptually consists of a GoUnblock, a GoBlock, and a GoStart event in series (unblocking the next goroutine to run, blocking the current goroutine, and then starting the next goroutine to run). GoSwitchDestroy is closely related to GoSwitch, implementing the same semantics except that GoBlock is replaced with GoDestroy. This is used when exiting the coroutine. The implementation of all this is fairly straightforward, and the trace parser simply translates GoSwitch* into the three constituent events. Because GoSwitch and GoSwitchDestroy imply a GoUnblock and a GoStart, they need to synchronize with other past and future GoStart events to create a correct partial ordering in the trace. Therefore, these events need a sequence number for the goroutine that will be unblocked and started. Also, while implementing this, I noticed that the coroutine implementation is actually buggy with respect to LockOSThread. In fact, it blatantly disregards its invariants without an explicit panic. While such a case is likely to be rare (and inefficient!) we should decide how iter.Pull behaves with respect to runtime.LockOSThread. Lastly, this change also bumps the trace version from Go 1.22 to Go 1.23. We're adding events that are incompatible with a Go 1.22 parser, but Go 1.22 traces are all valid Go 1.23 traces, so the newer parser supports both (and the CL otherwise updates the Go 1.22 definitions of events and such). We may want to reconsider the structure and naming of some of these packages though; it could quickly get confusing. For #61897. Change-Id: I96897a46d5852c02691cde9f957dc6c13ef4d8e7 Reviewed-on: https://go-review.googlesource.com/c/go/+/565937 Reviewed-by: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Michael Knyszek <mknyszek@google.com>
Diffstat (limited to 'src/runtime/trace2runtime.go')
-rw-r--r--src/runtime/trace2runtime.go40
1 files changed, 33 insertions, 7 deletions
diff --git a/src/runtime/trace2runtime.go b/src/runtime/trace2runtime.go
index 7b88c258ba..b391fd79ff 100644
--- a/src/runtime/trace2runtime.go
+++ b/src/runtime/trace2runtime.go
@@ -389,9 +389,13 @@ func (tl traceLocker) GCMarkAssistDone() {
}
// GoCreate emits a GoCreate event.
-func (tl traceLocker) GoCreate(newg *g, pc uintptr) {
+func (tl traceLocker) GoCreate(newg *g, pc uintptr, blocked bool) {
newg.trace.setStatusTraced(tl.gen)
- tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvGoCreate, traceArg(newg.goid), tl.startPC(pc), tl.stack(2))
+ ev := traceEvGoCreate
+ if blocked {
+ ev = traceEvGoCreateBlocked
+ }
+ tl.eventWriter(traceGoRunning, traceProcRunning).commit(ev, traceArg(newg.goid), tl.startPC(pc), tl.stack(2))
}
// GoStart emits a GoStart event.
@@ -442,14 +446,36 @@ func (tl traceLocker) GoPark(reason traceBlockReason, skip int) {
func (tl traceLocker) GoUnpark(gp *g, skip int) {
// Emit a GoWaiting status if necessary for the unblocked goroutine.
w := tl.eventWriter(traceGoRunning, traceProcRunning)
- if !gp.trace.statusWasTraced(tl.gen) && gp.trace.acquireStatus(tl.gen) {
- // Careful: don't use the event writer. We never want status or in-progress events
- // to trigger more in-progress events.
- w.w = w.w.writeGoStatus(gp.goid, -1, traceGoWaiting, gp.inMarkAssist)
- }
+ // Careful: don't use the event writer. We never want status or in-progress events
+ // to trigger more in-progress events.
+ w.w = emitUnblockStatus(w.w, gp, tl.gen)
w.commit(traceEvGoUnblock, traceArg(gp.goid), gp.trace.nextSeq(tl.gen), tl.stack(skip))
}
+// GoCoroswitch emits a GoSwitch event. If destroy is true, the calling goroutine
+// is simultaneously being destroyed.
+func (tl traceLocker) GoSwitch(nextg *g, destroy bool) {
+ // Emit a GoWaiting status if necessary for the unblocked goroutine.
+ w := tl.eventWriter(traceGoRunning, traceProcRunning)
+ // Careful: don't use the event writer. We never want status or in-progress events
+ // to trigger more in-progress events.
+ w.w = emitUnblockStatus(w.w, nextg, tl.gen)
+ ev := traceEvGoSwitch
+ if destroy {
+ ev = traceEvGoSwitchDestroy
+ }
+ w.commit(ev, traceArg(nextg.goid), nextg.trace.nextSeq(tl.gen))
+}
+
+// emitUnblockStatus emits a GoStatus GoWaiting event for a goroutine about to be
+// unblocked to the trace writer.
+func emitUnblockStatus(w traceWriter, gp *g, gen uintptr) traceWriter {
+ if !gp.trace.statusWasTraced(gen) && gp.trace.acquireStatus(gen) {
+ w = w.writeGoStatus(gp.goid, -1, traceGoWaiting, gp.inMarkAssist)
+ }
+ return w
+}
+
// GoSysCall emits a GoSyscallBegin event.
//
// Must be called with a valid P.