1
0
mirror of https://github.com/golang/go synced 2024-11-24 02:00:20 -07:00

runtime/pprof: add race annotations for goroutine profiles

The race annotations for goroutine label maps covered the special type
of read necessary to create CPU profiles. Extend that to include
goroutine profiles. Annotate the copy involved in creating new
goroutines.

Fixes #50292

Change-Id: I10f69314e4f4eba85c506590fe4781f4d6b8ec2d
Reviewed-on: https://go-review.googlesource.com/c/go/+/385660
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
Rhys Hiltner 2022-02-14 12:16:22 -08:00 committed by Michael Knyszek
parent 7c404d59db
commit 209942fa88
3 changed files with 67 additions and 1 deletions

View File

@ -818,6 +818,10 @@ func goroutineProfileWithLabels(p []StackRecord, labels []unsafe.Pointer) (n int
})
}
if raceenabled {
raceacquire(unsafe.Pointer(&labelSync))
}
startTheWorld()
return n, ok
}

View File

@ -1442,7 +1442,7 @@ func TestCPUProfileLabel(t *testing.T) {
func TestLabelRace(t *testing.T) {
// Test the race detector annotations for synchronization
// between settings labels and consuming them from the
// between setting labels and consuming them from the
// profile.
matches := matchAndAvoidStacks(stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, nil)
testCPUProfile(t, matches, func(dur time.Duration) {
@ -1464,6 +1464,63 @@ func TestLabelRace(t *testing.T) {
})
}
func TestGoroutineProfileLabelRace(t *testing.T) {
// Test the race detector annotations for synchronization
// between setting labels and consuming them from the
// goroutine profile. See issue #50292.
t.Run("reset", func(t *testing.T) {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
goroutineProf := Lookup("goroutine")
for ctx.Err() == nil {
var w bytes.Buffer
goroutineProf.WriteTo(&w, 1)
prof := w.String()
if strings.Contains(prof, "loop-i") {
cancel()
}
}
}()
for i := 0; ctx.Err() == nil; i++ {
Do(ctx, Labels("loop-i", fmt.Sprint(i)), func(ctx context.Context) {
})
}
})
t.Run("churn", func(t *testing.T) {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
var ready sync.WaitGroup
ready.Add(1)
var churn func(i int)
churn = func(i int) {
SetGoroutineLabels(WithLabels(ctx, Labels("churn-i", fmt.Sprint(i))))
if i == 0 {
ready.Done()
}
if ctx.Err() == nil {
go churn(i + 1)
}
}
go func() {
churn(0)
}()
ready.Wait()
goroutineProf := Lookup("goroutine")
for i := 0; i < 10; i++ {
goroutineProf.WriteTo(io.Discard, 1)
}
})
}
// TestLabelSystemstack makes sure CPU profiler samples of goroutines running
// on systemstack include the correct pprof labels. See issue #48577
func TestLabelSystemstack(t *testing.T) {

View File

@ -4155,6 +4155,11 @@ func newproc1(fn *funcval, callergp *g, callerpc uintptr) *g {
_p_.goidcache++
if raceenabled {
newg.racectx = racegostart(callerpc)
if newg.labels != nil {
// See note in proflabel.go on labelSync's role in synchronizing
// with the reads in the signal handler.
racereleasemergeg(newg, unsafe.Pointer(&labelSync))
}
}
if trace.enabled {
traceGoCreate(newg, newg.startpc)