aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/coverage/testsupport.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2024-05-15 16:06:23 -0400
committerGopher Robot <gobot@golang.org>2024-05-21 19:41:02 +0000
commit180ea45566d19e60aa2d660f6139b7f6e18ff56b (patch)
treee14f155a336e4f5888334d2e0c8076ac8353f49a /src/runtime/coverage/testsupport.go
parent647870becc230b022b431a4ef8b7c9b31382db6c (diff)
downloadgo-180ea45566d19e60aa2d660f6139b7f6e18ff56b.tar.xz
runtime/coverage: remove uses of //go:linkname
Move code to internal/coverage/cfile, making it possible to access directly from testing/internal/testdeps, so that we can avoid needing //go:linkname hacks. For #67401. Change-Id: I10b23a9970164afd2165e718ef3b2d9e86783883 Reviewed-on: https://go-review.googlesource.com/c/go/+/585820 Auto-Submit: Russ Cox <rsc@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
Diffstat (limited to 'src/runtime/coverage/testsupport.go')
-rw-r--r--src/runtime/coverage/testsupport.go329
1 files changed, 0 insertions, 329 deletions
diff --git a/src/runtime/coverage/testsupport.go b/src/runtime/coverage/testsupport.go
deleted file mode 100644
index b673d3cd2c..0000000000
--- a/src/runtime/coverage/testsupport.go
+++ /dev/null
@@ -1,329 +0,0 @@
-// Copyright 2022 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 coverage
-
-import (
- "encoding/json"
- "fmt"
- "internal/coverage"
- "internal/coverage/calloc"
- "internal/coverage/cformat"
- "internal/coverage/cmerge"
- "internal/coverage/decodecounter"
- "internal/coverage/decodemeta"
- "internal/coverage/pods"
- "internal/runtime/atomic"
- "io"
- "os"
- "path/filepath"
- "strings"
- "unsafe"
-)
-
-// processCoverTestDir is injected in testmain.
-//go:linkname processCoverTestDir
-
-// processCoverTestDir is called (via a linknamed reference) from
-// testmain code when "go test -cover" is in effect. It is not
-// intended to be used other than internally by the Go command's
-// generated code.
-func processCoverTestDir(dir string, cfile string, cm string, cpkg string) error {
- return processCoverTestDirInternal(dir, cfile, cm, cpkg, os.Stdout)
-}
-
-// processCoverTestDirInternal is an io.Writer version of processCoverTestDir,
-// exposed for unit testing.
-func processCoverTestDirInternal(dir string, cfile string, cm string, cpkg string, w io.Writer) error {
- cmode := coverage.ParseCounterMode(cm)
- if cmode == coverage.CtrModeInvalid {
- return fmt.Errorf("invalid counter mode %q", cm)
- }
-
- // Emit meta-data and counter data.
- ml := getCovMetaList()
- if len(ml) == 0 {
- // This corresponds to the case where we have a package that
- // contains test code but no functions (which is fine). In this
- // case there is no need to emit anything.
- } else {
- if err := emitMetaDataToDirectory(dir, ml); err != nil {
- return err
- }
- if err := emitCounterDataToDirectory(dir); err != nil {
- return err
- }
- }
-
- // Collect pods from test run. For the majority of cases we would
- // expect to see a single pod here, but allow for multiple pods in
- // case the test harness is doing extra work to collect data files
- // from builds that it kicks off as part of the testing.
- podlist, err := pods.CollectPods([]string{dir}, false)
- if err != nil {
- return fmt.Errorf("reading from %s: %v", dir, err)
- }
-
- // Open text output file if appropriate.
- var tf *os.File
- var tfClosed bool
- if cfile != "" {
- var err error
- tf, err = os.Create(cfile)
- if err != nil {
- return fmt.Errorf("internal error: opening coverage data output file %q: %v", cfile, err)
- }
- defer func() {
- if !tfClosed {
- tfClosed = true
- tf.Close()
- }
- }()
- }
-
- // Read/process the pods.
- ts := &tstate{
- cm: &cmerge.Merger{},
- cf: cformat.NewFormatter(cmode),
- cmode: cmode,
- }
- // Generate the expected hash string based on the final meta-data
- // hash for this test, then look only for pods that refer to that
- // hash (just in case there are multiple instrumented executables
- // in play). See issue #57924 for more on this.
- hashstring := fmt.Sprintf("%x", finalHash)
- importpaths := make(map[string]struct{})
- for _, p := range podlist {
- if !strings.Contains(p.MetaFile, hashstring) {
- continue
- }
- if err := ts.processPod(p, importpaths); err != nil {
- return err
- }
- }
-
- metafilespath := filepath.Join(dir, coverage.MetaFilesFileName)
- if _, err := os.Stat(metafilespath); err == nil {
- if err := ts.readAuxMetaFiles(metafilespath, importpaths); err != nil {
- return err
- }
- }
-
- // Emit percent.
- if err := ts.cf.EmitPercent(w, cpkg, true, true); err != nil {
- return err
- }
-
- // Emit text output.
- if tf != nil {
- if err := ts.cf.EmitTextual(tf); err != nil {
- return err
- }
- tfClosed = true
- if err := tf.Close(); err != nil {
- return fmt.Errorf("closing %s: %v", cfile, err)
- }
- }
-
- return nil
-}
-
-type tstate struct {
- calloc.BatchCounterAlloc
- cm *cmerge.Merger
- cf *cformat.Formatter
- cmode coverage.CounterMode
-}
-
-// processPod reads coverage counter data for a specific pod.
-func (ts *tstate) processPod(p pods.Pod, importpaths map[string]struct{}) error {
- // Open meta-data file
- f, err := os.Open(p.MetaFile)
- if err != nil {
- return fmt.Errorf("unable to open meta-data file %s: %v", p.MetaFile, err)
- }
- defer func() {
- f.Close()
- }()
- var mfr *decodemeta.CoverageMetaFileReader
- mfr, err = decodemeta.NewCoverageMetaFileReader(f, nil)
- if err != nil {
- return fmt.Errorf("error reading meta-data file %s: %v", p.MetaFile, err)
- }
- newmode := mfr.CounterMode()
- if newmode != ts.cmode {
- return fmt.Errorf("internal error: counter mode clash: %q from test harness, %q from data file %s", ts.cmode.String(), newmode.String(), p.MetaFile)
- }
- newgran := mfr.CounterGranularity()
- if err := ts.cm.SetModeAndGranularity(p.MetaFile, cmode, newgran); err != nil {
- return err
- }
-
- // A map to store counter data, indexed by pkgid/fnid tuple.
- pmm := make(map[pkfunc][]uint32)
-
- // Helper to read a single counter data file.
- readcdf := func(cdf string) error {
- cf, err := os.Open(cdf)
- if err != nil {
- return fmt.Errorf("opening counter data file %s: %s", cdf, err)
- }
- defer cf.Close()
- var cdr *decodecounter.CounterDataReader
- cdr, err = decodecounter.NewCounterDataReader(cdf, cf)
- if err != nil {
- return fmt.Errorf("reading counter data file %s: %s", cdf, err)
- }
- var data decodecounter.FuncPayload
- for {
- ok, err := cdr.NextFunc(&data)
- if err != nil {
- return fmt.Errorf("reading counter data file %s: %v", cdf, err)
- }
- if !ok {
- break
- }
-
- // NB: sanity check on pkg and func IDs?
- key := pkfunc{pk: data.PkgIdx, fcn: data.FuncIdx}
- if prev, found := pmm[key]; found {
- // Note: no overflow reporting here.
- if err, _ := ts.cm.MergeCounters(data.Counters, prev); err != nil {
- return fmt.Errorf("processing counter data file %s: %v", cdf, err)
- }
- }
- c := ts.AllocateCounters(len(data.Counters))
- copy(c, data.Counters)
- pmm[key] = c
- }
- return nil
- }
-
- // Read counter data files.
- for _, cdf := range p.CounterDataFiles {
- if err := readcdf(cdf); err != nil {
- return err
- }
- }
-
- // Visit meta-data file.
- np := uint32(mfr.NumPackages())
- payload := []byte{}
- for pkIdx := uint32(0); pkIdx < np; pkIdx++ {
- var pd *decodemeta.CoverageMetaDataDecoder
- pd, payload, err = mfr.GetPackageDecoder(pkIdx, payload)
- if err != nil {
- return fmt.Errorf("reading pkg %d from meta-file %s: %s", pkIdx, p.MetaFile, err)
- }
- ts.cf.SetPackage(pd.PackagePath())
- importpaths[pd.PackagePath()] = struct{}{}
- var fd coverage.FuncDesc
- nf := pd.NumFuncs()
- for fnIdx := uint32(0); fnIdx < nf; fnIdx++ {
- if err := pd.ReadFunc(fnIdx, &fd); err != nil {
- return fmt.Errorf("reading meta-data file %s: %v",
- p.MetaFile, err)
- }
- key := pkfunc{pk: pkIdx, fcn: fnIdx}
- counters, haveCounters := pmm[key]
- for i := 0; i < len(fd.Units); i++ {
- u := fd.Units[i]
- // Skip units with non-zero parent (no way to represent
- // these in the existing format).
- if u.Parent != 0 {
- continue
- }
- count := uint32(0)
- if haveCounters {
- count = counters[i]
- }
- ts.cf.AddUnit(fd.Srcfile, fd.Funcname, fd.Lit, u, count)
- }
- }
- }
- return nil
-}
-
-type pkfunc struct {
- pk, fcn uint32
-}
-
-func (ts *tstate) readAuxMetaFiles(metafiles string, importpaths map[string]struct{}) error {
- // Unmarshal the information on available aux metafiles into
- // a MetaFileCollection struct.
- var mfc coverage.MetaFileCollection
- data, err := os.ReadFile(metafiles)
- if err != nil {
- return fmt.Errorf("error reading auxmetafiles file %q: %v", metafiles, err)
- }
- if err := json.Unmarshal(data, &mfc); err != nil {
- return fmt.Errorf("error reading auxmetafiles file %q: %v", metafiles, err)
- }
-
- // Walk through each available aux meta-file. If we've already
- // seen the package path in question during the walk of the
- // "regular" meta-data file, then we can skip the package,
- // otherwise construct a dummy pod with the single meta-data file
- // (no counters) and invoke processPod on it.
- for i := range mfc.ImportPaths {
- p := mfc.ImportPaths[i]
- if _, ok := importpaths[p]; ok {
- continue
- }
- var pod pods.Pod
- pod.MetaFile = mfc.MetaFileFragments[i]
- if err := ts.processPod(pod, importpaths); err != nil {
- return err
- }
- }
- return nil
-}
-
-// snapshot is injected in testmain.
-//go:linkname snapshot
-
-// 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)
-}