aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/export_test.go10
-rw-r--r--src/runtime/metrics/doc.go5
-rw-r--r--src/runtime/runtime-gdb_test.go3
-rw-r--r--src/runtime/symtab.go5
-rw-r--r--src/runtime/sys_linux_s390x.s4
-rw-r--r--src/runtime/testdata/testprognet/signalexec.go43
-rw-r--r--src/runtime/trace.go2
-rw-r--r--src/runtime/traceevent.go2
-rw-r--r--src/runtime/tracestack.go8
-rw-r--r--src/runtime/tracestack_test.go46
10 files changed, 118 insertions, 10 deletions
diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index b3bb5d2c58..81542deb59 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -1919,3 +1919,13 @@ const (
BubbleAssocCurrentBubble = bubbleAssocCurrentBubble
BubbleAssocOtherBubble = bubbleAssocOtherBubble
)
+
+type TraceStackTable traceStackTable
+
+func (t *TraceStackTable) Reset() {
+ t.tab.reset()
+}
+
+func TraceStack(gp *G, tab *TraceStackTable) {
+ traceStack(0, gp, (*traceStackTable)(tab))
+}
diff --git a/src/runtime/metrics/doc.go b/src/runtime/metrics/doc.go
index 32fc436e1a..a1902bc6d7 100644
--- a/src/runtime/metrics/doc.go
+++ b/src/runtime/metrics/doc.go
@@ -230,6 +230,11 @@ Below is the full list of supported metrics, ordered lexicographically.
/gc/stack/starting-size:bytes
The stack size of new goroutines.
+ /godebug/non-default-behavior/allowmultiplevcs:events
+ The number of non-default behaviors executed by the cmd/go
+ package due to a non-default GODEBUG=allowmultiplevcs=...
+ setting.
+
/godebug/non-default-behavior/asynctimerchan:events
The number of non-default behaviors executed by the time package
due to a non-default GODEBUG=asynctimerchan=... setting.
diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go
index 19ad29c127..47c1fe5851 100644
--- a/src/runtime/runtime-gdb_test.go
+++ b/src/runtime/runtime-gdb_test.go
@@ -78,6 +78,9 @@ func checkGdbVersion(t *testing.T) {
if major < 10 {
t.Skipf("skipping: gdb version %d.%d too old", major, minor)
}
+ if major < 12 || (major == 12 && minor < 1) {
+ t.Logf("gdb version <12.1 is known to crash due to a SIGWINCH recieved in non-interactive mode; if you see a crash, some test may be sending SIGWINCH to the whole process group. See go.dev/issue/58932.")
+ }
t.Logf("gdb version %d.%d", major, minor)
}
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index 8c6ef2b4fc..866c46a83d 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -981,6 +981,9 @@ func pcvalue(f funcInfo, off uint32, targetpc uintptr, strict bool) (int32, uint
// matches the cached contents.
const debugCheckCache = false
+ // If true, skip checking the cache entirely.
+ const skipCache = false
+
if off == 0 {
return -1, 0
}
@@ -991,7 +994,7 @@ func pcvalue(f funcInfo, off uint32, targetpc uintptr, strict bool) (int32, uint
var checkVal int32
var checkPC uintptr
ck := pcvalueCacheKey(targetpc)
- {
+ if !skipCache {
mp := acquirem()
cache := &mp.pcvalueCache
// The cache can be used by the signal handler on this M. Avoid
diff --git a/src/runtime/sys_linux_s390x.s b/src/runtime/sys_linux_s390x.s
index 2f9d4beda8..a3472a4508 100644
--- a/src/runtime/sys_linux_s390x.s
+++ b/src/runtime/sys_linux_s390x.s
@@ -226,7 +226,7 @@ TEXT runtime·walltime(SB),NOSPLIT,$32-12
MOVD R4, 24(R15)
MOVD R14, R8 // Backup return address
- MOVD $sec+0(FP), R4 // return parameter caller
+ MOVD $ret-8(FP), R4 // caller's SP
MOVD R8, m_vdsoPC(R6)
MOVD R4, m_vdsoSP(R6)
@@ -312,7 +312,7 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$32-8
MOVD R4, 24(R15)
MOVD R14, R8 // Backup return address
- MOVD $ret+0(FP), R4 // caller's SP
+ MOVD $ret-8(FP), R4 // caller's SP
MOVD R8, m_vdsoPC(R6)
MOVD R4, m_vdsoSP(R6)
diff --git a/src/runtime/testdata/testprognet/signalexec.go b/src/runtime/testdata/testprognet/signalexec.go
index 62ebce7176..7e7591e8f7 100644
--- a/src/runtime/testdata/testprognet/signalexec.go
+++ b/src/runtime/testdata/testprognet/signalexec.go
@@ -13,9 +13,11 @@ package main
import (
"fmt"
+ "io"
"os"
"os/exec"
"os/signal"
+ "runtime"
"sync"
"syscall"
"time"
@@ -23,10 +25,51 @@ import (
func init() {
register("SignalDuringExec", SignalDuringExec)
+ register("SignalDuringExecPgrp", SignalDuringExecPgrp)
register("Nop", Nop)
}
func SignalDuringExec() {
+ // Re-launch ourselves in a new process group.
+ cmd := exec.Command(os.Args[0], "SignalDuringExecPgrp")
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ cmd.SysProcAttr = &syscall.SysProcAttr{
+ Setpgid: true,
+ }
+
+ // Start the new process with an extra pipe. It will
+ // exit if the pipe is closed.
+ rp, wp, err := os.Pipe()
+ if err != nil {
+ fmt.Printf("Failed to create pipe: %v", err)
+ return
+ }
+ cmd.ExtraFiles = []*os.File{rp}
+
+ // Run the command.
+ if err := cmd.Run(); err != nil {
+ fmt.Printf("Run failed: %v", err)
+ }
+
+ // We don't actually need to write to the pipe, it just
+ // needs to get closed, which will happen on process
+ // exit.
+ runtime.KeepAlive(wp)
+}
+
+func SignalDuringExecPgrp() {
+ // Grab fd 3 which is a pipe we need to read on.
+ f := os.NewFile(3, "pipe")
+ go func() {
+ // Nothing will ever get written to the pipe, so we'll
+ // just block on it. If it closes, ReadAll will return
+ // one way or another, at which point we'll exit.
+ io.ReadAll(f)
+ os.Exit(1)
+ }()
+
+ // This is just for SignalDuringExec.
pgrp := syscall.Getpgrp()
const tries = 10
diff --git a/src/runtime/trace.go b/src/runtime/trace.go
index b92e7b4e8e..0d71ad445c 100644
--- a/src/runtime/trace.go
+++ b/src/runtime/trace.go
@@ -396,7 +396,7 @@ func traceAdvance(stopTrace bool) {
ug.status = readgstatus(s.g) &^ _Gscan
ug.waitreason = s.g.waitreason
ug.inMarkAssist = s.g.inMarkAssist
- ug.stackID = traceStack(0, gp, gen)
+ ug.stackID = traceStack(0, gp, &trace.stackTab[gen%2])
}
resumeG(s)
casgstatus(me, _Gwaiting, _Grunning)
diff --git a/src/runtime/traceevent.go b/src/runtime/traceevent.go
index 9d1a93d3f9..263847be2e 100644
--- a/src/runtime/traceevent.go
+++ b/src/runtime/traceevent.go
@@ -56,7 +56,7 @@ func (e traceEventWriter) event(ev tracev2.EventType, args ...traceArg) {
// It then returns a traceArg representing that stack which may be
// passed to write.
func (tl traceLocker) stack(skip int) traceArg {
- return traceArg(traceStack(skip, nil, tl.gen))
+ return traceArg(traceStack(skip, nil, &trace.stackTab[tl.gen%2]))
}
// startPC takes a start PC for a goroutine and produces a unique
diff --git a/src/runtime/tracestack.go b/src/runtime/tracestack.go
index 2ee68c85f0..76d6b05048 100644
--- a/src/runtime/tracestack.go
+++ b/src/runtime/tracestack.go
@@ -28,10 +28,8 @@ const (
// skip controls the number of leaf frames to omit in order to hide tracer internals
// from stack traces, see CL 5523.
//
-// Avoid calling this function directly. gen needs to be the current generation
-// that this stack trace is being written out for, which needs to be synchronized with
-// generations moving forward. Prefer traceEventWriter.stack.
-func traceStack(skip int, gp *g, gen uintptr) uint64 {
+// Avoid calling this function directly. Prefer traceEventWriter.stack.
+func traceStack(skip int, gp *g, tab *traceStackTable) uint64 {
var pcBuf [tracev2.MaxFramesPerStack]uintptr
// Figure out gp and mp for the backtrace.
@@ -134,7 +132,7 @@ func traceStack(skip int, gp *g, gen uintptr) uint64 {
if nstk > 0 && gp.goid == 1 {
nstk-- // skip runtime.main
}
- id := trace.stackTab[gen%2].put(pcBuf[:nstk])
+ id := tab.put(pcBuf[:nstk])
return id
}
diff --git a/src/runtime/tracestack_test.go b/src/runtime/tracestack_test.go
new file mode 100644
index 0000000000..eaf4d906e3
--- /dev/null
+++ b/src/runtime/tracestack_test.go
@@ -0,0 +1,46 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime_test
+
+import (
+ "runtime"
+ "strconv"
+ "testing"
+)
+
+func BenchmarkTraceStack(b *testing.B) {
+ for _, stackDepth := range []int{1, 10, 100} {
+ b.Run("stackDepth="+strconv.Itoa(stackDepth), func(b *testing.B) {
+ benchmarkTraceStack(b, stackDepth)
+ })
+ }
+}
+
+func benchmarkTraceStack(b *testing.B, stackDepth int) {
+ var tab runtime.TraceStackTable
+ defer tab.Reset()
+
+ wait := make(chan struct{})
+ ready := make(chan struct{})
+ done := make(chan struct{})
+ var gp *runtime.G
+ go func() {
+ gp = runtime.Getg()
+ useStackAndCall(stackDepth, func() {
+ ready <- struct{}{}
+ <-wait
+ })
+ done <- struct{}{}
+ }()
+ <-ready
+
+ for b.Loop() {
+ runtime.TraceStack(gp, &tab)
+ }
+
+ // Clean up.
+ wait <- struct{}{}
+ <-done
+}