diff options
| author | Michael Anthony Knyszek <mknyszek@google.com> | 2025-02-02 19:50:39 +0000 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2025-10-30 10:08:55 -0700 |
| commit | 7244e9221ff25b0c93a13ad8f1aa8917ca50f697 (patch) | |
| tree | e6fb8794933dca05c72b7571008982815027b0ac /src/runtime/mprof.go | |
| parent | 5ef19c0d0c870cc014ba28aa13b926c13ec1063a (diff) | |
| download | go-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.go | 13 |
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") } |
