]> Cypherpunks repositories - gostls13.git/commitdiff
runtime/pprof: fix goroutine leak profile tests for noopt
authorMichael Anthony Knyszek <mknyszek@google.com>
Thu, 13 Nov 2025 00:09:05 +0000 (00:09 +0000)
committerMichael Knyszek <mknyszek@google.com>
Thu, 13 Nov 2025 15:23:03 +0000 (07:23 -0800)
The goroutine leak profile tests currently rely on a function being
inlined, which results in a slightly different representation in the
pprof proto. This function is not inlined on the noopt builder.

Disable inlining of the one function which could be inlined to align but
the regular and noopt builder versions of this test. We're not
interested in testing the inlining functionality of profiles with this
test, we care about certain stack frames appearing in a certain order.

This also simplifies a bunch of the checking in the test.

Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-noopt
Change-Id: I28902cc4c9fae32d1e3fa41b93b00c3be4d6074a
Reviewed-on: https://go-review.googlesource.com/c/go/+/720100
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/runtime/pprof/pprof_test.go

index c1d4f06595f224b571f9e28710cf05f1fcf07f7a..4c9279c5a6f032b0d6fe5b55b2ddc47d236b7606 100644 (file)
@@ -1569,6 +1569,9 @@ func containsCountsLabels(prof *profile.Profile, countLabels map[int64]map[strin
        return true
 }
 
+// Inlining disabled to make identification simpler.
+//
+//go:noinline
 func goroutineLeakExample() {
        <-make(chan struct{})
        panic("unreachable")
@@ -1595,12 +1598,12 @@ func TestGoroutineLeakProfileConcurrency(t *testing.T) {
 
        checkFrame := func(i int, j int, locations []*profile.Location, expectedFunctionName string) {
                if len(locations) <= i {
-                       t.Errorf("leaked goroutine stack locations out of range at %d of %d", i+1, len(locations))
+                       t.Errorf("leaked goroutine stack locations: out of range index %d, length %d", i, len(locations))
                        return
                }
                location := locations[i]
                if len(location.Line) <= j {
-                       t.Errorf("leaked goroutine stack location lines out of range at %d of %d", j+1, len(location.Line))
+                       t.Errorf("leaked goroutine stack location lines: out of range index %d, length %d", j, len(location.Line))
                        return
                }
                if location.Line[j].Function.Name != expectedFunctionName {
@@ -1650,26 +1653,7 @@ func TestGoroutineLeakProfileConcurrency(t *testing.T) {
                                t.Errorf("expected %d leaked goroutines with specific stack configurations, but found %d", leakCount, pc)
                                return
                        }
-                       switch len(locations) {
-                       case 4:
-                               // We expect a receive operation. This is the typical stack.
-                               checkFrame(0, 0, locations, "runtime.gopark")
-                               checkFrame(1, 0, locations, "runtime.chanrecv")
-                               checkFrame(2, 0, locations, "runtime.chanrecv1")
-                               switch len(locations[3].Line) {
-                               case 2:
-                                       // Running `go func() { goroutineLeakExample() }()` will produce a stack with 2 lines.
-                                       // The anonymous function will have the call to goroutineLeakExample inlined.
-                                       checkFrame(3, 1, locations, "runtime/pprof.TestGoroutineLeakProfileConcurrency.func5")
-                                       fallthrough
-                               case 1:
-                                       // Running `go goroutineLeakExample()` will produce a stack with 1 line.
-                                       checkFrame(3, 0, locations, "runtime/pprof.goroutineLeakExample")
-                               default:
-                                       t.Errorf("leaked goroutine stack location expected 1 or 2 lines in the 4th location but found %d", len(locations[3].Line))
-                                       return
-                               }
-                       default:
+                       if len(locations) < 4 || len(locations) > 5 {
                                message := fmt.Sprintf("leaked goroutine stack expected 4 or 5 locations but found %d", len(locations))
                                for _, location := range locations {
                                        for _, line := range location.Line {
@@ -1677,6 +1661,15 @@ func TestGoroutineLeakProfileConcurrency(t *testing.T) {
                                        }
                                }
                                t.Errorf("%s", message)
+                               return
+                       }
+                       // We expect a receive operation. This is the typical stack.
+                       checkFrame(0, 0, locations, "runtime.gopark")
+                       checkFrame(1, 0, locations, "runtime.chanrecv")
+                       checkFrame(2, 0, locations, "runtime.chanrecv1")
+                       checkFrame(3, 0, locations, "runtime/pprof.goroutineLeakExample")
+                       if len(locations) == 5 {
+                               checkFrame(4, 0, locations, "runtime/pprof.TestGoroutineLeakProfileConcurrency.func5")
                        }
                }
        }