mirror of
https://github.com/golang/go.git
synced 2025-10-19 19:13:18 +00:00
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 <cherryyz@google.com> Auto-Submit: Michael Knyszek <mknyszek@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
20978f46fd
commit
75b43f9a97
6 changed files with 65 additions and 8 deletions
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
46
src/runtime/tracestack_test.go
Normal file
46
src/runtime/tracestack_test.go
Normal file
|
@ -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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue