aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorCherry Mui <cherryyz@google.com>2025-11-24 11:03:06 -0500
committerCherry Mui <cherryyz@google.com>2025-11-24 11:03:06 -0500
commit220d73cc44a0c580dc8ee10df6395c8e517e118f (patch)
treed072aab20229f5489f43a0ce13ab52ff867ba38c /src/runtime
parent0c69e7734308f36de7acbeda4cabced8018c04e3 (diff)
parent8dd5b13abcb64993959ea02c3f664654af6367a9 (diff)
downloadgo-220d73cc44a0c580dc8ee10df6395c8e517e118f.tar.xz
[dev.simd] all: merge master (8dd5b13) into dev.simd
Merge List: + 2025-11-24 8dd5b13abc cmd/compile: relax stmtline_test on amd64 + 2025-11-23 feae743bdb cmd/compile: use 32x32->64 multiplies on loong64 + 2025-11-23 e88be8a128 runtime: fix stale comment for mheap/malloc + 2025-11-23 a318843a2a cmd/internal/obj/loong64: optimize duplicate optab entries + 2025-11-23 a18294bb6a cmd/internal/obj/arm64, image/gif, runtime, sort: use math/bits to calculate log2 + 2025-11-23 437323ef7b slices: fix incorrect comment in slices.Insert function documentation + 2025-11-23 1993dca400 doc/next: pre-announce end of support for macOS 12 in Go 1.27 + 2025-11-22 337f7b1f5d cmd/go: update default go directive in mod or work init + 2025-11-21 3c26aef8fb cmd/internal/obj/riscv: improve large branch/call/jump tests + 2025-11-21 31aa9f800b crypto/tls: use inner hello for earlyData when using QUIC and ECH + 2025-11-21 d68aec8db1 runtime: replace trace seqlock with write flag + 2025-11-21 8d9906cd34 runtime/trace: add Log benchmark + 2025-11-21 6aeacdff38 cmd/go: support sha1 repos when git default is sha256 + 2025-11-21 9570036ca5 crypto/sha3: make the zero value of SHAKE useable + 2025-11-21 155efbbeeb crypto/sha3: make the zero value of SHA3 useable + 2025-11-21 6f16669e34 database/sql: don't ignore ColumnConverter for unknown input count + 2025-11-21 121bc3e464 runtime/pprof: remove hard-coded sleep in CPU profile reader + 2025-11-21 b604148c4e runtime: fix double wakeup in CPU profile buffer + 2025-11-21 22f24f90b5 cmd/compile: change testing.B.Loop keep alive semantic + 2025-11-21 cfb9d2eb73 net: remove unused linknames + 2025-11-21 65ef314f89 net/http: remove unused linknames + 2025-11-21 0f32fbc631 net/http: populate Response.Request when using NewFileTransport + 2025-11-21 3e0a8e7867 net/http: preserve original path encoding in redirects + 2025-11-21 831af61120 net/http: use HTTP 307 redirects in ServeMux + 2025-11-21 87269224cb net/http: update Response.Request.URL after redirects on GOOS=js + 2025-11-21 7aa9ca729f net/http/cookiejar: treat localhost as secure origin + 2025-11-21 f870a1d398 net/url: warn that JoinPath arguments should be escaped + 2025-11-21 9962d95fed crypto/internal/fips140/mldsa: unroll NTT and inverseNTT + 2025-11-21 f821fc46c5 crypto/internal/fisp140test: update acvptool, test data + 2025-11-21 b59efc38a0 crypto/internal/fips140/mldsa: new package + 2025-11-21 62741480b8 runtime: remove linkname for gopanic + 2025-11-21 7db2f0bb9a crypto/internal/hpke: separate KEM and PublicKey/PrivateKey interfaces + 2025-11-21 e15800c0ec crypto/internal/hpke: add ML-KEM and hybrid KEMs, and SHAKE KDFs + 2025-11-21 7c985a2df4 crypto/internal/hpke: modularize API and support more ciphersuites + 2025-11-21 e7d47ac33d cmd/compile: simplify negative on multiplication + 2025-11-21 35d2712b32 net/http: fix typo in Transport docs + 2025-11-21 90c970cd0f net: remove unnecessary loop variable copies in tests + 2025-11-21 9772d3a690 cmd/cgo: strip top-level const qualifier from argument frame struct + 2025-11-21 1903782ade errors: add examples for custom Is/As matching + 2025-11-21 ec92bc6d63 cmd/compile: rewrite Rsh to RshU if arguments are proved positive + 2025-11-21 3820f94c1d cmd/compile: propagate unsigned relations for Rsh if arguments are positive + 2025-11-21 d474f1fd21 cmd/compile: make dse track multiple shadowed ranges + 2025-11-21 d0d0a72980 cmd/compile/internal/ssa: correct type of ARM64 conditional instructions + 2025-11-21 a9704f89ea internal/runtime/gc/scan: add AVX512 impl of filterNil. + 2025-11-21 ccd389036a cmd/internal/objabi: remove -V=goexperiment internal special case + 2025-11-21 e7787b9eca runtime: go fmt + 2025-11-21 17b3b98796 internal/strconv: go fmt + 2025-11-21 c851827c68 internal/trace: go fmt + 2025-11-21 f87aaec53d cmd/compile: fix integer overflow in prove pass + 2025-11-21 dbd2ab9992 cmd/compile/internal: fix typos + 2025-11-21 b9d86baae3 cmd/compile/internal/devirtualize: fix typos + 2025-11-20 4b0e3cc1d6 cmd/link: support loading R_LARCH_PCREL20_S2 and R_LARCH_CALL36 relocs + 2025-11-20 cdba82c7d6 cmd/internal/obj/loong64: add {,X}VSLT.{B/H/W/V}{,U} instructions support + 2025-11-20 bd2b117c2c crypto/tls: add QUICErrorEvent + 2025-11-20 3ad2e113fc net/http/httputil: wrap ReverseProxy's outbound request body so Close is a noop + 2025-11-20 d58b733646 runtime: track goroutine location until actual STW + 2025-11-20 1bc54868d4 cmd/vendor: update to x/tools@68724af + 2025-11-20 8c3195973b runtime: disable stack allocation tests on sanitizers + 2025-11-20 ff654ea100 net/url: permit colons in the host of postgresql:// URLs + 2025-11-20 a662badab9 encoding/json: remove linknames + 2025-11-20 5afe237d65 mime: add missing path for mime types in godoc + 2025-11-20 c1b7112af8 os/signal: make NotifyContext cancel the context with a cause Change-Id: Ib93ef643be610dfbdd83ff45095a7b1ca2537b8b
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/cgocall.go1
-rw-r--r--src/runtime/list.go8
-rw-r--r--src/runtime/list_manual.go8
-rw-r--r--src/runtime/malloc.go2
-rw-r--r--src/runtime/mgcmark_nogreenteagc.go2
-rw-r--r--src/runtime/mheap.go2
-rw-r--r--src/runtime/panic.go5
-rw-r--r--src/runtime/pprof/pprof.go5
-rw-r--r--src/runtime/proc_test.go47
-rw-r--r--src/runtime/profbuf.go30
-rw-r--r--src/runtime/profbuf_test.go92
-rw-r--r--src/runtime/runtime2.go14
-rw-r--r--src/runtime/slice_test.go11
-rw-r--r--src/runtime/stack.go9
-rw-r--r--src/runtime/trace.go14
-rw-r--r--src/runtime/trace/annotation_test.go13
-rw-r--r--src/runtime/tracecpu.go14
-rw-r--r--src/runtime/traceruntime.go37
18 files changed, 225 insertions, 89 deletions
diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go
index a53fd6da34..f01353ffa6 100644
--- a/src/runtime/cgocall.go
+++ b/src/runtime/cgocall.go
@@ -592,6 +592,7 @@ func cgoCheckPointer(ptr any, arg any) {
}
type cgoErrorMsg int
+
const (
cgoCheckPointerFail cgoErrorMsg = iota
cgoResultFail
diff --git a/src/runtime/list.go b/src/runtime/list.go
index c900ad7ff3..43b51996e4 100644
--- a/src/runtime/list.go
+++ b/src/runtime/list.go
@@ -34,11 +34,11 @@ func (head *listHead) init(off uintptr) {
//
// For example:
//
-// type foo struct {
-// val int
+// type foo struct {
+// val int
//
-// node listNode
-// }
+// node listNode
+// }
//
// var fooHead listHead
// fooHead.init(unsafe.Offsetof(foo{}.node))
diff --git a/src/runtime/list_manual.go b/src/runtime/list_manual.go
index af0ae6b2d6..f0ce9ad4da 100644
--- a/src/runtime/list_manual.go
+++ b/src/runtime/list_manual.go
@@ -41,11 +41,11 @@ func (head *listHeadManual) init(off uintptr) {
//
// For example:
//
-// type foo struct {
-// val int
+// type foo struct {
+// val int
//
-// node listNodeManual
-// }
+// node listNodeManual
+// }
//
// var fooHead listHeadManual
// fooHead.init(unsafe.Offsetof(foo{}.node))
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index d49dacaf68..4971e16c6a 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -1169,7 +1169,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
var x unsafe.Pointer
var elemsize uintptr
if sizeSpecializedMallocEnabled {
- // we know that heapBitsInSpan is true.
+ // we know that heapBitsInSpan is false.
if size <= maxSmallSize-gc.MallocHeaderSize {
if typ == nil || !typ.Pointers() {
x, elemsize = mallocgcSmallNoscan(size, typ, needzero)
diff --git a/src/runtime/mgcmark_nogreenteagc.go b/src/runtime/mgcmark_nogreenteagc.go
index a0470c6e32..ce0d8d80f3 100644
--- a/src/runtime/mgcmark_nogreenteagc.go
+++ b/src/runtime/mgcmark_nogreenteagc.go
@@ -67,7 +67,7 @@ func (q *spanQueue) destroy() {
}
type spanSPMC struct {
- _ sys.NotInHeap
+ _ sys.NotInHeap
allnode listNodeManual
}
diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go
index d2ff063b00..0ccaadc891 100644
--- a/src/runtime/mheap.go
+++ b/src/runtime/mheap.go
@@ -56,7 +56,7 @@ const (
)
// Main malloc heap.
-// The heap itself is the "free" and "scav" treaps,
+// The heap use pageAlloc to manage free and scavenged pages,
// but all the other global data is here too.
//
// mheap must not be heap-allocated because it contains mSpanLists,
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index ff2dec386f..d467e9305d 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -800,10 +800,7 @@ var panicnil = &godebugInc{name: "panicnil"}
// The compiler emits calls to this function.
//
// gopanic should be an internal detail,
-// but widely used packages access it using linkname.
-// Notable members of the hall of shame include:
-// - go.undefinedlabs.com/scopeagent
-// - github.com/goplus/igop
+// but historically, widely used packages access it using linkname.
//
// Do not remove or change the type signature.
// See go.dev/issue/67401.
diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go
index 78d00af6ca..c617a8b26a 100644
--- a/src/runtime/pprof/pprof.go
+++ b/src/runtime/pprof/pprof.go
@@ -924,7 +924,10 @@ func profileWriter(w io.Writer) {
b := newProfileBuilder(w)
var err error
for {
- time.Sleep(100 * time.Millisecond)
+ if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
+ // see runtime_pprof_readProfile
+ time.Sleep(100 * time.Millisecond)
+ }
data, tags, eof := readProfile()
if e := b.addCPUData(data, tags); e != nil && err == nil {
err = e
diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go
index 35a1aeab1f..a2c8b55342 100644
--- a/src/runtime/proc_test.go
+++ b/src/runtime/proc_test.go
@@ -1175,9 +1175,9 @@ func TestBigGOMAXPROCS(t *testing.T) {
}
type goroutineState struct {
- G trace.GoID // This goroutine.
- P trace.ProcID // Most recent P this goroutine ran on.
- M trace.ThreadID // Most recent M this goroutine ran on.
+ G trace.GoID // This goroutine.
+ P trace.ProcID // Most recent P this goroutine ran on.
+ M trace.ThreadID // Most recent M this goroutine ran on.
}
func newGoroutineState(g trace.GoID) *goroutineState {
@@ -1228,7 +1228,7 @@ func TestTraceSTW(t *testing.T) {
}
}
- pct := float64(errors)/float64(runs)
+ pct := float64(errors) / float64(runs)
t.Logf("Errors: %d/%d = %f%%", errors, runs, 100*pct)
if pct > 0.25 {
t.Errorf("Error rate too high")
@@ -1264,7 +1264,7 @@ func TestTraceGCSTW(t *testing.T) {
}
}
- pct := float64(errors)/float64(runs)
+ pct := float64(errors) / float64(runs)
t.Logf("Errors: %d/%d = %f%%", errors, runs, 100*pct)
if pct > 0.25 {
t.Errorf("Error rate too high")
@@ -1321,11 +1321,13 @@ func runTestTracesSTW(t *testing.T, run int, name, stwType string) (err error) {
//
// 2. Once found, track which M and P the target goroutines run on until...
//
- // 3. Look for the "TraceSTW" "start" log message, where we commit the
- // target goroutines' "before" M and P.
+ // 3. Look for the first STW after the "TraceSTW" "start" log message,
+ // where we commit the target goroutines' "before" M and P.
//
// N.B. We must do (1) and (2) together because the first target
// goroutine may start running before the second is created.
+ var startLogSeen bool
+ var stwSeen bool
findStart:
for {
ev, err := br.ReadEvent()
@@ -1384,10 +1386,26 @@ findStart:
// Found start point, move on to next stage.
t.Logf("Found start message")
- break findStart
+ startLogSeen = true
+ case trace.EventRangeBegin:
+ if !startLogSeen {
+ // Ignore spurious STW before we expect.
+ continue
+ }
+
+ r := ev.Range()
+ if r.Name == stwType {
+ t.Logf("Found STW")
+ stwSeen = true
+ break findStart
+ }
}
}
+ if !stwSeen {
+ t.Fatal("Can't find STW in the test trace")
+ }
+
t.Log("Target goroutines:")
for _, gs := range targetGoroutines {
t.Logf("%+v", gs)
@@ -1440,7 +1458,6 @@ findStart:
// [1] This is slightly fragile because there is a small window between
// the "start" log and actual STW during which the target goroutines
// could legitimately migrate.
- var stwSeen bool
var pRunning []trace.ProcID
var gRunning []trace.GoID
findEnd:
@@ -1543,21 +1560,9 @@ findEnd:
// Found end point.
t.Logf("Found end message")
break findEnd
- case trace.EventRangeBegin:
- r := ev.Range()
- if r.Name == stwType {
- // Note when we see the STW begin. This is not
- // load bearing; it's purpose is simply to fail
- // the test if we accidentally remove the STW.
- stwSeen = true
- }
}
}
- if !stwSeen {
- t.Fatal("No STW in the test trace")
- }
-
return nil
}
diff --git a/src/runtime/profbuf.go b/src/runtime/profbuf.go
index 8ae626b1b0..147212206f 100644
--- a/src/runtime/profbuf.go
+++ b/src/runtime/profbuf.go
@@ -38,6 +38,10 @@ import (
// be returned by read. By definition, r ≤ rNext ≤ w (before wraparound),
// and rNext is only used by the reader, so it can be accessed without atomics.
//
+// If the reader is blocked waiting for more data, the writer will wake it up if
+// either the buffer is more than half full, or when the writer sets the eof
+// marker or writes overflow entries (described below.)
+//
// If the writer gets ahead of the reader, so that the buffer fills,
// future writes are discarded and replaced in the output stream by an
// overflow entry, which has size 2+hdrsize+1, time set to the time of
@@ -378,11 +382,28 @@ func (b *profBuf) write(tagPtr *unsafe.Pointer, now int64, hdr []uint64, stk []u
// Racing with reader setting flag bits in b.w, to avoid lost wakeups.
old := b.w.load()
new := old.addCountsAndClearFlags(skip+2+len(stk)+int(b.hdrsize), 1)
+ // We re-load b.r here to reduce the likelihood of early wakeups
+ // if the reader already consumed some data between the last
+ // time we read b.r and now. This isn't strictly necessary.
+ unread := countSub(new.dataCount(), b.r.load().dataCount())
+ if unread < 0 {
+ // The new count overflowed and wrapped around.
+ unread += len(b.data)
+ }
+ wakeupThreshold := len(b.data) / 2
+ if unread < wakeupThreshold {
+ // Carry over the sleeping flag since we're not planning
+ // to wake the reader yet
+ new |= old & profReaderSleeping
+ }
if !b.w.cas(old, new) {
continue
}
- // If there was a reader, wake it up.
- if old&profReaderSleeping != 0 {
+ // If we've hit our high watermark for data in the buffer,
+ // and there is a reader, wake it up.
+ if unread >= wakeupThreshold && old&profReaderSleeping != 0 {
+ // NB: if we reach this point, then the sleeping bit is
+ // cleared in the new b.w value
notewakeup(&b.wait)
}
break
@@ -406,6 +427,11 @@ func (b *profBuf) wakeupExtra() {
for {
old := b.w.load()
new := old | profWriteExtra
+ // Clear profReaderSleeping. We're going to wake up the reader
+ // if it was sleeping and we don't want double wakeups in case
+ // we, for example, attempt to write into a full buffer multiple
+ // times before the reader wakes up.
+ new &^= profReaderSleeping
if !b.w.cas(old, new) {
continue
}
diff --git a/src/runtime/profbuf_test.go b/src/runtime/profbuf_test.go
index 9050d1fa25..2f068ac386 100644
--- a/src/runtime/profbuf_test.go
+++ b/src/runtime/profbuf_test.go
@@ -5,8 +5,12 @@
package runtime_test
import (
+ "fmt"
+ "regexp"
+ "runtime"
. "runtime"
"slices"
+ "sync"
"testing"
"time"
"unsafe"
@@ -174,3 +178,91 @@ func TestProfBuf(t *testing.T) {
}
})
}
+
+func TestProfBufDoubleWakeup(t *testing.T) {
+ b := NewProfBuf(2, 16, 2)
+ go func() {
+ for range 1000 {
+ b.Write(nil, 1, []uint64{5, 6}, []uintptr{7, 8})
+ }
+ b.Close()
+ }()
+ for {
+ _, _, eof := b.Read(ProfBufBlocking)
+ if eof {
+ return
+ }
+ }
+}
+
+func TestProfBufWakeup(t *testing.T) {
+ b := NewProfBuf(2, 16, 2)
+ var wg sync.WaitGroup
+ wg.Go(func() {
+ read := 0
+ for {
+ rdata, _, eof := b.Read(ProfBufBlocking)
+ if read == 0 && len(rdata) < 8 {
+ t.Errorf("first wake up at less than half full, got %x", rdata)
+ }
+ read += len(rdata)
+ if eof {
+ return
+ }
+ }
+ })
+
+ // Under the hood profBuf uses notetsleepg when the reader blocks.
+ // Different platforms have different implementations, leading to
+ // different statuses we need to look for to determine whether the
+ // reader is blocked.
+ var waitStatus string
+ switch runtime.GOOS {
+ case "js":
+ waitStatus = "waiting"
+ case "wasip1":
+ waitStatus = "runnable"
+ default:
+ waitStatus = "syscall"
+ }
+
+ // Ensure that the reader is blocked
+ awaitBlockedGoroutine(waitStatus, "TestProfBufWakeup.func1")
+ // NB: this writes 6 words not 4. Fine for the test.
+ // The reader shouldn't wake up for this
+ b.Write(nil, 1, []uint64{1, 2}, []uintptr{3, 4})
+
+ // The reader should still be blocked
+ //
+ // TODO(nick): this is racy. We could Gosched and still have the reader
+ // blocked in a buggy implementation because it just didn't get a chance
+ // to run
+ awaitBlockedGoroutine(waitStatus, "TestProfBufWakeup.func1")
+ b.Write(nil, 1, []uint64{5, 6}, []uintptr{7, 8})
+ b.Close()
+
+ // Wait here so we can be sure the reader got the data
+ wg.Wait()
+}
+
+// see also runtime/pprof tests
+func awaitBlockedGoroutine(state, fName string) {
+ re := fmt.Sprintf(`(?m)^goroutine \d+ \[%s\]:\n(?:.+\n\t.+\n)*runtime_test\.%s`, regexp.QuoteMeta(state), fName)
+ r := regexp.MustCompile(re)
+
+ buf := make([]byte, 64<<10)
+ for {
+ Gosched()
+ n := Stack(buf, true)
+ if n == len(buf) {
+ // Buffer wasn't large enough for a full goroutine dump.
+ // Resize it and try again.
+ buf = make([]byte, 2*len(buf))
+ continue
+ }
+ const count = 1
+ if len(r.FindAll(buf[:n], -1)) >= count {
+ return
+ }
+ }
+}
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index 56082bf7f5..3175ee55f5 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -838,7 +838,7 @@ type p struct {
palloc persistentAlloc // per-P to avoid mutex
// Per-P GC state
- gcAssistTime int64 // Nanoseconds in assistAlloc
+ gcAssistTime int64 // Nanoseconds in assistAlloc
gcFractionalMarkTime atomic.Int64 // Nanoseconds in fractional mark worker
// limiterEvent tracks events for the GC CPU limiter.
@@ -934,12 +934,12 @@ type schedt struct {
// sure to call checkdead().
midle listHeadManual // idle m's waiting for work
- nmidle int32 // number of idle m's waiting for work
- nmidlelocked int32 // number of locked m's waiting for work
- mnext int64 // number of m's that have been created and next M ID
- maxmcount int32 // maximum number of m's allowed (or die)
- nmsys int32 // number of system m's not counted for deadlock
- nmfreed int64 // cumulative number of freed m's
+ nmidle int32 // number of idle m's waiting for work
+ nmidlelocked int32 // number of locked m's waiting for work
+ mnext int64 // number of m's that have been created and next M ID
+ maxmcount int32 // maximum number of m's allowed (or die)
+ nmsys int32 // number of system m's not counted for deadlock
+ nmfreed int64 // cumulative number of freed m's
ngsys atomic.Int32 // number of system goroutines
nGsyscallNoP atomic.Int32 // number of goroutines in syscalls without a P
diff --git a/src/runtime/slice_test.go b/src/runtime/slice_test.go
index 5463b6c02f..376b4a58f2 100644
--- a/src/runtime/slice_test.go
+++ b/src/runtime/slice_test.go
@@ -6,6 +6,8 @@ package runtime_test
import (
"fmt"
+ "internal/asan"
+ "internal/msan"
"internal/race"
"internal/testenv"
"runtime"
@@ -516,6 +518,9 @@ func TestAppendByteInLoop(t *testing.T) {
if race.Enabled {
t.Skip("skipping in -race mode")
}
+ if asan.Enabled || msan.Enabled {
+ t.Skip("skipping in sanitizer mode")
+ }
for _, test := range [][3]int{
{0, 0, 0},
{1, 1, 8},
@@ -562,6 +567,9 @@ func TestAppendPtrInLoop(t *testing.T) {
if race.Enabled {
t.Skip("skipping in -race mode")
}
+ if asan.Enabled || msan.Enabled {
+ t.Skip("skipping in sanitizer mode")
+ }
var tests [][3]int
if runtime.PtrSize == 8 {
tests = [][3]int{
@@ -628,6 +636,9 @@ func TestAppendByteCapInLoop(t *testing.T) {
if race.Enabled {
t.Skip("skipping in -race mode")
}
+ if asan.Enabled || msan.Enabled {
+ t.Skip("skipping in sanitizer mode")
+ }
for _, test := range [][3]int{
{0, 0, 0},
{1, 1, 8},
diff --git a/src/runtime/stack.go b/src/runtime/stack.go
index 55e97e77af..c92accf188 100644
--- a/src/runtime/stack.go
+++ b/src/runtime/stack.go
@@ -12,6 +12,7 @@ import (
"internal/runtime/atomic"
"internal/runtime/gc"
"internal/runtime/sys"
+ "math/bits"
"unsafe"
)
@@ -181,12 +182,10 @@ func stackinit() {
// stacklog2 returns ⌊log_2(n)⌋.
func stacklog2(n uintptr) int {
- log2 := 0
- for n > 1 {
- n >>= 1
- log2++
+ if n == 0 {
+ return 0
}
- return log2
+ return bits.Len64(uint64(n))
}
// Allocates a stack from the free pool. Must be called with
diff --git a/src/runtime/trace.go b/src/runtime/trace.go
index 7130e2c136..0fdc829f71 100644
--- a/src/runtime/trace.go
+++ b/src/runtime/trace.go
@@ -13,17 +13,17 @@
// ## Design
//
// The basic idea behind the the execution tracer is to have per-M buffers that
-// trace data may be written into. Each M maintains a seqlock indicating whether
+// trace data may be written into. Each M maintains a write flag indicating whether
// its trace buffer is currently in use.
//
// Tracing is initiated by StartTrace, and proceeds in "generations," with each
// generation being marked by a call to traceAdvance, to advance to the next
// generation. Generations are a global synchronization point for trace data,
// and we proceed to a new generation by moving forward trace.gen. Each M reads
-// trace.gen under its own seqlock to determine which generation it is writing
+// trace.gen under its own write flag to determine which generation it is writing
// trace data for. To this end, each M has 2 slots for buffers: one slot for the
// previous generation, one slot for the current one. It uses tl.gen to select
-// which buffer slot to write to. Simultaneously, traceAdvance uses the seqlock
+// which buffer slot to write to. Simultaneously, traceAdvance uses the write flag
// to determine whether every thread is guaranteed to observe an updated
// trace.gen. Once it is sure, it may then flush any buffers that are left over
// from the previous generation safely, since it knows the Ms will not mutate
@@ -43,7 +43,7 @@
// appear in pairs: one for the previous generation, and one for the current one.
// Like the per-M buffers, which of the two is written to is selected using trace.gen,
// and anything managed this way must similarly be mutated only in traceAdvance or
-// under the M's seqlock.
+// under the M's write flag.
//
// Trace events themselves are simple. They consist of a single byte for the event type,
// followed by zero or more LEB128-encoded unsigned varints. They are decoded using
@@ -629,7 +629,7 @@ func traceAdvance(stopTrace bool) {
// while they're still on that list. Removal from sched.freem is serialized with
// this snapshot, so either we'll capture an m on sched.freem and race with
// the removal to flush its buffers (resolved by traceThreadDestroy acquiring
- // the thread's seqlock, which one of us must win, so at least its old gen buffer
+ // the thread's write flag, which one of us must win, so at least its old gen buffer
// will be flushed in time for the new generation) or it will have flushed its
// buffers before we snapshotted it to begin with.
lock(&sched.lock)
@@ -645,7 +645,7 @@ func traceAdvance(stopTrace bool) {
// Iterate over our snapshot, flushing every buffer until we're done.
//
- // Because trace writers read the generation while the seqlock is
+ // Because trace writers read the generation while the write flag is
// held, we can be certain that when there are no writers there are
// also no stale generation values left. Therefore, it's safe to flush
// any buffers that remain in that generation's slot.
@@ -658,7 +658,7 @@ func traceAdvance(stopTrace bool) {
for mToFlush != nil {
prev := &mToFlush
for mp := *prev; mp != nil; {
- if mp.trace.seqlock.Load()%2 != 0 {
+ if mp.trace.writing.Load() {
// The M is writing. Come back to it later.
prev = &mp.trace.link
mp = mp.trace.link
diff --git a/src/runtime/trace/annotation_test.go b/src/runtime/trace/annotation_test.go
index 3bd767bfbe..ea10843230 100644
--- a/src/runtime/trace/annotation_test.go
+++ b/src/runtime/trace/annotation_test.go
@@ -6,6 +6,7 @@ package trace_test
import (
"context"
+ "io"
. "runtime/trace"
"testing"
)
@@ -34,3 +35,15 @@ func BenchmarkNewTask(b *testing.B) {
}
})
}
+
+func BenchmarkLog(b *testing.B) {
+ b.ReportAllocs()
+
+ Start(io.Discard)
+ defer Stop()
+
+ ctx := context.Background()
+ for b.Loop() {
+ Log(ctx, "", "")
+ }
+}
diff --git a/src/runtime/tracecpu.go b/src/runtime/tracecpu.go
index e64ca32cdf..c9c3a1511f 100644
--- a/src/runtime/tracecpu.go
+++ b/src/runtime/tracecpu.go
@@ -224,20 +224,20 @@ func traceCPUSample(gp *g, mp *m, pp *p, stk []uintptr) {
// We're going to conditionally write to one of two buffers based on the
// generation. To make sure we write to the correct one, we need to make
- // sure this thread's trace seqlock is held. If it already is, then we're
+ // sure this thread's trace write flag is set. If it already is, then we're
// in the tracer and we can just take advantage of that. If it isn't, then
// we need to acquire it and read the generation.
locked := false
- if mp.trace.seqlock.Load()%2 == 0 {
- mp.trace.seqlock.Add(1)
+ if !mp.trace.writing.Load() {
+ mp.trace.writing.Store(true)
locked = true
}
gen := trace.gen.Load()
if gen == 0 {
- // Tracing is disabled, as it turns out. Release the seqlock if necessary
+ // Tracing is disabled, as it turns out. Clear the write flag if necessary
// and exit.
if locked {
- mp.trace.seqlock.Add(1)
+ mp.trace.writing.Store(false)
}
return
}
@@ -275,8 +275,8 @@ func traceCPUSample(gp *g, mp *m, pp *p, stk []uintptr) {
trace.signalLock.Store(0)
- // Release the seqlock if we acquired it earlier.
+ // Clear the write flag if we set it earlier.
if locked {
- mp.trace.seqlock.Add(1)
+ mp.trace.writing.Store(false)
}
}
diff --git a/src/runtime/traceruntime.go b/src/runtime/traceruntime.go
index ad91d9c836..92d07c6063 100644
--- a/src/runtime/traceruntime.go
+++ b/src/runtime/traceruntime.go
@@ -25,7 +25,7 @@ func (s *gTraceState) reset() {
// mTraceState is per-M state for the tracer.
type mTraceState struct {
- seqlock atomic.Uintptr // seqlock indicating that this M is writing to a trace buffer.
+ writing atomic.Bool // flag indicating that this M is writing to a trace buffer.
buf [2][tracev2.NumExperiments]*traceBuf // Per-M traceBuf for writing. Indexed by trace.gen%2.
link *m // Snapshot of alllink or freelink.
reentered uint32 // Whether we've reentered tracing from within tracing.
@@ -211,21 +211,18 @@ func traceAcquireEnabled() traceLocker {
// Check if we're already tracing. It's safe to be reentrant in general,
// because this function (and the invariants of traceLocker.writer) ensure
// that it is.
- if mp.trace.seqlock.Load()%2 == 1 {
+ if mp.trace.writing.Load() {
mp.trace.reentered++
return traceLocker{mp, mp.trace.entryGen}
}
- // Acquire the trace seqlock. This prevents traceAdvance from moving forward
- // until all Ms are observed to be outside of their seqlock critical section.
+ // Set the write flag. This prevents traceAdvance from moving forward
+ // until all Ms are observed to be outside of a write critical section.
//
- // Note: The seqlock is mutated here and also in traceCPUSample. If you update
- // usage of the seqlock here, make sure to also look at what traceCPUSample is
+ // Note: The write flag is mutated here and also in traceCPUSample. If you update
+ // usage of the write flag here, make sure to also look at what traceCPUSample is
// doing.
- seq := mp.trace.seqlock.Add(1)
- if debugTraceReentrancy && seq%2 != 1 {
- throw("bad use of trace.seqlock")
- }
+ mp.trace.writing.Store(true)
// N.B. This load of gen appears redundant with the one in traceEnabled.
// However, it's very important that the gen we use for writing to the trace
@@ -237,7 +234,7 @@ func traceAcquireEnabled() traceLocker {
// what we did and bail.
gen := trace.gen.Load()
if gen == 0 {
- mp.trace.seqlock.Add(1)
+ mp.trace.writing.Store(false)
releasem(mp)
return traceLocker{}
}
@@ -263,11 +260,7 @@ func traceRelease(tl traceLocker) {
if tl.mp.trace.reentered > 0 {
tl.mp.trace.reentered--
} else {
- seq := tl.mp.trace.seqlock.Add(1)
- if debugTraceReentrancy && seq%2 != 0 {
- print("runtime: seq=", seq, "\n")
- throw("bad use of trace.seqlock")
- }
+ tl.mp.trace.writing.Store(false)
}
releasem(tl.mp)
}
@@ -699,10 +692,10 @@ func traceThreadDestroy(mp *m) {
// Perform a traceAcquire/traceRelease on behalf of mp to
// synchronize with the tracer trying to flush our buffer
// as well.
- seq := mp.trace.seqlock.Add(1)
- if debugTraceReentrancy && seq%2 != 1 {
- throw("bad use of trace.seqlock")
+ if debugTraceReentrancy && mp.trace.writing.Load() {
+ throw("bad use of trace.writing")
}
+ mp.trace.writing.Store(true)
systemstack(func() {
lock(&trace.lock)
for i := range mp.trace.buf {
@@ -717,9 +710,5 @@ func traceThreadDestroy(mp *m) {
}
unlock(&trace.lock)
})
- seq1 := mp.trace.seqlock.Add(1)
- if seq1 != seq+1 {
- print("runtime: seq1=", seq1, "\n")
- throw("bad use of trace.seqlock")
- }
+ mp.trace.writing.Store(false)
}