From: Michael Anthony Knyszek Date: Fri, 27 Jun 2025 16:40:43 +0000 (+0000) Subject: runtime: make traceStack testable and add a benchmark X-Git-Tag: go1.25rc3~5^2~47 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=75b43f9a97ada1f0fce7a5775d4ab373bd0dad9c;p=gostls13.git runtime: make traceStack testable and add a benchmark Change-Id: Ide4daa5eee3fd4f3007d6ef23aa84b8916562c39 Reviewed-on: https://go-review.googlesource.com/c/go/+/684457 Reviewed-by: Cherry Mui Auto-Submit: Michael Knyszek LUCI-TryBot-Result: Go LUCI --- diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index 83cf301be4..9a4611e26e 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -1917,3 +1917,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/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/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 +}