]> Cypherpunks repositories - gostls13.git/commitdiff
runtime/coverage: add coverage snapshot helper routine
authorThan McIntosh <thanm@google.com>
Fri, 12 May 2023 12:20:08 +0000 (08:20 -0400)
committerThan McIntosh <thanm@google.com>
Tue, 23 May 2023 11:37:04 +0000 (11:37 +0000)
Add a new function runtime/coverage.snapshot(), which samples the
current values of coverage counters in a running "go test -cover"
binary and returns percentage of statements executed so far. This
function is intended to be used by the function testing.Coverage().

Updates #59590.

Change-Id: I861393701c0cef47b4980aec14331168a9e64e8e
Reviewed-on: https://go-review.googlesource.com/c/go/+/495449
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
src/runtime/coverage/testsupport.go
src/runtime/coverage/ts_test.go

index 2b9e58b5f63bfeae124b00e432f56fb652bb0976..332b39b76eaea0dda873fc7cb0133f198a04a406 100644 (file)
@@ -15,7 +15,9 @@ import (
        "internal/coverage/pods"
        "io"
        "os"
+       "runtime/internal/atomic"
        "strings"
+       "unsafe"
 )
 
 // processCoverTestDir is called (via a linknamed reference) from
@@ -232,3 +234,48 @@ func (ts *tstate) processPod(p pods.Pod) error {
 type pkfunc struct {
        pk, fcn uint32
 }
+
+// snapshot returns a snapshot of coverage percentage at a moment of
+// time within a running test, so as to support the testing.Coverage()
+// function. This version doesn't examine coverage meta-data, so the
+// result it returns will be less accurate (more "slop") due to the
+// fact that we don't look at the meta data to see how many statements
+// are associated with each counter.
+func snapshot() float64 {
+       cl := getCovCounterList()
+       if len(cl) == 0 {
+               // no work to do here.
+               return 0.0
+       }
+
+       tot := uint64(0)
+       totExec := uint64(0)
+       for _, c := range cl {
+               sd := unsafe.Slice((*atomic.Uint32)(unsafe.Pointer(c.Counters)), c.Len)
+               tot += uint64(len(sd))
+               for i := 0; i < len(sd); i++ {
+                       // Skip ahead until the next non-zero value.
+                       if sd[i].Load() == 0 {
+                               continue
+                       }
+                       // We found a function that was executed.
+                       nCtrs := sd[i+coverage.NumCtrsOffset].Load()
+                       cst := i + coverage.FirstCtrOffset
+
+                       if cst+int(nCtrs) > len(sd) {
+                               break
+                       }
+                       counters := sd[cst : cst+int(nCtrs)]
+                       for i := range counters {
+                               if counters[i].Load() != 0 {
+                                       totExec++
+                               }
+                       }
+                       i += coverage.FirstCtrOffset + int(nCtrs) - 1
+               }
+       }
+       if tot == 0 {
+               return 0.0
+       }
+       return float64(totExec) / float64(tot)
+}
index a95d405a3e2d1cff718fa6323055a07e1884e4a8..19b307fd2604acec76d38786350c333e63db14dd 100644 (file)
@@ -53,3 +53,43 @@ func TestTestSupport(t *testing.T) {
                t.Fatalf("percent output missing token: %q", want)
        }
 }
+
+var funcInvoked bool
+
+//go:noinline
+func thisFunctionOnlyCalledFromSnapshotTest(n int) int {
+       if funcInvoked {
+               panic("bad")
+       }
+       funcInvoked = true
+
+       // Contents here not especially important, just so long as we
+       // have some statements.
+       t := 0
+       for i := 0; i < n; i++ {
+               for j := 0; j < i; j++ {
+                       t += i ^ j
+               }
+       }
+       return t
+}
+
+// Tests runtime/coverage.snapshot() directly. Note that if
+// coverage is not enabled, the hook is designed to just return
+// zero.
+func TestCoverageSnapshot(t *testing.T) {
+       C1 := snapshot()
+       thisFunctionOnlyCalledFromSnapshotTest(15)
+       C2 := snapshot()
+       cond := "C1 > C2"
+       val := C1 > C2
+       if testing.CoverMode() != "" {
+               cond = "C1 >= C2"
+               val = C1 >= C2
+       }
+       t.Logf("%f %f\n", C1, C2)
+       if val {
+               t.Errorf("erroneous snapshots, %s = true C1=%f C2=%f",
+                       cond, C1, C2)
+       }
+}