aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/coverage
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
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')
-rw-r--r--src/runtime/coverage/apis.go178
-rw-r--r--src/runtime/coverage/coverage.go66
-rw-r--r--src/runtime/coverage/dummy.s8
-rw-r--r--src/runtime/coverage/emit.go612
-rw-r--r--src/runtime/coverage/emitdata_test.go550
-rw-r--r--src/runtime/coverage/hooks.go42
-rw-r--r--src/runtime/coverage/testdata/harness.go259
-rw-r--r--src/runtime/coverage/testdata/issue56006/repro.go26
-rw-r--r--src/runtime/coverage/testdata/issue56006/repro_test.go8
-rw-r--r--src/runtime/coverage/testdata/issue59563/repro.go823
-rw-r--r--src/runtime/coverage/testdata/issue59563/repro_test.go14
-rw-r--r--src/runtime/coverage/testsupport.go329
-rw-r--r--src/runtime/coverage/ts_test.go207
13 files changed, 66 insertions, 3056 deletions
diff --git a/src/runtime/coverage/apis.go b/src/runtime/coverage/apis.go
deleted file mode 100644
index 15ba04a86f..0000000000
--- a/src/runtime/coverage/apis.go
+++ /dev/null
@@ -1,178 +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 (
- "fmt"
- "internal/coverage"
- "io"
- "sync/atomic"
- "unsafe"
-)
-
-// WriteMetaDir writes a coverage meta-data file for the currently
-// running program to the directory specified in 'dir'. An error will
-// be returned if the operation can't be completed successfully (for
-// example, if the currently running program was not built with
-// "-cover", or if the directory does not exist).
-func WriteMetaDir(dir string) error {
- if !finalHashComputed {
- return fmt.Errorf("error: no meta-data available (binary not built with -cover?)")
- }
- return emitMetaDataToDirectory(dir, getCovMetaList())
-}
-
-// WriteMeta writes the meta-data content (the payload that would
-// normally be emitted to a meta-data file) for the currently running
-// program to the writer 'w'. An error will be returned if the
-// operation can't be completed successfully (for example, if the
-// currently running program was not built with "-cover", or if a
-// write fails).
-func WriteMeta(w io.Writer) error {
- if w == nil {
- return fmt.Errorf("error: nil writer in WriteMeta")
- }
- if !finalHashComputed {
- return fmt.Errorf("error: no meta-data available (binary not built with -cover?)")
- }
- ml := getCovMetaList()
- return writeMetaData(w, ml, cmode, cgran, finalHash)
-}
-
-// WriteCountersDir writes a coverage counter-data file for the
-// currently running program to the directory specified in 'dir'. An
-// error will be returned if the operation can't be completed
-// successfully (for example, if the currently running program was not
-// built with "-cover", or if the directory does not exist). The
-// counter data written will be a snapshot taken at the point of the
-// call.
-func WriteCountersDir(dir string) error {
- if cmode != coverage.CtrModeAtomic {
- return fmt.Errorf("WriteCountersDir invoked for program built with -covermode=%s (please use -covermode=atomic)", cmode.String())
- }
- return emitCounterDataToDirectory(dir)
-}
-
-// WriteCounters writes coverage counter-data content for the
-// currently running program to the writer 'w'. An error will be
-// returned if the operation can't be completed successfully (for
-// example, if the currently running program was not built with
-// "-cover", or if a write fails). The counter data written will be a
-// snapshot taken at the point of the invocation.
-func WriteCounters(w io.Writer) error {
- if w == nil {
- return fmt.Errorf("error: nil writer in WriteCounters")
- }
- if cmode != coverage.CtrModeAtomic {
- return fmt.Errorf("WriteCounters invoked for program built with -covermode=%s (please use -covermode=atomic)", cmode.String())
- }
- // Ask the runtime for the list of coverage counter symbols.
- cl := getCovCounterList()
- if len(cl) == 0 {
- return fmt.Errorf("program not built with -cover")
- }
- if !finalHashComputed {
- return fmt.Errorf("meta-data not written yet, unable to write counter data")
- }
-
- pm := getCovPkgMap()
- s := &emitState{
- counterlist: cl,
- pkgmap: pm,
- }
- return s.emitCounterDataToWriter(w)
-}
-
-// ClearCounters clears/resets all coverage counter variables in the
-// currently running program. It returns an error if the program in
-// question was not built with the "-cover" flag. Clearing of coverage
-// counters is also not supported for programs not using atomic
-// counter mode (see more detailed comments below for the rationale
-// here).
-func ClearCounters() error {
- cl := getCovCounterList()
- if len(cl) == 0 {
- return fmt.Errorf("program not built with -cover")
- }
- if cmode != coverage.CtrModeAtomic {
- return fmt.Errorf("ClearCounters invoked for program built with -covermode=%s (please use -covermode=atomic)", cmode.String())
- }
-
- // Implementation note: this function would be faster and simpler
- // if we could just zero out the entire counter array, but for the
- // moment we go through and zero out just the slots in the array
- // corresponding to the counter values. We do this to avoid the
- // following bad scenario: suppose that a user builds their Go
- // program with "-cover", and that program has a function (call it
- // main.XYZ) that invokes ClearCounters:
- //
- // func XYZ() {
- // ... do some stuff ...
- // coverage.ClearCounters()
- // if someCondition { <<--- HERE
- // ...
- // }
- // }
- //
- // At the point where ClearCounters executes, main.XYZ has not yet
- // finished running, thus as soon as the call returns the line
- // marked "HERE" above will trigger the writing of a non-zero
- // value into main.XYZ's counter slab. However since we've just
- // finished clearing the entire counter segment, we will have lost
- // the values in the prolog portion of main.XYZ's counter slab
- // (nctrs, pkgid, funcid). This means that later on at the end of
- // program execution as we walk through the entire counter array
- // for the program looking for executed functions, we'll zoom past
- // main.XYZ's prolog (which was zero'd) and hit the non-zero
- // counter value corresponding to the "HERE" block, which will
- // then be interpreted as the start of another live function.
- // Things will go downhill from there.
- //
- // This same scenario is also a potential risk if the program is
- // running on an architecture that permits reordering of
- // writes/stores, since the inconsistency described above could
- // arise here. Example scenario:
- //
- // func ABC() {
- // ... // prolog
- // if alwaysTrue() {
- // XYZ() // counter update here
- // }
- // }
- //
- // In the instrumented version of ABC, the prolog of the function
- // will contain a series of stores to the initial portion of the
- // counter array to write number-of-counters, pkgid, funcid. Later
- // in the function there is also a store to increment a counter
- // for the block containing the call to XYZ(). If the CPU is
- // allowed to reorder stores and decides to issue the XYZ store
- // before the prolog stores, this could be observable as an
- // inconsistency similar to the one above. Hence the requirement
- // for atomic counter mode: according to package atomic docs,
- // "...operations that happen in a specific order on one thread,
- // will always be observed to happen in exactly that order by
- // another thread". Thus we can be sure that there will be no
- // inconsistency when reading the counter array from the thread
- // running ClearCounters.
-
- for _, c := range cl {
- sd := unsafe.Slice((*atomic.Uint32)(unsafe.Pointer(c.Counters)), int(c.Len))
- for i := 0; i < len(sd); i++ {
- // Skip ahead until the next non-zero value.
- sdi := sd[i].Load()
- if sdi == 0 {
- continue
- }
- // We found a function that was executed; clear its counters.
- nCtrs := sdi
- for j := 0; j < int(nCtrs); j++ {
- sd[i+coverage.FirstCtrOffset+j].Store(0)
- }
- // Move to next function.
- i += coverage.FirstCtrOffset + int(nCtrs) - 1
- }
- }
- return nil
-}
diff --git a/src/runtime/coverage/coverage.go b/src/runtime/coverage/coverage.go
new file mode 100644
index 0000000000..6b99a0bce6
--- /dev/null
+++ b/src/runtime/coverage/coverage.go
@@ -0,0 +1,66 @@
+// 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 (
+ "internal/coverage/cfile"
+ "io"
+)
+
+// initHook is invoked from main.init in programs built with -cover.
+// The call is emitted by the compiler.
+func initHook(istest bool) {
+ cfile.InitHook(istest)
+}
+
+// WriteMetaDir writes a coverage meta-data file for the currently
+// running program to the directory specified in 'dir'. An error will
+// be returned if the operation can't be completed successfully (for
+// example, if the currently running program was not built with
+// "-cover", or if the directory does not exist).
+func WriteMetaDir(dir string) error {
+ return cfile.WriteMetaDir(dir)
+}
+
+// WriteMeta writes the meta-data content (the payload that would
+// normally be emitted to a meta-data file) for the currently running
+// program to the writer 'w'. An error will be returned if the
+// operation can't be completed successfully (for example, if the
+// currently running program was not built with "-cover", or if a
+// write fails).
+func WriteMeta(w io.Writer) error {
+ return cfile.WriteMeta(w)
+}
+
+// WriteCountersDir writes a coverage counter-data file for the
+// currently running program to the directory specified in 'dir'. An
+// error will be returned if the operation can't be completed
+// successfully (for example, if the currently running program was not
+// built with "-cover", or if the directory does not exist). The
+// counter data written will be a snapshot taken at the point of the
+// call.
+func WriteCountersDir(dir string) error {
+ return cfile.WriteCountersDir(dir)
+}
+
+// WriteCounters writes coverage counter-data content for the
+// currently running program to the writer 'w'. An error will be
+// returned if the operation can't be completed successfully (for
+// example, if the currently running program was not built with
+// "-cover", or if a write fails). The counter data written will be a
+// snapshot taken at the point of the invocation.
+func WriteCounters(w io.Writer) error {
+ return cfile.WriteCounters(w)
+}
+
+// ClearCounters clears/resets all coverage counter variables in the
+// currently running program. It returns an error if the program in
+// question was not built with the "-cover" flag. Clearing of coverage
+// counters is also not supported for programs not using atomic
+// counter mode (see more detailed comments below for the rationale
+// here).
+func ClearCounters() error {
+ return cfile.ClearCounters()
+}
diff --git a/src/runtime/coverage/dummy.s b/src/runtime/coverage/dummy.s
deleted file mode 100644
index 75928593a0..0000000000
--- a/src/runtime/coverage/dummy.s
+++ /dev/null
@@ -1,8 +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.
-
-// The runtime package uses //go:linkname to push a few functions into this
-// package but we still need a .s file so the Go tool does not pass -complete
-// to 'go tool compile' so the latter does not complain about Go functions
-// with no bodies.
diff --git a/src/runtime/coverage/emit.go b/src/runtime/coverage/emit.go
deleted file mode 100644
index 6510c889ea..0000000000
--- a/src/runtime/coverage/emit.go
+++ /dev/null
@@ -1,612 +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 (
- "crypto/md5"
- "fmt"
- "internal/coverage"
- "internal/coverage/encodecounter"
- "internal/coverage/encodemeta"
- "internal/coverage/rtcov"
- "io"
- "os"
- "path/filepath"
- "runtime"
- "strconv"
- "sync/atomic"
- "time"
- "unsafe"
-)
-
-// This file contains functions that support the writing of data files
-// emitted at the end of code coverage testing runs, from instrumented
-// executables.
-
-// getCovMetaList returns a list of meta-data blobs registered
-// for the currently executing instrumented program. It is defined in the
-// runtime.
-func getCovMetaList() []rtcov.CovMetaBlob
-
-// getCovCounterList returns a list of counter-data blobs registered
-// for the currently executing instrumented program. It is defined in the
-// runtime.
-func getCovCounterList() []rtcov.CovCounterBlob
-
-// getCovPkgMap returns a map storing the remapped package IDs for
-// hard-coded runtime packages (see internal/coverage/pkgid.go for
-// more on why hard-coded package IDs are needed). This function
-// is defined in the runtime.
-func getCovPkgMap() map[int]int
-
-// emitState holds useful state information during the emit process.
-//
-// When an instrumented program finishes execution and starts the
-// process of writing out coverage data, it's possible that an
-// existing meta-data file already exists in the output directory. In
-// this case openOutputFiles() below will leave the 'mf' field below
-// as nil. If a new meta-data file is needed, field 'mfname' will be
-// the final desired path of the meta file, 'mftmp' will be a
-// temporary file, and 'mf' will be an open os.File pointer for
-// 'mftmp'. The meta-data file payload will be written to 'mf', the
-// temp file will be then closed and renamed (from 'mftmp' to
-// 'mfname'), so as to insure that the meta-data file is created
-// atomically; we want this so that things work smoothly in cases
-// where there are several instances of a given instrumented program
-// all terminating at the same time and trying to create meta-data
-// files simultaneously.
-//
-// For counter data files there is less chance of a collision, hence
-// the openOutputFiles() stores the counter data file in 'cfname' and
-// then places the *io.File into 'cf'.
-type emitState struct {
- mfname string // path of final meta-data output file
- mftmp string // path to meta-data temp file (if needed)
- mf *os.File // open os.File for meta-data temp file
- cfname string // path of final counter data file
- cftmp string // path to counter data temp file
- cf *os.File // open os.File for counter data file
- outdir string // output directory
-
- // List of meta-data symbols obtained from the runtime
- metalist []rtcov.CovMetaBlob
-
- // List of counter-data symbols obtained from the runtime
- counterlist []rtcov.CovCounterBlob
-
- // Table to use for remapping hard-coded pkg ids.
- pkgmap map[int]int
-
- // emit debug trace output
- debug bool
-}
-
-var (
- // finalHash is computed at init time from the list of meta-data
- // symbols registered during init. It is used both for writing the
- // meta-data file and counter-data files.
- finalHash [16]byte
- // Set to true when we've computed finalHash + finalMetaLen.
- finalHashComputed bool
- // Total meta-data length.
- finalMetaLen uint64
- // Records whether we've already attempted to write meta-data.
- metaDataEmitAttempted bool
- // Counter mode for this instrumented program run.
- cmode coverage.CounterMode
- // Counter granularity for this instrumented program run.
- cgran coverage.CounterGranularity
- // Cached value of GOCOVERDIR environment variable.
- goCoverDir string
- // Copy of os.Args made at init time, converted into map format.
- capturedOsArgs map[string]string
- // Flag used in tests to signal that coverage data already written.
- covProfileAlreadyEmitted bool
-)
-
-// fileType is used to select between counter-data files and
-// meta-data files.
-type fileType int
-
-const (
- noFile = 1 << iota
- metaDataFile
- counterDataFile
-)
-
-// emitMetaData emits the meta-data output file for this coverage run.
-// This entry point is intended to be invoked by the compiler from
-// an instrumented program's main package init func.
-func emitMetaData() {
- if covProfileAlreadyEmitted {
- return
- }
- ml, err := prepareForMetaEmit()
- if err != nil {
- fmt.Fprintf(os.Stderr, "error: coverage meta-data prep failed: %v\n", err)
- if os.Getenv("GOCOVERDEBUG") != "" {
- panic("meta-data write failure")
- }
- }
- if len(ml) == 0 {
- fmt.Fprintf(os.Stderr, "program not built with -cover\n")
- return
- }
-
- goCoverDir = os.Getenv("GOCOVERDIR")
- if goCoverDir == "" {
- fmt.Fprintf(os.Stderr, "warning: GOCOVERDIR not set, no coverage data emitted\n")
- return
- }
-
- if err := emitMetaDataToDirectory(goCoverDir, ml); err != nil {
- fmt.Fprintf(os.Stderr, "error: coverage meta-data emit failed: %v\n", err)
- if os.Getenv("GOCOVERDEBUG") != "" {
- panic("meta-data write failure")
- }
- }
-}
-
-func modeClash(m coverage.CounterMode) bool {
- if m == coverage.CtrModeRegOnly || m == coverage.CtrModeTestMain {
- return false
- }
- if cmode == coverage.CtrModeInvalid {
- cmode = m
- return false
- }
- return cmode != m
-}
-
-func granClash(g coverage.CounterGranularity) bool {
- if cgran == coverage.CtrGranularityInvalid {
- cgran = g
- return false
- }
- return cgran != g
-}
-
-// prepareForMetaEmit performs preparatory steps needed prior to
-// emitting a meta-data file, notably computing a final hash of
-// all meta-data blobs and capturing os args.
-func prepareForMetaEmit() ([]rtcov.CovMetaBlob, error) {
- // Ask the runtime for the list of coverage meta-data symbols.
- ml := getCovMetaList()
-
- // In the normal case (go build -o prog.exe ... ; ./prog.exe)
- // len(ml) will always be non-zero, but we check here since at
- // some point this function will be reachable via user-callable
- // APIs (for example, to write out coverage data from a server
- // program that doesn't ever call os.Exit).
- if len(ml) == 0 {
- return nil, nil
- }
-
- s := &emitState{
- metalist: ml,
- debug: os.Getenv("GOCOVERDEBUG") != "",
- }
-
- // Capture os.Args() now so as to avoid issues if args
- // are rewritten during program execution.
- capturedOsArgs = captureOsArgs()
-
- if s.debug {
- fmt.Fprintf(os.Stderr, "=+= GOCOVERDIR is %s\n", os.Getenv("GOCOVERDIR"))
- fmt.Fprintf(os.Stderr, "=+= contents of covmetalist:\n")
- for k, b := range ml {
- fmt.Fprintf(os.Stderr, "=+= slot: %d path: %s ", k, b.PkgPath)
- if b.PkgID != -1 {
- fmt.Fprintf(os.Stderr, " hcid: %d", b.PkgID)
- }
- fmt.Fprintf(os.Stderr, "\n")
- }
- pm := getCovPkgMap()
- fmt.Fprintf(os.Stderr, "=+= remap table:\n")
- for from, to := range pm {
- fmt.Fprintf(os.Stderr, "=+= from %d to %d\n",
- uint32(from), uint32(to))
- }
- }
-
- h := md5.New()
- tlen := uint64(unsafe.Sizeof(coverage.MetaFileHeader{}))
- for _, entry := range ml {
- if _, err := h.Write(entry.Hash[:]); err != nil {
- return nil, err
- }
- tlen += uint64(entry.Len)
- ecm := coverage.CounterMode(entry.CounterMode)
- if modeClash(ecm) {
- return nil, fmt.Errorf("coverage counter mode clash: package %s uses mode=%d, but package %s uses mode=%s\n", ml[0].PkgPath, cmode, entry.PkgPath, ecm)
- }
- ecg := coverage.CounterGranularity(entry.CounterGranularity)
- if granClash(ecg) {
- return nil, fmt.Errorf("coverage counter granularity clash: package %s uses gran=%d, but package %s uses gran=%s\n", ml[0].PkgPath, cgran, entry.PkgPath, ecg)
- }
- }
-
- // Hash mode and granularity as well.
- h.Write([]byte(cmode.String()))
- h.Write([]byte(cgran.String()))
-
- // Compute final digest.
- fh := h.Sum(nil)
- copy(finalHash[:], fh)
- finalHashComputed = true
- finalMetaLen = tlen
-
- return ml, nil
-}
-
-// emitMetaDataToDirectory emits the meta-data output file to the specified
-// directory, returning an error if something went wrong.
-func emitMetaDataToDirectory(outdir string, ml []rtcov.CovMetaBlob) error {
- ml, err := prepareForMetaEmit()
- if err != nil {
- return err
- }
- if len(ml) == 0 {
- return nil
- }
-
- metaDataEmitAttempted = true
-
- s := &emitState{
- metalist: ml,
- debug: os.Getenv("GOCOVERDEBUG") != "",
- outdir: outdir,
- }
-
- // Open output files.
- if err := s.openOutputFiles(finalHash, finalMetaLen, metaDataFile); err != nil {
- return err
- }
-
- // Emit meta-data file only if needed (may already be present).
- if s.needMetaDataFile() {
- if err := s.emitMetaDataFile(finalHash, finalMetaLen); err != nil {
- return err
- }
- }
- return nil
-}
-
-// emitCounterData emits the counter data output file for this coverage run.
-// This entry point is intended to be invoked by the runtime when an
-// instrumented program is terminating or calling os.Exit().
-func emitCounterData() {
- if goCoverDir == "" || !finalHashComputed || covProfileAlreadyEmitted {
- return
- }
- if err := emitCounterDataToDirectory(goCoverDir); err != nil {
- fmt.Fprintf(os.Stderr, "error: coverage counter data emit failed: %v\n", err)
- if os.Getenv("GOCOVERDEBUG") != "" {
- panic("counter-data write failure")
- }
- }
-}
-
-// emitCounterDataToDirectory emits the counter-data output file for this coverage run.
-func emitCounterDataToDirectory(outdir string) error {
- // Ask the runtime for the list of coverage counter symbols.
- cl := getCovCounterList()
- if len(cl) == 0 {
- // no work to do here.
- return nil
- }
-
- if !finalHashComputed {
- return fmt.Errorf("error: meta-data not available (binary not built with -cover?)")
- }
-
- // Ask the runtime for the list of coverage counter symbols.
- pm := getCovPkgMap()
- s := &emitState{
- counterlist: cl,
- pkgmap: pm,
- outdir: outdir,
- debug: os.Getenv("GOCOVERDEBUG") != "",
- }
-
- // Open output file.
- if err := s.openOutputFiles(finalHash, finalMetaLen, counterDataFile); err != nil {
- return err
- }
- if s.cf == nil {
- return fmt.Errorf("counter data output file open failed (no additional info")
- }
-
- // Emit counter data file.
- if err := s.emitCounterDataFile(finalHash, s.cf); err != nil {
- return err
- }
- if err := s.cf.Close(); err != nil {
- return fmt.Errorf("closing counter data file: %v", err)
- }
-
- // Counter file has now been closed. Rename the temp to the
- // final desired path.
- if err := os.Rename(s.cftmp, s.cfname); err != nil {
- return fmt.Errorf("writing %s: rename from %s failed: %v\n", s.cfname, s.cftmp, err)
- }
-
- return nil
-}
-
-// emitCounterDataToWriter emits counter data for this coverage run to an io.Writer.
-func (s *emitState) emitCounterDataToWriter(w io.Writer) error {
- if err := s.emitCounterDataFile(finalHash, w); err != nil {
- return err
- }
- return nil
-}
-
-// openMetaFile determines whether we need to emit a meta-data output
-// file, or whether we can reuse the existing file in the coverage out
-// dir. It updates mfname/mftmp/mf fields in 's', returning an error
-// if something went wrong. See the comment on the emitState type
-// definition above for more on how file opening is managed.
-func (s *emitState) openMetaFile(metaHash [16]byte, metaLen uint64) error {
-
- // Open meta-outfile for reading to see if it exists.
- fn := fmt.Sprintf("%s.%x", coverage.MetaFilePref, metaHash)
- s.mfname = filepath.Join(s.outdir, fn)
- fi, err := os.Stat(s.mfname)
- if err != nil || fi.Size() != int64(metaLen) {
- // We need a new meta-file.
- tname := "tmp." + fn + strconv.FormatInt(time.Now().UnixNano(), 10)
- s.mftmp = filepath.Join(s.outdir, tname)
- s.mf, err = os.Create(s.mftmp)
- if err != nil {
- return fmt.Errorf("creating meta-data file %s: %v", s.mftmp, err)
- }
- }
- return nil
-}
-
-// openCounterFile opens an output file for the counter data portion
-// of a test coverage run. If updates the 'cfname' and 'cf' fields in
-// 's', returning an error if something went wrong.
-func (s *emitState) openCounterFile(metaHash [16]byte) error {
- processID := os.Getpid()
- fn := fmt.Sprintf(coverage.CounterFileTempl, coverage.CounterFilePref, metaHash, processID, time.Now().UnixNano())
- s.cfname = filepath.Join(s.outdir, fn)
- s.cftmp = filepath.Join(s.outdir, "tmp."+fn)
- var err error
- s.cf, err = os.Create(s.cftmp)
- if err != nil {
- return fmt.Errorf("creating counter data file %s: %v", s.cftmp, err)
- }
- return nil
-}
-
-// openOutputFiles opens output files in preparation for emitting
-// coverage data. In the case of the meta-data file, openOutputFiles
-// may determine that we can reuse an existing meta-data file in the
-// outdir, in which case it will leave the 'mf' field in the state
-// struct as nil. If a new meta-file is needed, the field 'mfname'
-// will be the final desired path of the meta file, 'mftmp' will be a
-// temporary file, and 'mf' will be an open os.File pointer for
-// 'mftmp'. The idea is that the client/caller will write content into
-// 'mf', close it, and then rename 'mftmp' to 'mfname'. This function
-// also opens the counter data output file, setting 'cf' and 'cfname'
-// in the state struct.
-func (s *emitState) openOutputFiles(metaHash [16]byte, metaLen uint64, which fileType) error {
- fi, err := os.Stat(s.outdir)
- if err != nil {
- return fmt.Errorf("output directory %q inaccessible (err: %v); no coverage data written", s.outdir, err)
- }
- if !fi.IsDir() {
- return fmt.Errorf("output directory %q not a directory; no coverage data written", s.outdir)
- }
-
- if (which & metaDataFile) != 0 {
- if err := s.openMetaFile(metaHash, metaLen); err != nil {
- return err
- }
- }
- if (which & counterDataFile) != 0 {
- if err := s.openCounterFile(metaHash); err != nil {
- return err
- }
- }
- return nil
-}
-
-// emitMetaDataFile emits coverage meta-data to a previously opened
-// temporary file (s.mftmp), then renames the generated file to the
-// final path (s.mfname).
-func (s *emitState) emitMetaDataFile(finalHash [16]byte, tlen uint64) error {
- if err := writeMetaData(s.mf, s.metalist, cmode, cgran, finalHash); err != nil {
- return fmt.Errorf("writing %s: %v\n", s.mftmp, err)
- }
- if err := s.mf.Close(); err != nil {
- return fmt.Errorf("closing meta data temp file: %v", err)
- }
-
- // Temp file has now been flushed and closed. Rename the temp to the
- // final desired path.
- if err := os.Rename(s.mftmp, s.mfname); err != nil {
- return fmt.Errorf("writing %s: rename from %s failed: %v\n", s.mfname, s.mftmp, err)
- }
-
- return nil
-}
-
-// needMetaDataFile returns TRUE if we need to emit a meta-data file
-// for this program run. It should be used only after
-// openOutputFiles() has been invoked.
-func (s *emitState) needMetaDataFile() bool {
- return s.mf != nil
-}
-
-func writeMetaData(w io.Writer, metalist []rtcov.CovMetaBlob, cmode coverage.CounterMode, gran coverage.CounterGranularity, finalHash [16]byte) error {
- mfw := encodemeta.NewCoverageMetaFileWriter("<io.Writer>", w)
-
- var blobs [][]byte
- for _, e := range metalist {
- sd := unsafe.Slice(e.P, int(e.Len))
- blobs = append(blobs, sd)
- }
- return mfw.Write(finalHash, blobs, cmode, gran)
-}
-
-func (s *emitState) VisitFuncs(f encodecounter.CounterVisitorFn) error {
- var tcounters []uint32
-
- rdCounters := func(actrs []atomic.Uint32, ctrs []uint32) []uint32 {
- ctrs = ctrs[:0]
- for i := range actrs {
- ctrs = append(ctrs, actrs[i].Load())
- }
- return ctrs
- }
-
- dpkg := uint32(0)
- for _, c := range s.counterlist {
- sd := unsafe.Slice((*atomic.Uint32)(unsafe.Pointer(c.Counters)), int(c.Len))
- for i := 0; i < len(sd); i++ {
- // Skip ahead until the next non-zero value.
- sdi := sd[i].Load()
- if sdi == 0 {
- continue
- }
-
- // We found a function that was executed.
- nCtrs := sd[i+coverage.NumCtrsOffset].Load()
- pkgId := sd[i+coverage.PkgIdOffset].Load()
- funcId := sd[i+coverage.FuncIdOffset].Load()
- cst := i + coverage.FirstCtrOffset
- counters := sd[cst : cst+int(nCtrs)]
-
- // Check to make sure that we have at least one live
- // counter. See the implementation note in ClearCoverageCounters
- // for a description of why this is needed.
- isLive := false
- for i := 0; i < len(counters); i++ {
- if counters[i].Load() != 0 {
- isLive = true
- break
- }
- }
- if !isLive {
- // Skip this function.
- i += coverage.FirstCtrOffset + int(nCtrs) - 1
- continue
- }
-
- if s.debug {
- if pkgId != dpkg {
- dpkg = pkgId
- fmt.Fprintf(os.Stderr, "\n=+= %d: pk=%d visit live fcn",
- i, pkgId)
- }
- fmt.Fprintf(os.Stderr, " {i=%d F%d NC%d}", i, funcId, nCtrs)
- }
-
- // Vet and/or fix up package ID. A package ID of zero
- // indicates that there is some new package X that is a
- // runtime dependency, and this package has code that
- // executes before its corresponding init package runs.
- // This is a fatal error that we should only see during
- // Go development (e.g. tip).
- ipk := int32(pkgId)
- if ipk == 0 {
- fmt.Fprintf(os.Stderr, "\n")
- reportErrorInHardcodedList(int32(i), ipk, funcId, nCtrs)
- } else if ipk < 0 {
- if newId, ok := s.pkgmap[int(ipk)]; ok {
- pkgId = uint32(newId)
- } else {
- fmt.Fprintf(os.Stderr, "\n")
- reportErrorInHardcodedList(int32(i), ipk, funcId, nCtrs)
- }
- } else {
- // The package ID value stored in the counter array
- // has 1 added to it (so as to preclude the
- // possibility of a zero value ; see
- // runtime.addCovMeta), so subtract off 1 here to form
- // the real package ID.
- pkgId--
- }
-
- tcounters = rdCounters(counters, tcounters)
- if err := f(pkgId, funcId, tcounters); err != nil {
- return err
- }
-
- // Skip over this function.
- i += coverage.FirstCtrOffset + int(nCtrs) - 1
- }
- if s.debug {
- fmt.Fprintf(os.Stderr, "\n")
- }
- }
- return nil
-}
-
-// captureOsArgs converts os.Args() into the format we use to store
-// this info in the counter data file (counter data file "args"
-// section is a generic key-value collection). See the 'args' section
-// in internal/coverage/defs.go for more info. The args map
-// is also used to capture GOOS + GOARCH values as well.
-func captureOsArgs() map[string]string {
- m := make(map[string]string)
- m["argc"] = strconv.Itoa(len(os.Args))
- for k, a := range os.Args {
- m[fmt.Sprintf("argv%d", k)] = a
- }
- m["GOOS"] = runtime.GOOS
- m["GOARCH"] = runtime.GOARCH
- return m
-}
-
-// emitCounterDataFile emits the counter data portion of a
-// coverage output file (to the file 's.cf').
-func (s *emitState) emitCounterDataFile(finalHash [16]byte, w io.Writer) error {
- cfw := encodecounter.NewCoverageDataWriter(w, coverage.CtrULeb128)
- if err := cfw.Write(finalHash, capturedOsArgs, s); err != nil {
- return err
- }
- return nil
-}
-
-// markProfileEmitted is injected to testmain via linkname.
-//go:linkname markProfileEmitted
-
-// markProfileEmitted signals the runtime/coverage machinery that
-// coverage data output files have already been written out, and there
-// is no need to take any additional action at exit time. This
-// function is called (via linknamed reference) from the
-// coverage-related boilerplate code in _testmain.go emitted for go
-// unit tests.
-func markProfileEmitted(val bool) {
- covProfileAlreadyEmitted = val
-}
-
-func reportErrorInHardcodedList(slot, pkgID int32, fnID, nCtrs uint32) {
- metaList := getCovMetaList()
- pkgMap := getCovPkgMap()
-
- println("internal error in coverage meta-data tracking:")
- println("encountered bad pkgID:", pkgID, " at slot:", slot,
- " fnID:", fnID, " numCtrs:", nCtrs)
- println("list of hard-coded runtime package IDs needs revising.")
- println("[see the comment on the 'rtPkgs' var in ")
- println(" <goroot>/src/internal/coverage/pkid.go]")
- println("registered list:")
- for k, b := range metaList {
- print("slot: ", k, " path='", b.PkgPath, "' ")
- if b.PkgID != -1 {
- print(" hard-coded id: ", b.PkgID)
- }
- println("")
- }
- println("remap table:")
- for from, to := range pkgMap {
- println("from ", from, " to ", to)
- }
-}
diff --git a/src/runtime/coverage/emitdata_test.go b/src/runtime/coverage/emitdata_test.go
deleted file mode 100644
index 3558dd2d88..0000000000
--- a/src/runtime/coverage/emitdata_test.go
+++ /dev/null
@@ -1,550 +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 (
- "fmt"
- "internal/coverage"
- "internal/goexperiment"
- "internal/platform"
- "internal/testenv"
- "os"
- "os/exec"
- "path/filepath"
- "runtime"
- "strings"
- "testing"
-)
-
-// Set to true for debugging (linux only).
-const fixedTestDir = false
-
-func TestCoverageApis(t *testing.T) {
- if testing.Short() {
- t.Skipf("skipping test: too long for short mode")
- }
- if !goexperiment.CoverageRedesign {
- t.Skipf("skipping new coverage tests (experiment not enabled)")
- }
- testenv.MustHaveGoBuild(t)
- dir := t.TempDir()
- if fixedTestDir {
- dir = "/tmp/qqqzzz"
- os.RemoveAll(dir)
- mkdir(t, dir)
- }
-
- // Build harness. We need two copies of the harness, one built
- // with -covermode=atomic and one built non-atomic.
- bdir1 := mkdir(t, filepath.Join(dir, "build1"))
- hargs1 := []string{"-covermode=atomic", "-coverpkg=all"}
- atomicHarnessPath := buildHarness(t, bdir1, hargs1)
- nonAtomicMode := testing.CoverMode()
- if testing.CoverMode() == "atomic" {
- nonAtomicMode = "set"
- }
- bdir2 := mkdir(t, filepath.Join(dir, "build2"))
- hargs2 := []string{"-coverpkg=all", "-covermode=" + nonAtomicMode}
- nonAtomicHarnessPath := buildHarness(t, bdir2, hargs2)
-
- t.Logf("atomic harness path is %s", atomicHarnessPath)
- t.Logf("non-atomic harness path is %s", nonAtomicHarnessPath)
-
- // Sub-tests for each API we want to inspect, plus
- // extras for error testing.
- t.Run("emitToDir", func(t *testing.T) {
- t.Parallel()
- testEmitToDir(t, atomicHarnessPath, dir)
- })
- t.Run("emitToWriter", func(t *testing.T) {
- t.Parallel()
- testEmitToWriter(t, atomicHarnessPath, dir)
- })
- t.Run("emitToNonexistentDir", func(t *testing.T) {
- t.Parallel()
- testEmitToNonexistentDir(t, atomicHarnessPath, dir)
- })
- t.Run("emitToNilWriter", func(t *testing.T) {
- t.Parallel()
- testEmitToNilWriter(t, atomicHarnessPath, dir)
- })
- t.Run("emitToFailingWriter", func(t *testing.T) {
- t.Parallel()
- testEmitToFailingWriter(t, atomicHarnessPath, dir)
- })
- t.Run("emitWithCounterClear", func(t *testing.T) {
- t.Parallel()
- testEmitWithCounterClear(t, atomicHarnessPath, dir)
- })
- t.Run("emitToDirNonAtomic", func(t *testing.T) {
- t.Parallel()
- testEmitToDirNonAtomic(t, nonAtomicHarnessPath, nonAtomicMode, dir)
- })
- t.Run("emitToWriterNonAtomic", func(t *testing.T) {
- t.Parallel()
- testEmitToWriterNonAtomic(t, nonAtomicHarnessPath, nonAtomicMode, dir)
- })
- t.Run("emitWithCounterClearNonAtomic", func(t *testing.T) {
- t.Parallel()
- testEmitWithCounterClearNonAtomic(t, nonAtomicHarnessPath, nonAtomicMode, dir)
- })
-}
-
-// upmergeCoverData helps improve coverage data for this package
-// itself. If this test itself is being invoked with "-cover", then
-// what we'd like is for package coverage data (that is, coverage for
-// routines in "runtime/coverage") to be incorporated into the test
-// run from the "harness.exe" runs we've just done. We can accomplish
-// this by doing a merge from the harness gocoverdir's to the test
-// gocoverdir.
-func upmergeCoverData(t *testing.T, gocoverdir string, mode string) {
- if testing.CoverMode() != mode {
- return
- }
- testGoCoverDir := os.Getenv("GOCOVERDIR")
- if testGoCoverDir == "" {
- return
- }
- args := []string{"tool", "covdata", "merge", "-pkg=runtime/coverage",
- "-o", testGoCoverDir, "-i", gocoverdir}
- t.Logf("up-merge of covdata from %s to %s", gocoverdir, testGoCoverDir)
- t.Logf("executing: go %+v", args)
- cmd := exec.Command(testenv.GoToolPath(t), args...)
- if b, err := cmd.CombinedOutput(); err != nil {
- t.Fatalf("covdata merge failed (%v): %s", err, b)
- }
-}
-
-// buildHarness builds the helper program "harness.exe".
-func buildHarness(t *testing.T, dir string, opts []string) string {
- harnessPath := filepath.Join(dir, "harness.exe")
- harnessSrc := filepath.Join("testdata", "harness.go")
- args := []string{"build", "-o", harnessPath}
- args = append(args, opts...)
- args = append(args, harnessSrc)
- //t.Logf("harness build: go %+v\n", args)
- cmd := exec.Command(testenv.GoToolPath(t), args...)
- if b, err := cmd.CombinedOutput(); err != nil {
- t.Fatalf("build failed (%v): %s", err, b)
- }
- return harnessPath
-}
-
-func mkdir(t *testing.T, d string) string {
- t.Helper()
- if err := os.Mkdir(d, 0777); err != nil {
- t.Fatalf("mkdir failed: %v", err)
- }
- return d
-}
-
-// updateGoCoverDir updates the specified environment 'env' to set
-// GOCOVERDIR to 'gcd' (if setGoCoverDir is TRUE) or removes
-// GOCOVERDIR from the environment (if setGoCoverDir is false).
-func updateGoCoverDir(env []string, gcd string, setGoCoverDir bool) []string {
- rv := []string{}
- found := false
- for _, v := range env {
- if strings.HasPrefix(v, "GOCOVERDIR=") {
- if !setGoCoverDir {
- continue
- }
- v = "GOCOVERDIR=" + gcd
- found = true
- }
- rv = append(rv, v)
- }
- if !found && setGoCoverDir {
- rv = append(rv, "GOCOVERDIR="+gcd)
- }
- return rv
-}
-
-func runHarness(t *testing.T, harnessPath string, tp string, setGoCoverDir bool, rdir, edir string) (string, error) {
- t.Logf("running: %s -tp %s -o %s with rdir=%s and GOCOVERDIR=%v", harnessPath, tp, edir, rdir, setGoCoverDir)
- cmd := exec.Command(harnessPath, "-tp", tp, "-o", edir)
- cmd.Dir = rdir
- cmd.Env = updateGoCoverDir(os.Environ(), rdir, setGoCoverDir)
- b, err := cmd.CombinedOutput()
- //t.Logf("harness run output: %s\n", string(b))
- return string(b), err
-}
-
-func testForSpecificFunctions(t *testing.T, dir string, want []string, avoid []string) string {
- args := []string{"tool", "covdata", "debugdump",
- "-live", "-pkg=command-line-arguments", "-i=" + dir}
- t.Logf("running: go %v\n", args)
- cmd := exec.Command(testenv.GoToolPath(t), args...)
- b, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("'go tool covdata failed (%v): %s", err, b)
- }
- output := string(b)
- rval := ""
- for _, f := range want {
- wf := "Func: " + f + "\n"
- if strings.Contains(output, wf) {
- continue
- }
- rval += fmt.Sprintf("error: output should contain %q but does not\n", wf)
- }
- for _, f := range avoid {
- wf := "Func: " + f + "\n"
- if strings.Contains(output, wf) {
- rval += fmt.Sprintf("error: output should not contain %q but does\n", wf)
- }
- }
- if rval != "" {
- t.Logf("=-= begin output:\n" + output + "\n=-= end output\n")
- }
- return rval
-}
-
-func withAndWithoutRunner(f func(setit bool, tag string)) {
- // Run 'f' with and without GOCOVERDIR set.
- for i := 0; i < 2; i++ {
- tag := "x"
- setGoCoverDir := true
- if i == 0 {
- setGoCoverDir = false
- tag = "y"
- }
- f(setGoCoverDir, tag)
- }
-}
-
-func mktestdirs(t *testing.T, tag, tp, dir string) (string, string) {
- t.Helper()
- rdir := mkdir(t, filepath.Join(dir, tp+"-rdir-"+tag))
- edir := mkdir(t, filepath.Join(dir, tp+"-edir-"+tag))
- return rdir, edir
-}
-
-func testEmitToDir(t *testing.T, harnessPath string, dir string) {
- withAndWithoutRunner(func(setGoCoverDir bool, tag string) {
- tp := "emitToDir"
- rdir, edir := mktestdirs(t, tag, tp, dir)
- output, err := runHarness(t, harnessPath, tp,
- setGoCoverDir, rdir, edir)
- if err != nil {
- t.Logf("%s", output)
- t.Fatalf("running 'harness -tp emitDir': %v", err)
- }
-
- // Just check to make sure meta-data file and counter data file were
- // written. Another alternative would be to run "go tool covdata"
- // or equivalent, but for now, this is what we've got.
- dents, err := os.ReadDir(edir)
- if err != nil {
- t.Fatalf("os.ReadDir(%s) failed: %v", edir, err)
- }
- mfc := 0
- cdc := 0
- for _, e := range dents {
- if e.IsDir() {
- continue
- }
- if strings.HasPrefix(e.Name(), coverage.MetaFilePref) {
- mfc++
- } else if strings.HasPrefix(e.Name(), coverage.CounterFilePref) {
- cdc++
- }
- }
- wantmf := 1
- wantcf := 1
- if mfc != wantmf {
- t.Errorf("EmitToDir: want %d meta-data files, got %d\n", wantmf, mfc)
- }
- if cdc != wantcf {
- t.Errorf("EmitToDir: want %d counter-data files, got %d\n", wantcf, cdc)
- }
- upmergeCoverData(t, edir, "atomic")
- upmergeCoverData(t, rdir, "atomic")
- })
-}
-
-func testEmitToWriter(t *testing.T, harnessPath string, dir string) {
- withAndWithoutRunner(func(setGoCoverDir bool, tag string) {
- tp := "emitToWriter"
- rdir, edir := mktestdirs(t, tag, tp, dir)
- output, err := runHarness(t, harnessPath, tp, setGoCoverDir, rdir, edir)
- if err != nil {
- t.Logf("%s", output)
- t.Fatalf("running 'harness -tp %s': %v", tp, err)
- }
- want := []string{"main", tp}
- avoid := []string{"final"}
- if msg := testForSpecificFunctions(t, edir, want, avoid); msg != "" {
- t.Errorf("coverage data from %q output match failed: %s", tp, msg)
- }
- upmergeCoverData(t, edir, "atomic")
- upmergeCoverData(t, rdir, "atomic")
- })
-}
-
-func testEmitToNonexistentDir(t *testing.T, harnessPath string, dir string) {
- withAndWithoutRunner(func(setGoCoverDir bool, tag string) {
- tp := "emitToNonexistentDir"
- rdir, edir := mktestdirs(t, tag, tp, dir)
- output, err := runHarness(t, harnessPath, tp, setGoCoverDir, rdir, edir)
- if err != nil {
- t.Logf("%s", output)
- t.Fatalf("running 'harness -tp %s': %v", tp, err)
- }
- upmergeCoverData(t, edir, "atomic")
- upmergeCoverData(t, rdir, "atomic")
- })
-}
-
-func testEmitToUnwritableDir(t *testing.T, harnessPath string, dir string) {
- withAndWithoutRunner(func(setGoCoverDir bool, tag string) {
-
- tp := "emitToUnwritableDir"
- rdir, edir := mktestdirs(t, tag, tp, dir)
-
- // Make edir unwritable.
- if err := os.Chmod(edir, 0555); err != nil {
- t.Fatalf("chmod failed: %v", err)
- }
- defer os.Chmod(edir, 0777)
-
- output, err := runHarness(t, harnessPath, tp, setGoCoverDir, rdir, edir)
- if err != nil {
- t.Logf("%s", output)
- t.Fatalf("running 'harness -tp %s': %v", tp, err)
- }
- upmergeCoverData(t, edir, "atomic")
- upmergeCoverData(t, rdir, "atomic")
- })
-}
-
-func testEmitToNilWriter(t *testing.T, harnessPath string, dir string) {
- withAndWithoutRunner(func(setGoCoverDir bool, tag string) {
- tp := "emitToNilWriter"
- rdir, edir := mktestdirs(t, tag, tp, dir)
- output, err := runHarness(t, harnessPath, tp, setGoCoverDir, rdir, edir)
- if err != nil {
- t.Logf("%s", output)
- t.Fatalf("running 'harness -tp %s': %v", tp, err)
- }
- upmergeCoverData(t, edir, "atomic")
- upmergeCoverData(t, rdir, "atomic")
- })
-}
-
-func testEmitToFailingWriter(t *testing.T, harnessPath string, dir string) {
- withAndWithoutRunner(func(setGoCoverDir bool, tag string) {
- tp := "emitToFailingWriter"
- rdir, edir := mktestdirs(t, tag, tp, dir)
- output, err := runHarness(t, harnessPath, tp, setGoCoverDir, rdir, edir)
- if err != nil {
- t.Logf("%s", output)
- t.Fatalf("running 'harness -tp %s': %v", tp, err)
- }
- upmergeCoverData(t, edir, "atomic")
- upmergeCoverData(t, rdir, "atomic")
- })
-}
-
-func testEmitWithCounterClear(t *testing.T, harnessPath string, dir string) {
- withAndWithoutRunner(func(setGoCoverDir bool, tag string) {
- tp := "emitWithCounterClear"
- rdir, edir := mktestdirs(t, tag, tp, dir)
- output, err := runHarness(t, harnessPath, tp,
- setGoCoverDir, rdir, edir)
- if err != nil {
- t.Logf("%s", output)
- t.Fatalf("running 'harness -tp %s': %v", tp, err)
- }
- want := []string{tp, "postClear"}
- avoid := []string{"preClear", "main", "final"}
- if msg := testForSpecificFunctions(t, edir, want, avoid); msg != "" {
- t.Logf("%s", output)
- t.Errorf("coverage data from %q output match failed: %s", tp, msg)
- }
- upmergeCoverData(t, edir, "atomic")
- upmergeCoverData(t, rdir, "atomic")
- })
-}
-
-func testEmitToDirNonAtomic(t *testing.T, harnessPath string, naMode string, dir string) {
- tp := "emitToDir"
- tag := "nonatomdir"
- rdir, edir := mktestdirs(t, tag, tp, dir)
- output, err := runHarness(t, harnessPath, tp,
- true, rdir, edir)
-
- // We expect an error here.
- if err == nil {
- t.Logf("%s", output)
- t.Fatalf("running 'harness -tp %s': did not get expected error", tp)
- }
-
- got := strings.TrimSpace(string(output))
- want := "WriteCountersDir invoked for program built"
- if !strings.Contains(got, want) {
- t.Errorf("running 'harness -tp %s': got:\n%s\nwant: %s",
- tp, got, want)
- }
- upmergeCoverData(t, edir, naMode)
- upmergeCoverData(t, rdir, naMode)
-}
-
-func testEmitToWriterNonAtomic(t *testing.T, harnessPath string, naMode string, dir string) {
- tp := "emitToWriter"
- tag := "nonatomw"
- rdir, edir := mktestdirs(t, tag, tp, dir)
- output, err := runHarness(t, harnessPath, tp,
- true, rdir, edir)
-
- // We expect an error here.
- if err == nil {
- t.Logf("%s", output)
- t.Fatalf("running 'harness -tp %s': did not get expected error", tp)
- }
-
- got := strings.TrimSpace(string(output))
- want := "WriteCounters invoked for program built"
- if !strings.Contains(got, want) {
- t.Errorf("running 'harness -tp %s': got:\n%s\nwant: %s",
- tp, got, want)
- }
-
- upmergeCoverData(t, edir, naMode)
- upmergeCoverData(t, rdir, naMode)
-}
-
-func testEmitWithCounterClearNonAtomic(t *testing.T, harnessPath string, naMode string, dir string) {
- tp := "emitWithCounterClear"
- tag := "cclear"
- rdir, edir := mktestdirs(t, tag, tp, dir)
- output, err := runHarness(t, harnessPath, tp,
- true, rdir, edir)
-
- // We expect an error here.
- if err == nil {
- t.Logf("%s", output)
- t.Fatalf("running 'harness -tp %s' nonatomic: did not get expected error", tp)
- }
-
- got := strings.TrimSpace(string(output))
- want := "ClearCounters invoked for program built"
- if !strings.Contains(got, want) {
- t.Errorf("running 'harness -tp %s': got:\n%s\nwant: %s",
- tp, got, want)
- }
-
- upmergeCoverData(t, edir, naMode)
- upmergeCoverData(t, rdir, naMode)
-}
-
-func TestApisOnNocoverBinary(t *testing.T) {
- if testing.Short() {
- t.Skipf("skipping test: too long for short mode")
- }
- testenv.MustHaveGoBuild(t)
- dir := t.TempDir()
-
- // Build harness with no -cover.
- bdir := mkdir(t, filepath.Join(dir, "nocover"))
- edir := mkdir(t, filepath.Join(dir, "emitDirNo"))
- harnessPath := buildHarness(t, bdir, nil)
- output, err := runHarness(t, harnessPath, "emitToDir", false, edir, edir)
- if err == nil {
- t.Fatalf("expected error on TestApisOnNocoverBinary harness run")
- }
- const want = "not built with -cover"
- if !strings.Contains(output, want) {
- t.Errorf("error output does not contain %q: %s", want, output)
- }
-}
-
-func TestIssue56006EmitDataRaceCoverRunningGoroutine(t *testing.T) {
- if testing.Short() {
- t.Skipf("skipping test: too long for short mode")
- }
- if !goexperiment.CoverageRedesign {
- t.Skipf("skipping new coverage tests (experiment not enabled)")
- }
-
- // This test requires "go test -race -cover", meaning that we need
- // go build, go run, and "-race" support.
- testenv.MustHaveGoRun(t)
- if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) ||
- !testenv.HasCGO() {
- t.Skip("skipped due to lack of race detector support / CGO")
- }
-
- // This will run a program with -cover and -race where we have a
- // goroutine still running (and updating counters) at the point where
- // the test runtime is trying to write out counter data.
- cmd := exec.Command(testenv.GoToolPath(t), "test", "-cover", "-race")
- cmd.Dir = filepath.Join("testdata", "issue56006")
- b, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("go test -cover -race failed: %v", err)
- }
-
- // Don't want to see any data races in output.
- avoid := []string{"DATA RACE"}
- for _, no := range avoid {
- if strings.Contains(string(b), no) {
- t.Logf("%s\n", string(b))
- t.Fatalf("found %s in test output, not permitted", no)
- }
- }
-}
-
-func TestIssue59563TruncatedCoverPkgAll(t *testing.T) {
- if testing.Short() {
- t.Skipf("skipping test: too long for short mode")
- }
- testenv.MustHaveGoRun(t)
-
- tmpdir := t.TempDir()
- ppath := filepath.Join(tmpdir, "foo.cov")
-
- cmd := exec.Command(testenv.GoToolPath(t), "test", "-coverpkg=all", "-coverprofile="+ppath)
- cmd.Dir = filepath.Join("testdata", "issue59563")
- b, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("go test -cover failed: %v", err)
- }
-
- cmd = exec.Command(testenv.GoToolPath(t), "tool", "cover", "-func="+ppath)
- b, err = cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("go tool cover -func failed: %v", err)
- }
-
- lines := strings.Split(string(b), "\n")
- nfound := 0
- bad := false
- for _, line := range lines {
- f := strings.Fields(line)
- if len(f) == 0 {
- continue
- }
- // We're only interested in the specific function "large" for
- // the testcase being built. See the #59563 for details on why
- // size matters.
- if !(strings.HasPrefix(f[0], "runtime/coverage/testdata/issue59563/repro.go") && strings.Contains(line, "large")) {
- continue
- }
- nfound++
- want := "100.0%"
- if f[len(f)-1] != want {
- t.Errorf("wanted %s got: %q\n", want, line)
- bad = true
- }
- }
- if nfound != 1 {
- t.Errorf("wanted 1 found, got %d\n", nfound)
- bad = true
- }
- if bad {
- t.Logf("func output:\n%s\n", string(b))
- }
-}
diff --git a/src/runtime/coverage/hooks.go b/src/runtime/coverage/hooks.go
deleted file mode 100644
index a9fbf9d7dd..0000000000
--- a/src/runtime/coverage/hooks.go
+++ /dev/null
@@ -1,42 +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 _ "unsafe"
-
-// initHook is invoked from the main package "init" routine in
-// programs built with "-cover". This function is intended to be
-// called only by the compiler.
-//
-// If 'istest' is false, it indicates we're building a regular program
-// ("go build -cover ..."), in which case we immediately try to write
-// out the meta-data file, and register emitCounterData as an exit
-// hook.
-//
-// If 'istest' is true (indicating that the program in question is a
-// Go test binary), then we tentatively queue up both emitMetaData and
-// emitCounterData as exit hooks. In the normal case (e.g. regular "go
-// test -cover" run) the testmain.go boilerplate will run at the end
-// of the test, write out the coverage percentage, and then invoke
-// markProfileEmitted() to indicate that no more work needs to be
-// done. If however that call is never made, this is a sign that the
-// test binary is being used as a replacement binary for the tool
-// being tested, hence we do want to run exit hooks when the program
-// terminates.
-func initHook(istest bool) {
- // Note: hooks are run in reverse registration order, so
- // register the counter data hook before the meta-data hook
- // (in the case where two hooks are needed).
- runOnNonZeroExit := true
- runtime_addExitHook(emitCounterData, runOnNonZeroExit)
- if istest {
- runtime_addExitHook(emitMetaData, runOnNonZeroExit)
- } else {
- emitMetaData()
- }
-}
-
-//go:linkname runtime_addExitHook runtime.addExitHook
-func runtime_addExitHook(f func(), runOnNonZeroExit bool)
diff --git a/src/runtime/coverage/testdata/harness.go b/src/runtime/coverage/testdata/harness.go
deleted file mode 100644
index 03969da426..0000000000
--- a/src/runtime/coverage/testdata/harness.go
+++ /dev/null
@@ -1,259 +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 main
-
-import (
- "flag"
- "fmt"
- "internal/coverage/slicewriter"
- "io"
- "log"
- "os"
- "path/filepath"
- "runtime/coverage"
- "strings"
-)
-
-var verbflag = flag.Int("v", 0, "Verbose trace output level")
-var testpointflag = flag.String("tp", "", "Testpoint to run")
-var outdirflag = flag.String("o", "", "Output dir into which to emit")
-
-func emitToWriter() {
- log.SetPrefix("emitToWriter: ")
- var slwm slicewriter.WriteSeeker
- if err := coverage.WriteMeta(&slwm); err != nil {
- log.Fatalf("error: WriteMeta returns %v", err)
- }
- mf := filepath.Join(*outdirflag, "covmeta.0abcdef")
- if err := os.WriteFile(mf, slwm.BytesWritten(), 0666); err != nil {
- log.Fatalf("error: writing %s: %v", mf, err)
- }
- var slwc slicewriter.WriteSeeker
- if err := coverage.WriteCounters(&slwc); err != nil {
- log.Fatalf("error: WriteCounters returns %v", err)
- }
- cf := filepath.Join(*outdirflag, "covcounters.0abcdef.99.77")
- if err := os.WriteFile(cf, slwc.BytesWritten(), 0666); err != nil {
- log.Fatalf("error: writing %s: %v", cf, err)
- }
-}
-
-func emitToDir() {
- log.SetPrefix("emitToDir: ")
- if err := coverage.WriteMetaDir(*outdirflag); err != nil {
- log.Fatalf("error: WriteMetaDir returns %v", err)
- }
- if err := coverage.WriteCountersDir(*outdirflag); err != nil {
- log.Fatalf("error: WriteCountersDir returns %v", err)
- }
-}
-
-func emitToNonexistentDir() {
- log.SetPrefix("emitToNonexistentDir: ")
-
- want := []string{
- "no such file or directory", // linux-ish
- "system cannot find the file specified", // windows
- "does not exist", // plan9
- }
-
- checkWant := func(which string, got string) {
- found := false
- for _, w := range want {
- if strings.Contains(got, w) {
- found = true
- break
- }
- }
- if !found {
- log.Fatalf("%s emit to bad dir: got error:\n %v\nwanted error with one of:\n %+v", which, got, want)
- }
- }
-
- // Mangle the output directory to produce something nonexistent.
- mangled := *outdirflag + "_MANGLED"
- if err := coverage.WriteMetaDir(mangled); err == nil {
- log.Fatal("expected error from WriteMetaDir to nonexistent dir")
- } else {
- got := fmt.Sprintf("%v", err)
- checkWant("meta data", got)
- }
-
- // Now try to emit counter data file to a bad dir.
- if err := coverage.WriteCountersDir(mangled); err == nil {
- log.Fatal("expected error emitting counter data to bad dir")
- } else {
- got := fmt.Sprintf("%v", err)
- checkWant("counter data", got)
- }
-}
-
-func emitToUnwritableDir() {
- log.SetPrefix("emitToUnwritableDir: ")
-
- want := "permission denied"
-
- if err := coverage.WriteMetaDir(*outdirflag); err == nil {
- log.Fatal("expected error from WriteMetaDir to unwritable dir")
- } else {
- got := fmt.Sprintf("%v", err)
- if !strings.Contains(got, want) {
- log.Fatalf("meta-data emit to unwritable dir: wanted error containing %q got %q", want, got)
- }
- }
-
- // Similarly with writing counter data.
- if err := coverage.WriteCountersDir(*outdirflag); err == nil {
- log.Fatal("expected error emitting counter data to unwritable dir")
- } else {
- got := fmt.Sprintf("%v", err)
- if !strings.Contains(got, want) {
- log.Fatalf("emitting counter data to unwritable dir: wanted error containing %q got %q", want, got)
- }
- }
-}
-
-func emitToNilWriter() {
- log.SetPrefix("emitToWriter: ")
- want := "nil writer"
- var bad io.WriteSeeker
- if err := coverage.WriteMeta(bad); err == nil {
- log.Fatal("expected error passing nil writer for meta emit")
- } else {
- got := fmt.Sprintf("%v", err)
- if !strings.Contains(got, want) {
- log.Fatalf("emitting meta-data passing nil writer: wanted error containing %q got %q", want, got)
- }
- }
-
- if err := coverage.WriteCounters(bad); err == nil {
- log.Fatal("expected error passing nil writer for counter emit")
- } else {
- got := fmt.Sprintf("%v", err)
- if !strings.Contains(got, want) {
- log.Fatalf("emitting counter data passing nil writer: wanted error containing %q got %q", want, got)
- }
- }
-}
-
-type failingWriter struct {
- writeCount int
- writeLimit int
- slws slicewriter.WriteSeeker
-}
-
-func (f *failingWriter) Write(p []byte) (n int, err error) {
- c := f.writeCount
- f.writeCount++
- if f.writeLimit < 0 || c < f.writeLimit {
- return f.slws.Write(p)
- }
- return 0, fmt.Errorf("manufactured write error")
-}
-
-func (f *failingWriter) Seek(offset int64, whence int) (int64, error) {
- return f.slws.Seek(offset, whence)
-}
-
-func (f *failingWriter) reset(lim int) {
- f.writeCount = 0
- f.writeLimit = lim
- f.slws = slicewriter.WriteSeeker{}
-}
-
-func writeStressTest(tag string, testf func(testf *failingWriter) error) {
- // Invoke the function initially without the write limit
- // set, to capture the number of writes performed.
- fw := &failingWriter{writeLimit: -1}
- testf(fw)
-
- // Now that we know how many writes are going to happen, run the
- // function repeatedly, each time with a Write operation set to
- // fail at a new spot. The goal here is to make sure that:
- // A) an error is reported, and B) nothing crashes.
- tot := fw.writeCount
- for i := 0; i < tot; i++ {
- fw.reset(i)
- err := testf(fw)
- if err == nil {
- log.Fatalf("no error from write %d tag %s", i, tag)
- }
- }
-}
-
-func postClear() int {
- return 42
-}
-
-func preClear() int {
- return 42
-}
-
-// This test is designed to ensure that write errors are properly
-// handled by the code that writes out coverage data. It repeatedly
-// invokes the 'emit to writer' apis using a specially crafted writer
-// that captures the total number of expected writes, then replays the
-// execution N times with a manufactured write error at the
-// appropriate spot.
-func emitToFailingWriter() {
- log.SetPrefix("emitToFailingWriter: ")
-
- writeStressTest("emit-meta", func(f *failingWriter) error {
- return coverage.WriteMeta(f)
- })
- writeStressTest("emit-counter", func(f *failingWriter) error {
- return coverage.WriteCounters(f)
- })
-}
-
-func emitWithCounterClear() {
- log.SetPrefix("emitWitCounterClear: ")
- preClear()
- if err := coverage.ClearCounters(); err != nil {
- log.Fatalf("clear failed: %v", err)
- }
- postClear()
- if err := coverage.WriteMetaDir(*outdirflag); err != nil {
- log.Fatalf("error: WriteMetaDir returns %v", err)
- }
- if err := coverage.WriteCountersDir(*outdirflag); err != nil {
- log.Fatalf("error: WriteCountersDir returns %v", err)
- }
-}
-
-func final() int {
- println("I run last.")
- return 43
-}
-
-func main() {
- log.SetFlags(0)
- flag.Parse()
- if *testpointflag == "" {
- log.Fatalf("error: no testpoint (use -tp flag)")
- }
- if *outdirflag == "" {
- log.Fatalf("error: no output dir specified (use -o flag)")
- }
- switch *testpointflag {
- case "emitToDir":
- emitToDir()
- case "emitToWriter":
- emitToWriter()
- case "emitToNonexistentDir":
- emitToNonexistentDir()
- case "emitToUnwritableDir":
- emitToUnwritableDir()
- case "emitToNilWriter":
- emitToNilWriter()
- case "emitToFailingWriter":
- emitToFailingWriter()
- case "emitWithCounterClear":
- emitWithCounterClear()
- default:
- log.Fatalf("error: unknown testpoint %q", *testpointflag)
- }
- final()
-}
diff --git a/src/runtime/coverage/testdata/issue56006/repro.go b/src/runtime/coverage/testdata/issue56006/repro.go
deleted file mode 100644
index 60a4925143..0000000000
--- a/src/runtime/coverage/testdata/issue56006/repro.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package main
-
-//go:noinline
-func blah(x int) int {
- if x != 0 {
- return x + 42
- }
- return x - 42
-}
-
-func main() {
- go infloop()
- println(blah(1) + blah(0))
-}
-
-var G int
-
-func infloop() {
- for {
- G += blah(1)
- G += blah(0)
- if G > 10000 {
- G = 0
- }
- }
-}
diff --git a/src/runtime/coverage/testdata/issue56006/repro_test.go b/src/runtime/coverage/testdata/issue56006/repro_test.go
deleted file mode 100644
index 674d819c3b..0000000000
--- a/src/runtime/coverage/testdata/issue56006/repro_test.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package main
-
-import "testing"
-
-func TestSomething(t *testing.T) {
- go infloop()
- println(blah(1) + blah(0))
-}
diff --git a/src/runtime/coverage/testdata/issue59563/repro.go b/src/runtime/coverage/testdata/issue59563/repro.go
deleted file mode 100644
index d054567dc5..0000000000
--- a/src/runtime/coverage/testdata/issue59563/repro.go
+++ /dev/null
@@ -1,823 +0,0 @@
-// Copyright 2023 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 repro
-
-import (
- "fmt"
- "net/http"
-)
-
-func small() {
- go func() {
- fmt.Println(http.ListenAndServe("localhost:7070", nil))
- }()
-}
-
-func large(x int) int {
- if x == 0 {
- x += 0
- } else if x == 1 {
- x += 1
- } else if x == 2 {
- x += 2
- } else if x == 3 {
- x += 3
- } else if x == 4 {
- x += 4
- } else if x == 5 {
- x += 5
- } else if x == 6 {
- x += 6
- } else if x == 7 {
- x += 7
- } else if x == 8 {
- x += 8
- } else if x == 9 {
- x += 9
- } else if x == 10 {
- x += 10
- } else if x == 11 {
- x += 11
- } else if x == 12 {
- x += 12
- } else if x == 13 {
- x += 13
- } else if x == 14 {
- x += 14
- } else if x == 15 {
- x += 15
- } else if x == 16 {
- x += 16
- } else if x == 17 {
- x += 17
- } else if x == 18 {
- x += 18
- } else if x == 19 {
- x += 19
- } else if x == 20 {
- x += 20
- } else if x == 21 {
- x += 21
- } else if x == 22 {
- x += 22
- } else if x == 23 {
- x += 23
- } else if x == 24 {
- x += 24
- } else if x == 25 {
- x += 25
- } else if x == 26 {
- x += 26
- } else if x == 27 {
- x += 27
- } else if x == 28 {
- x += 28
- } else if x == 29 {
- x += 29
- } else if x == 30 {
- x += 30
- } else if x == 31 {
- x += 31
- } else if x == 32 {
- x += 32
- } else if x == 33 {
- x += 33
- } else if x == 34 {
- x += 34
- } else if x == 35 {
- x += 35
- } else if x == 36 {
- x += 36
- } else if x == 37 {
- x += 37
- } else if x == 38 {
- x += 38
- } else if x == 39 {
- x += 39
- } else if x == 40 {
- x += 40
- } else if x == 41 {
- x += 41
- } else if x == 42 {
- x += 42
- } else if x == 43 {
- x += 43
- } else if x == 44 {
- x += 44
- } else if x == 45 {
- x += 45
- } else if x == 46 {
- x += 46
- } else if x == 47 {
- x += 47
- } else if x == 48 {
- x += 48
- } else if x == 49 {
- x += 49
- } else if x == 50 {
- x += 50
- } else if x == 51 {
- x += 51
- } else if x == 52 {
- x += 52
- } else if x == 53 {
- x += 53
- } else if x == 54 {
- x += 54
- } else if x == 55 {
- x += 55
- } else if x == 56 {
- x += 56
- } else if x == 57 {
- x += 57
- } else if x == 58 {
- x += 58
- } else if x == 59 {
- x += 59
- } else if x == 60 {
- x += 60
- } else if x == 61 {
- x += 61
- } else if x == 62 {
- x += 62
- } else if x == 63 {
- x += 63
- } else if x == 64 {
- x += 64
- } else if x == 65 {
- x += 65
- } else if x == 66 {
- x += 66
- } else if x == 67 {
- x += 67
- } else if x == 68 {
- x += 68
- } else if x == 69 {
- x += 69
- } else if x == 70 {
- x += 70
- } else if x == 71 {
- x += 71
- } else if x == 72 {
- x += 72
- } else if x == 73 {
- x += 73
- } else if x == 74 {
- x += 74
- } else if x == 75 {
- x += 75
- } else if x == 76 {
- x += 76
- } else if x == 77 {
- x += 77
- } else if x == 78 {
- x += 78
- } else if x == 79 {
- x += 79
- } else if x == 80 {
- x += 80
- } else if x == 81 {
- x += 81
- } else if x == 82 {
- x += 82
- } else if x == 83 {
- x += 83
- } else if x == 84 {
- x += 84
- } else if x == 85 {
- x += 85
- } else if x == 86 {
- x += 86
- } else if x == 87 {
- x += 87
- } else if x == 88 {
- x += 88
- } else if x == 89 {
- x += 89
- } else if x == 90 {
- x += 90
- } else if x == 91 {
- x += 91
- } else if x == 92 {
- x += 92
- } else if x == 93 {
- x += 93
- } else if x == 94 {
- x += 94
- } else if x == 95 {
- x += 95
- } else if x == 96 {
- x += 96
- } else if x == 97 {
- x += 97
- } else if x == 98 {
- x += 98
- } else if x == 99 {
- x += 99
- } else if x == 100 {
- x += 100
- } else if x == 101 {
- x += 101
- } else if x == 102 {
- x += 102
- } else if x == 103 {
- x += 103
- } else if x == 104 {
- x += 104
- } else if x == 105 {
- x += 105
- } else if x == 106 {
- x += 106
- } else if x == 107 {
- x += 107
- } else if x == 108 {
- x += 108
- } else if x == 109 {
- x += 109
- } else if x == 110 {
- x += 110
- } else if x == 111 {
- x += 111
- } else if x == 112 {
- x += 112
- } else if x == 113 {
- x += 113
- } else if x == 114 {
- x += 114
- } else if x == 115 {
- x += 115
- } else if x == 116 {
- x += 116
- } else if x == 117 {
- x += 117
- } else if x == 118 {
- x += 118
- } else if x == 119 {
- x += 119
- } else if x == 120 {
- x += 120
- } else if x == 121 {
- x += 121
- } else if x == 122 {
- x += 122
- } else if x == 123 {
- x += 123
- } else if x == 124 {
- x += 124
- } else if x == 125 {
- x += 125
- } else if x == 126 {
- x += 126
- } else if x == 127 {
- x += 127
- } else if x == 128 {
- x += 128
- } else if x == 129 {
- x += 129
- } else if x == 130 {
- x += 130
- } else if x == 131 {
- x += 131
- } else if x == 132 {
- x += 132
- } else if x == 133 {
- x += 133
- } else if x == 134 {
- x += 134
- } else if x == 135 {
- x += 135
- } else if x == 136 {
- x += 136
- } else if x == 137 {
- x += 137
- } else if x == 138 {
- x += 138
- } else if x == 139 {
- x += 139
- } else if x == 140 {
- x += 140
- } else if x == 141 {
- x += 141
- } else if x == 142 {
- x += 142
- } else if x == 143 {
- x += 143
- } else if x == 144 {
- x += 144
- } else if x == 145 {
- x += 145
- } else if x == 146 {
- x += 146
- } else if x == 147 {
- x += 147
- } else if x == 148 {
- x += 148
- } else if x == 149 {
- x += 149
- } else if x == 150 {
- x += 150
- } else if x == 151 {
- x += 151
- } else if x == 152 {
- x += 152
- } else if x == 153 {
- x += 153
- } else if x == 154 {
- x += 154
- } else if x == 155 {
- x += 155
- } else if x == 156 {
- x += 156
- } else if x == 157 {
- x += 157
- } else if x == 158 {
- x += 158
- } else if x == 159 {
- x += 159
- } else if x == 160 {
- x += 160
- } else if x == 161 {
- x += 161
- } else if x == 162 {
- x += 162
- } else if x == 163 {
- x += 163
- } else if x == 164 {
- x += 164
- } else if x == 165 {
- x += 165
- } else if x == 166 {
- x += 166
- } else if x == 167 {
- x += 167
- } else if x == 168 {
- x += 168
- } else if x == 169 {
- x += 169
- } else if x == 170 {
- x += 170
- } else if x == 171 {
- x += 171
- } else if x == 172 {
- x += 172
- } else if x == 173 {
- x += 173
- } else if x == 174 {
- x += 174
- } else if x == 175 {
- x += 175
- } else if x == 176 {
- x += 176
- } else if x == 177 {
- x += 177
- } else if x == 178 {
- x += 178
- } else if x == 179 {
- x += 179
- } else if x == 180 {
- x += 180
- } else if x == 181 {
- x += 181
- } else if x == 182 {
- x += 182
- } else if x == 183 {
- x += 183
- } else if x == 184 {
- x += 184
- } else if x == 185 {
- x += 185
- } else if x == 186 {
- x += 186
- } else if x == 187 {
- x += 187
- } else if x == 188 {
- x += 188
- } else if x == 189 {
- x += 189
- } else if x == 190 {
- x += 190
- } else if x == 191 {
- x += 191
- } else if x == 192 {
- x += 192
- } else if x == 193 {
- x += 193
- } else if x == 194 {
- x += 194
- } else if x == 195 {
- x += 195
- } else if x == 196 {
- x += 196
- } else if x == 197 {
- x += 197
- } else if x == 198 {
- x += 198
- } else if x == 199 {
- x += 199
- } else if x == 200 {
- x += 200
- } else if x == 201 {
- x += 201
- } else if x == 202 {
- x += 202
- } else if x == 203 {
- x += 203
- } else if x == 204 {
- x += 204
- } else if x == 205 {
- x += 205
- } else if x == 206 {
- x += 206
- } else if x == 207 {
- x += 207
- } else if x == 208 {
- x += 208
- } else if x == 209 {
- x += 209
- } else if x == 210 {
- x += 210
- } else if x == 211 {
- x += 211
- } else if x == 212 {
- x += 212
- } else if x == 213 {
- x += 213
- } else if x == 214 {
- x += 214
- } else if x == 215 {
- x += 215
- } else if x == 216 {
- x += 216
- } else if x == 217 {
- x += 217
- } else if x == 218 {
- x += 218
- } else if x == 219 {
- x += 219
- } else if x == 220 {
- x += 220
- } else if x == 221 {
- x += 221
- } else if x == 222 {
- x += 222
- } else if x == 223 {
- x += 223
- } else if x == 224 {
- x += 224
- } else if x == 225 {
- x += 225
- } else if x == 226 {
- x += 226
- } else if x == 227 {
- x += 227
- } else if x == 228 {
- x += 228
- } else if x == 229 {
- x += 229
- } else if x == 230 {
- x += 230
- } else if x == 231 {
- x += 231
- } else if x == 232 {
- x += 232
- } else if x == 233 {
- x += 233
- } else if x == 234 {
- x += 234
- } else if x == 235 {
- x += 235
- } else if x == 236 {
- x += 236
- } else if x == 237 {
- x += 237
- } else if x == 238 {
- x += 238
- } else if x == 239 {
- x += 239
- } else if x == 240 {
- x += 240
- } else if x == 241 {
- x += 241
- } else if x == 242 {
- x += 242
- } else if x == 243 {
- x += 243
- } else if x == 244 {
- x += 244
- } else if x == 245 {
- x += 245
- } else if x == 246 {
- x += 246
- } else if x == 247 {
- x += 247
- } else if x == 248 {
- x += 248
- } else if x == 249 {
- x += 249
- } else if x == 250 {
- x += 250
- } else if x == 251 {
- x += 251
- } else if x == 252 {
- x += 252
- } else if x == 253 {
- x += 253
- } else if x == 254 {
- x += 254
- } else if x == 255 {
- x += 255
- } else if x == 256 {
- x += 256
- } else if x == 257 {
- x += 257
- } else if x == 258 {
- x += 258
- } else if x == 259 {
- x += 259
- } else if x == 260 {
- x += 260
- } else if x == 261 {
- x += 261
- } else if x == 262 {
- x += 262
- } else if x == 263 {
- x += 263
- } else if x == 264 {
- x += 264
- } else if x == 265 {
- x += 265
- } else if x == 266 {
- x += 266
- } else if x == 267 {
- x += 267
- } else if x == 268 {
- x += 268
- } else if x == 269 {
- x += 269
- } else if x == 270 {
- x += 270
- } else if x == 271 {
- x += 271
- } else if x == 272 {
- x += 272
- } else if x == 273 {
- x += 273
- } else if x == 274 {
- x += 274
- } else if x == 275 {
- x += 275
- } else if x == 276 {
- x += 276
- } else if x == 277 {
- x += 277
- } else if x == 278 {
- x += 278
- } else if x == 279 {
- x += 279
- } else if x == 280 {
- x += 280
- } else if x == 281 {
- x += 281
- } else if x == 282 {
- x += 282
- } else if x == 283 {
- x += 283
- } else if x == 284 {
- x += 284
- } else if x == 285 {
- x += 285
- } else if x == 286 {
- x += 286
- } else if x == 287 {
- x += 287
- } else if x == 288 {
- x += 288
- } else if x == 289 {
- x += 289
- } else if x == 290 {
- x += 290
- } else if x == 291 {
- x += 291
- } else if x == 292 {
- x += 292
- } else if x == 293 {
- x += 293
- } else if x == 294 {
- x += 294
- } else if x == 295 {
- x += 295
- } else if x == 296 {
- x += 296
- } else if x == 297 {
- x += 297
- } else if x == 298 {
- x += 298
- } else if x == 299 {
- x += 299
- } else if x == 300 {
- x += 300
- } else if x == 301 {
- x += 301
- } else if x == 302 {
- x += 302
- } else if x == 303 {
- x += 303
- } else if x == 304 {
- x += 304
- } else if x == 305 {
- x += 305
- } else if x == 306 {
- x += 306
- } else if x == 307 {
- x += 307
- } else if x == 308 {
- x += 308
- } else if x == 309 {
- x += 309
- } else if x == 310 {
- x += 310
- } else if x == 311 {
- x += 311
- } else if x == 312 {
- x += 312
- } else if x == 313 {
- x += 313
- } else if x == 314 {
- x += 314
- } else if x == 315 {
- x += 315
- } else if x == 316 {
- x += 316
- } else if x == 317 {
- x += 317
- } else if x == 318 {
- x += 318
- } else if x == 319 {
- x += 319
- } else if x == 320 {
- x += 320
- } else if x == 321 {
- x += 321
- } else if x == 322 {
- x += 322
- } else if x == 323 {
- x += 323
- } else if x == 324 {
- x += 324
- } else if x == 325 {
- x += 325
- } else if x == 326 {
- x += 326
- } else if x == 327 {
- x += 327
- } else if x == 328 {
- x += 328
- } else if x == 329 {
- x += 329
- } else if x == 330 {
- x += 330
- } else if x == 331 {
- x += 331
- } else if x == 332 {
- x += 332
- } else if x == 333 {
- x += 333
- } else if x == 334 {
- x += 334
- } else if x == 335 {
- x += 335
- } else if x == 336 {
- x += 336
- } else if x == 337 {
- x += 337
- } else if x == 338 {
- x += 338
- } else if x == 339 {
- x += 339
- } else if x == 340 {
- x += 340
- } else if x == 341 {
- x += 341
- } else if x == 342 {
- x += 342
- } else if x == 343 {
- x += 343
- } else if x == 344 {
- x += 344
- } else if x == 345 {
- x += 345
- } else if x == 346 {
- x += 346
- } else if x == 347 {
- x += 347
- } else if x == 348 {
- x += 348
- } else if x == 349 {
- x += 349
- } else if x == 350 {
- x += 350
- } else if x == 351 {
- x += 351
- } else if x == 352 {
- x += 352
- } else if x == 353 {
- x += 353
- } else if x == 354 {
- x += 354
- } else if x == 355 {
- x += 355
- } else if x == 356 {
- x += 356
- } else if x == 357 {
- x += 357
- } else if x == 358 {
- x += 358
- } else if x == 359 {
- x += 359
- } else if x == 360 {
- x += 360
- } else if x == 361 {
- x += 361
- } else if x == 362 {
- x += 362
- } else if x == 363 {
- x += 363
- } else if x == 364 {
- x += 364
- } else if x == 365 {
- x += 365
- } else if x == 366 {
- x += 366
- } else if x == 367 {
- x += 367
- } else if x == 368 {
- x += 368
- } else if x == 369 {
- x += 369
- } else if x == 370 {
- x += 370
- } else if x == 371 {
- x += 371
- } else if x == 372 {
- x += 372
- } else if x == 373 {
- x += 373
- } else if x == 374 {
- x += 374
- } else if x == 375 {
- x += 375
- } else if x == 376 {
- x += 376
- } else if x == 377 {
- x += 377
- } else if x == 378 {
- x += 378
- } else if x == 379 {
- x += 379
- } else if x == 380 {
- x += 380
- } else if x == 381 {
- x += 381
- } else if x == 382 {
- x += 382
- } else if x == 383 {
- x += 383
- } else if x == 384 {
- x += 384
- } else if x == 385 {
- x += 385
- } else if x == 386 {
- x += 386
- } else if x == 387 {
- x += 387
- } else if x == 388 {
- x += 388
- } else if x == 389 {
- x += 389
- } else if x == 390 {
- x += 390
- } else if x == 391 {
- x += 391
- } else if x == 392 {
- x += 392
- } else if x == 393 {
- x += 393
- } else if x == 394 {
- x += 394
- } else if x == 395 {
- x += 395
- } else if x == 396 {
- x += 396
- } else if x == 397 {
- x += 397
- } else if x == 398 {
- x += 398
- } else if x == 399 {
- x += 399
- } else if x == 400 {
- x += 400
- }
- return x * x
-}
diff --git a/src/runtime/coverage/testdata/issue59563/repro_test.go b/src/runtime/coverage/testdata/issue59563/repro_test.go
deleted file mode 100644
index 15c8e01a28..0000000000
--- a/src/runtime/coverage/testdata/issue59563/repro_test.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2023 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 repro
-
-import "testing"
-
-func TestSomething(t *testing.T) {
- small()
- for i := 0; i < 1001; i++ {
- large(i)
- }
-}
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)
-}
diff --git a/src/runtime/coverage/ts_test.go b/src/runtime/coverage/ts_test.go
deleted file mode 100644
index b4c6e9716c..0000000000
--- a/src/runtime/coverage/ts_test.go
+++ /dev/null
@@ -1,207 +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"
- "internal/coverage"
- "internal/goexperiment"
- "internal/testenv"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
- "testing"
- _ "unsafe"
-)
-
-//go:linkname testing_testGoCoverDir testing.testGoCoverDir
-func testing_testGoCoverDir() string
-
-func testGoCoverDir(t *testing.T) string {
- tgcd := testing_testGoCoverDir()
- if tgcd != "" {
- return tgcd
- }
- return t.TempDir()
-}
-
-// TestTestSupport does a basic verification of the functionality in
-// runtime/coverage.processCoverTestDir (doing this here as opposed to
-// relying on other test paths will provide a better signal when
-// running "go test -cover" for this package).
-func TestTestSupport(t *testing.T) {
- if !goexperiment.CoverageRedesign {
- return
- }
- if testing.CoverMode() == "" {
- return
- }
- tgcd := testGoCoverDir(t)
- t.Logf("testing.testGoCoverDir() returns %s mode=%s\n",
- tgcd, testing.CoverMode())
-
- textfile := filepath.Join(t.TempDir(), "file.txt")
- var sb strings.Builder
- err := processCoverTestDirInternal(tgcd, textfile,
- testing.CoverMode(), "", &sb)
- if err != nil {
- t.Fatalf("bad: %v", err)
- }
-
- // Check for existence of text file.
- if inf, err := os.Open(textfile); err != nil {
- t.Fatalf("problems opening text file %s: %v", textfile, err)
- } else {
- inf.Close()
- }
-
- // Check for percent output with expected tokens.
- strout := sb.String()
- want := "of statements"
- if !strings.Contains(strout, want) {
- t.Logf("output from run: %s\n", strout)
- 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)
- }
-}
-
-const hellogo = `
-package main
-
-func main() {
- println("hello")
-}
-`
-
-// Returns a pair F,T where F is a meta-data file generated from
-// "hello.go" above, and T is a token to look for that should be
-// present in the coverage report from F.
-func genAuxMeta(t *testing.T, dstdir string) (string, string) {
- // Do a GOCOVERDIR=<tmp> go run hello.go
- src := filepath.Join(dstdir, "hello.go")
- if err := os.WriteFile(src, []byte(hellogo), 0777); err != nil {
- t.Fatalf("write failed: %v", err)
- }
- args := []string{"run", "-covermode=" + testing.CoverMode(), src}
- cmd := exec.Command(testenv.GoToolPath(t), args...)
- cmd.Env = updateGoCoverDir(os.Environ(), dstdir, true)
- if b, err := cmd.CombinedOutput(); err != nil {
- t.Fatalf("go run failed (%v): %s", err, b)
- }
-
- // Pick out the generated meta-data file.
- files, err := os.ReadDir(dstdir)
- if err != nil {
- t.Fatalf("reading %s: %v", dstdir, err)
- }
- for _, f := range files {
- if strings.HasPrefix(f.Name(), "covmeta") {
- return filepath.Join(dstdir, f.Name()), "hello.go:"
- }
- }
- t.Fatalf("could not locate generated meta-data file")
- return "", ""
-}
-
-func TestAuxMetaDataFiles(t *testing.T) {
- if !goexperiment.CoverageRedesign {
- return
- }
- if testing.CoverMode() == "" {
- return
- }
- testenv.MustHaveGoRun(t)
- tgcd := testGoCoverDir(t)
- t.Logf("testing.testGoCoverDir() returns %s mode=%s\n",
- tgcd, testing.CoverMode())
-
- td := t.TempDir()
-
- // Manufacture a new, separate meta-data file not related to this
- // test. Contents are not important, just so long as the
- // packages/paths are different.
- othermetadir := filepath.Join(td, "othermeta")
- if err := os.Mkdir(othermetadir, 0777); err != nil {
- t.Fatalf("mkdir failed: %v", err)
- }
- mfile, token := genAuxMeta(t, othermetadir)
-
- // Write a metafiles file.
- metafiles := filepath.Join(tgcd, coverage.MetaFilesFileName)
- mfc := coverage.MetaFileCollection{
- ImportPaths: []string{"command-line-arguments"},
- MetaFileFragments: []string{mfile},
- }
- jdata, err := json.Marshal(mfc)
- if err != nil {
- t.Fatalf("marshal MetaFileCollection: %v", err)
- }
- if err := os.WriteFile(metafiles, jdata, 0666); err != nil {
- t.Fatalf("write failed: %v", err)
- }
-
- // Kick off guts of test.
- var sb strings.Builder
- textfile := filepath.Join(td, "file2.txt")
- err = processCoverTestDirInternal(tgcd, textfile,
- testing.CoverMode(), "", &sb)
- if err != nil {
- t.Fatalf("bad: %v", err)
- }
- if err = os.Remove(metafiles); err != nil {
- t.Fatalf("removing metafiles file: %v", err)
- }
-
- // Look for the expected things in the coverage profile.
- contents, err := os.ReadFile(textfile)
- strc := string(contents)
- if err != nil {
- t.Fatalf("problems reading text file %s: %v", textfile, err)
- }
- if !strings.Contains(strc, token) {
- t.Logf("content: %s\n", string(contents))
- t.Fatalf("cov profile does not contain aux meta content %q", token)
- }
-}