aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/mprof.go
diff options
context:
space:
mode:
authorMichael Anthony Knyszek <mknyszek@google.com>2025-02-02 19:50:39 +0000
committerGopher Robot <gobot@golang.org>2025-10-30 10:08:55 -0700
commit7244e9221ff25b0c93a13ad8f1aa8917ca50f697 (patch)
treee6fb8794933dca05c72b7571008982815027b0ac /src/runtime/mprof.go
parent5ef19c0d0c870cc014ba28aa13b926c13ec1063a (diff)
downloadgo-7244e9221ff25b0c93a13ad8f1aa8917ca50f697.tar.xz
runtime: eliminate _Psyscall
This change eliminates the _Psyscall state by using synchronization on the G status _Gsyscall to make syscalls work instead. This removes an atomic Store and an atomic CAS on the syscall path, which reduces syscall and cgo overheads. It also simplifies the syscall paths quite a bit. The one danger with this change is that we have a new combination of states that was previously impossible. There are brief windows where it's possible to observe a goroutine in _Grunning but without a P. This change is careful to hide this detail from the execution tracer, but it may have unexpected effects in the rest of the runtime, making this change somewhat risky. goos: linux goarch: amd64 pkg: internal/runtime/cgobench cpu: AMD EPYC 7B13 │ before.out │ after.out │ │ sec/op │ sec/op vs base │ CgoCall-64 43.69n ± 1% 35.83n ± 1% -17.99% (p=0.002 n=6) CgoCallParallel-64 5.306n ± 1% 5.338n ± 1% ~ (p=0.132 n=6) Change-Id: I4551afc1eea0c1b67a0b2dd26b0d49aa47bf1fb8 Reviewed-on: https://go-review.googlesource.com/c/go/+/646198 Auto-Submit: Michael Knyszek <mknyszek@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com>
Diffstat (limited to 'src/runtime/mprof.go')
-rw-r--r--src/runtime/mprof.go13
1 files changed, 12 insertions, 1 deletions
diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go
index 19e90fbb33..d745d5f5b9 100644
--- a/src/runtime/mprof.go
+++ b/src/runtime/mprof.go
@@ -1535,7 +1535,18 @@ func doRecordGoroutineProfile(gp1 *g, pcbuf []uintptr) {
// everything else, we just don't record the stack in the profile.
return
}
- if readgstatus(gp1) == _Grunning {
+ // Double-check that we didn't make a grave mistake. If the G is running then in
+ // general, we cannot safely read its stack.
+ //
+ // However, there is one case where it's OK. There's a small window of time in
+ // exitsyscall where a goroutine could be in _Grunning as it's exiting a syscall.
+ // This is OK because goroutine will not exit the syscall until it passes through
+ // a call to tryRecordGoroutineProfile. (An explicit one on the fast path, an
+ // implicit one via the scheduler on the slow path.)
+ //
+ // This is also why it's safe to check syscallsp here. The syscall path mutates
+ // syscallsp only after passing through tryRecordGoroutineProfile.
+ if readgstatus(gp1) == _Grunning && gp1.syscallsp == 0 {
print("doRecordGoroutineProfile gp1=", gp1.goid, "\n")
throw("cannot read stack of running goroutine")
}