]> Cypherpunks repositories - gostls13.git/commitdiff
runtime/pprof: mark TestCPUProfileMultithreadMagnitude as flaky
authorMichael Pratt <mpratt@google.com>
Wed, 10 Nov 2021 22:35:13 +0000 (17:35 -0500)
committerMichael Pratt <mpratt@google.com>
Fri, 12 Nov 2021 20:02:21 +0000 (20:02 +0000)
The Linux kernel starting in 5.9 and fixed in 5.16 has a bug that can
break CPU timer signal delivery on new new threads if the timer
interrupt fires during handling of the clone system call.

Broken CPU timer signal deliver will skew CPU profile results and cause
this test to fail.

There is currently no known workaround, so mark the test as flaky on
builders with known broken kernels.

For #49065

Change-Id: I37ceb9ea244869b0aab5cd9a36b27ca2f7e5d315
Reviewed-on: https://go-review.googlesource.com/c/go/+/363214
Trust: Michael Pratt <mpratt@google.com>
Run-TryBot: Michael Pratt <mpratt@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
src/runtime/pprof/pprof_test.go
src/runtime/pprof/uname_linux_test.go [new file with mode: 0644]
src/runtime/pprof/uname_other_test.go [new file with mode: 0644]

index 704c0c516d3de0cc46b12922c9d2eb0759de0364..d9be00d030b8701524a2e360fae77de278e453a9 100644 (file)
@@ -116,6 +116,30 @@ func TestCPUProfileMultithreadMagnitude(t *testing.T) {
                t.Skip("issue 35057 is only confirmed on Linux")
        }
 
+       // Linux [5.9,5.16) has a kernel bug that can break CPU timers on newly
+       // created threads, breaking our CPU accounting.
+       major, minor, patch, err := linuxKernelVersion()
+       if err != nil {
+               t.Errorf("Error determining kernel version: %v", err)
+       }
+       t.Logf("Running on Linux %d.%d.%d", major, minor, patch)
+       defer func() {
+               if t.Failed() {
+                       t.Logf("Failure of this test may indicate that your system suffers from a known Linux kernel bug fixed on newer kernels. See https://golang.org/issue/49065.")
+               }
+       }()
+
+       // Disable on affected builders to avoid flakiness, but otherwise keep
+       // it enabled to potentially warn users that they are on a broken
+       // kernel.
+       if testenv.Builder() != "" && (runtime.GOARCH == "386" || runtime.GOARCH == "amd64") {
+               have59 := major > 5 || (major == 5 && minor >= 9)
+               have516 := major > 5 || (major == 5 && minor >= 16)
+               if have59 && !have516 {
+                       testenv.SkipFlaky(t, 49065)
+               }
+       }
+
        // Run a workload in a single goroutine, then run copies of the same
        // workload in several goroutines. For both the serial and parallel cases,
        // the CPU time the process measures with its own profiler should match the
diff --git a/src/runtime/pprof/uname_linux_test.go b/src/runtime/pprof/uname_linux_test.go
new file mode 100644 (file)
index 0000000..8374c83
--- /dev/null
@@ -0,0 +1,61 @@
+// Copyright 2021 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.
+
+//go:build linux
+
+package pprof
+
+import (
+       "fmt"
+       "regexp"
+       "strconv"
+       "syscall"
+)
+
+var versionRe = regexp.MustCompile(`^(\d+)(?:\.(\d+)(?:\.(\d+))).*$`)
+
+func linuxKernelVersion() (major, minor, patch int, err error) {
+       var uname syscall.Utsname
+       if err := syscall.Uname(&uname); err != nil {
+               return 0, 0, 0, err
+       }
+
+       buf := make([]byte, 0, len(uname.Release))
+       for _, b := range uname.Release {
+               if b == 0 {
+                       break
+               }
+               buf = append(buf, byte(b))
+       }
+       rl := string(buf)
+
+       m := versionRe.FindStringSubmatch(rl)
+       if m == nil {
+               return 0, 0, 0, fmt.Errorf("error matching version number in %q", rl)
+       }
+
+       v, err := strconv.ParseInt(m[1], 10, 64)
+       if err != nil {
+               return 0, 0, 0, fmt.Errorf("error parsing major version %q in %s: %w", m[1], rl, err)
+       }
+       major = int(v)
+
+       if len(m) >= 3 {
+               v, err := strconv.ParseInt(m[2], 10, 64)
+               if err != nil {
+                       return 0, 0, 0, fmt.Errorf("error parsing minor version %q in %s: %w", m[2], rl, err)
+               }
+               minor = int(v)
+       }
+
+       if len(m) >= 4 {
+               v, err := strconv.ParseInt(m[3], 10, 64)
+               if err != nil {
+                       return 0, 0, 0, fmt.Errorf("error parsing patch version %q in %s: %w", m[3], rl, err)
+               }
+               patch = int(v)
+       }
+
+       return
+}
diff --git a/src/runtime/pprof/uname_other_test.go b/src/runtime/pprof/uname_other_test.go
new file mode 100644 (file)
index 0000000..3276407
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2021 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.
+
+//go:build !linux
+
+package pprof
+
+import (
+       "errors"
+)
+
+func linuxKernelVersion() (major, minor, patch int, err error) {
+       return 0, 0, 0, errors.New("not running on linux")
+}