]> Cypherpunks repositories - gostls13.git/commitdiff
runtime/coverage: remove uses of //go:linkname
authorRuss Cox <rsc@golang.org>
Wed, 15 May 2024 20:06:23 +0000 (16:06 -0400)
committerGopher Robot <gobot@golang.org>
Tue, 21 May 2024 19:41:02 +0000 (19:41 +0000)
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>
20 files changed:
src/cmd/go/internal/load/test.go
src/go/build/deps_test.go
src/internal/coverage/cfile/apis.go [moved from src/runtime/coverage/apis.go with 74% similarity]
src/internal/coverage/cfile/emit.go [moved from src/runtime/coverage/emit.go with 97% similarity]
src/internal/coverage/cfile/emitdata_test.go [moved from src/runtime/coverage/emitdata_test.go with 98% similarity]
src/internal/coverage/cfile/hooks.go [moved from src/runtime/coverage/hooks.go with 86% similarity]
src/internal/coverage/cfile/testdata/harness.go [moved from src/runtime/coverage/testdata/harness.go with 100% similarity]
src/internal/coverage/cfile/testdata/issue56006/repro.go [moved from src/runtime/coverage/testdata/issue56006/repro.go with 100% similarity]
src/internal/coverage/cfile/testdata/issue56006/repro_test.go [moved from src/runtime/coverage/testdata/issue56006/repro_test.go with 100% similarity]
src/internal/coverage/cfile/testdata/issue59563/repro.go [moved from src/runtime/coverage/testdata/issue59563/repro.go with 100% similarity]
src/internal/coverage/cfile/testdata/issue59563/repro_test.go [moved from src/runtime/coverage/testdata/issue59563/repro_test.go with 100% similarity]
src/internal/coverage/cfile/testsupport.go [moved from src/runtime/coverage/testsupport.go with 92% similarity]
src/internal/coverage/cfile/ts_test.go [moved from src/runtime/coverage/ts_test.go with 95% similarity]
src/runtime/coverage/coverage.go [new file with mode: 0644]
src/runtime/coverage/dummy.s [deleted file]
src/runtime/covercounter.go
src/runtime/covermeta.go
src/testing/internal/testdeps/deps.go
src/testing/newcover.go
src/testing/testing.go

index 3135805905b8501b38c15afc8e31beadf5df1849..a9f0d452fc2493e324c8e0b10027967609745ac0 100644 (file)
@@ -902,9 +902,6 @@ package main
 
 import (
        "os"
-{{if .Cover}}
-       _ "unsafe"
-{{end}}
 {{if .TestMain}}
        "reflect"
 {{end}}
@@ -944,45 +941,14 @@ var examples = []testing.InternalExample{
 }
 
 func init() {
-       testdeps.ImportPath = {{.ImportPath | printf "%q"}}
-}
-
 {{if .Cover}}
-
-//go:linkname runtime_coverage_processCoverTestDir runtime/coverage.processCoverTestDir
-func runtime_coverage_processCoverTestDir(dir string, cfile string, cmode string, cpkgs string) error
-
-//go:linkname testing_registerCover2 testing.registerCover2
-func testing_registerCover2(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64)
-
-//go:linkname runtime_coverage_markProfileEmitted runtime/coverage.markProfileEmitted
-func runtime_coverage_markProfileEmitted(val bool)
-
-//go:linkname runtime_coverage_snapshot runtime/coverage.snapshot
-func runtime_coverage_snapshot() float64
-
-func coverTearDown(coverprofile string, gocoverdir string) (string, error) {
-       var err error
-       if gocoverdir == "" {
-               gocoverdir, err = os.MkdirTemp("", "gocoverdir")
-               if err != nil {
-                       return "error setting GOCOVERDIR: bad os.MkdirTemp return", err
-               }
-               defer os.RemoveAll(gocoverdir)
-       }
-       runtime_coverage_markProfileEmitted(true)
-       cmode := {{printf "%q" .Cover.Mode}}
-       if err := runtime_coverage_processCoverTestDir(gocoverdir, coverprofile, cmode, {{printf "%q" .Covered}}); err != nil {
-               return "error generating coverage report", err
-       }
-       return "", nil
-}
+       testdeps.CoverMode = {{printf "%q" .Cover.Mode}}
+       testdeps.Covered = {{printf "%q" .Covered}}
 {{end}}
+       testdeps.ImportPath = {{.ImportPath | printf "%q"}}
+}
 
 func main() {
-{{if .Cover}}
-       testing_registerCover2({{printf "%q" .Cover.Mode}}, coverTearDown, runtime_coverage_snapshot)
-{{end}}
        m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples)
 {{with .TestMain}}
        {{.Package}}.{{.Name}}(m)
index 067298cf42507a2c652453d99b26f4732b5af82f..4df56ab78aa438bb9583b4e6723e4c44e3c25d1b 100644 (file)
@@ -608,9 +608,6 @@ var depsRules = `
        internal/godebug, math/rand, encoding/hex, crypto/sha256
        < internal/fuzz;
 
-       internal/fuzz, internal/testlog, runtime/pprof, regexp
-       < testing/internal/testdeps;
-
        OS, flag, testing, internal/cfg, internal/platform, internal/goroot
        < internal/testenv;
 
@@ -691,8 +688,12 @@ var depsRules = `
        internal/coverage/decodecounter, internal/coverage/decodemeta,
        internal/coverage/encodecounter, internal/coverage/encodemeta,
        internal/coverage/pods
+       < internal/coverage/cfile
        < runtime/coverage;
 
+       internal/coverage/cfile, internal/fuzz, internal/testlog, runtime/pprof, regexp
+       < testing/internal/testdeps;
+
        # Test-only packages can have anything they want
        CGO, internal/syscall/unix < net/internal/cgotest;
 
similarity index 74%
rename from src/runtime/coverage/apis.go
rename to src/internal/coverage/cfile/apis.go
index 15ba04a86f9cd0dc3ae3d9cfb833c2c931f7d91c..efae20495ba1c3201dfcfad86896aa8655d578b8 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package coverage
+package cfile
 
 import (
        "fmt"
@@ -12,11 +12,7 @@ import (
        "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).
+// WriteMetaDir implements [runtime/coverage.WriteMetaDir].
 func WriteMetaDir(dir string) error {
        if !finalHashComputed {
                return fmt.Errorf("error: no meta-data available (binary not built with -cover?)")
@@ -24,12 +20,7 @@ func WriteMetaDir(dir string) error {
        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).
+// WriteMeta implements [runtime/coverage.WriteMeta].
 func WriteMeta(w io.Writer) error {
        if w == nil {
                return fmt.Errorf("error: nil writer in WriteMeta")
@@ -41,13 +32,7 @@ func WriteMeta(w io.Writer) error {
        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.
+// WriteCountersDir implements [runtime/coverage.WriteCountersDir].
 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())
@@ -55,12 +40,7 @@ func WriteCountersDir(dir string) error {
        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.
+// WriteCounters implements [runtime/coverage.WriteCounters].
 func WriteCounters(w io.Writer) error {
        if w == nil {
                return fmt.Errorf("error: nil writer in WriteCounters")
@@ -85,12 +65,7 @@ func WriteCounters(w io.Writer) error {
        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).
+// ClearCounters implements [runtime/coverage.ClearCounters].
 func ClearCounters() error {
        cl := getCovCounterList()
        if len(cl) == 0 {
similarity index 97%
rename from src/runtime/coverage/emit.go
rename to src/internal/coverage/cfile/emit.go
index 6510c889eaa5b55cc1dfcfc152258e010818d7e2..68d77c5ae890b363cb5b0dd92bf06eff122eab49 100644 (file)
@@ -2,7 +2,11 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package coverage
+// Package cfile implements management of coverage files.
+// It provides functionality exported in runtime/coverage as well as
+// additional functionality used directly by package testing
+// through testing/internal/testdeps.
+package cfile
 
 import (
        "crypto/md5"
@@ -28,17 +32,20 @@ import (
 // getCovMetaList returns a list of meta-data blobs registered
 // for the currently executing instrumented program. It is defined in the
 // runtime.
+//go:linkname getCovMetaList
 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.
+//go:linkname getCovCounterList
 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.
+//go:linkname getCovPkgMap
 func getCovPkgMap() map[int]int
 
 // emitState holds useful state information during the emit process.
@@ -574,16 +581,12 @@ func (s *emitState) emitCounterDataFile(finalHash [16]byte, w io.Writer) error {
        return nil
 }
 
-// markProfileEmitted is injected to testmain via linkname.
-//go:linkname markProfileEmitted
-
-// markProfileEmitted signals the runtime/coverage machinery that
+// MarkProfileEmitted signals the 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) {
+// function is called from the coverage-related boilerplate code in _testmain.go
+// emitted for go unit tests.
+func MarkProfileEmitted(val bool) {
        covProfileAlreadyEmitted = val
 }
 
similarity index 98%
rename from src/runtime/coverage/emitdata_test.go
rename to src/internal/coverage/cfile/emitdata_test.go
index 3558dd2d88cb180be8abb6e1b81943c3fe7ef382..a6f2d99a173d273a6af411922519d89d98806123 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package coverage
+package cfile
 
 import (
        "fmt"
@@ -484,7 +484,7 @@ func TestIssue56006EmitDataRaceCoverRunningGoroutine(t *testing.T) {
        cmd.Dir = filepath.Join("testdata", "issue56006")
        b, err := cmd.CombinedOutput()
        if err != nil {
-               t.Fatalf("go test -cover -race failed: %v", err)
+               t.Fatalf("go test -cover -race failed: %v\n%s", err, b)
        }
 
        // Don't want to see any data races in output.
@@ -510,7 +510,7 @@ func TestIssue59563TruncatedCoverPkgAll(t *testing.T) {
        cmd.Dir = filepath.Join("testdata", "issue59563")
        b, err := cmd.CombinedOutput()
        if err != nil {
-               t.Fatalf("go test -cover failed: %v", err)
+               t.Fatalf("go test -cover failed: %v\n%s", err, b)
        }
 
        cmd = exec.Command(testenv.GoToolPath(t), "tool", "cover", "-func="+ppath)
@@ -530,7 +530,7 @@ func TestIssue59563TruncatedCoverPkgAll(t *testing.T) {
                // 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")) {
+               if !(strings.HasPrefix(f[0], "internal/coverage/cfile/testdata/issue59563/repro.go") && strings.Contains(line, "large")) {
                        continue
                }
                nfound++
similarity index 86%
rename from src/runtime/coverage/hooks.go
rename to src/internal/coverage/cfile/hooks.go
index a9fbf9d7dd1842adea92f71722be3a2a33d20c29..003d6ca1e5652e79ea92a4d4f4d0f5b6294e0dd2 100644 (file)
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package coverage
+package cfile
 
 import _ "unsafe"
 
-// initHook is invoked from the main package "init" routine in
+// 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.
+// called only by the compiler (via runtime/coverage.initHook).
 //
 // If 'istest' is false, it indicates we're building a regular program
 // ("go build -cover ..."), in which case we immediately try to write
@@ -20,12 +20,12 @@ import _ "unsafe"
 // 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
+// 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) {
+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).
similarity index 92%
rename from src/runtime/coverage/testsupport.go
rename to src/internal/coverage/cfile/testsupport.go
index b673d3cd2c27b93ef0114b7b096ad22e4c0771ac..2a64899e2838aea0a79c12881740a12d5f77dca7 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package coverage
+package cfile
 
 import (
        "encoding/json"
@@ -22,20 +22,11 @@ import (
        "unsafe"
 )
 
-// processCoverTestDir is injected in testmain.
-//go:linkname processCoverTestDir
-
-// processCoverTestDir is called (via a linknamed reference) from
+// ProcessCoverTestDir is called 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 {
+func ProcessCoverTestDir(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)
@@ -280,16 +271,13 @@ func (ts *tstate) readAuxMetaFiles(metafiles string, importpaths map[string]stru
        return nil
 }
 
-// snapshot is injected in testmain.
-//go:linkname snapshot
-
-// snapshot returns a snapshot of coverage percentage at a moment of
+// 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 {
+func Snapshot() float64 {
        cl := getCovCounterList()
        if len(cl) == 0 {
                // no work to do here.
similarity index 95%
rename from src/runtime/coverage/ts_test.go
rename to src/internal/coverage/cfile/ts_test.go
index b4c6e9716cbd307ced5f900a3dfd675c37a4ef33..edbc603a1b8fbfccc146482fc0fce44ef1eae134 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package coverage
+package cfile
 
 import (
        "encoding/json"
@@ -29,7 +29,7 @@ func testGoCoverDir(t *testing.T) string {
 }
 
 // TestTestSupport does a basic verification of the functionality in
-// runtime/coverage.processCoverTestDir (doing this here as opposed to
+// 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) {
@@ -45,7 +45,7 @@ func TestTestSupport(t *testing.T) {
 
        textfile := filepath.Join(t.TempDir(), "file.txt")
        var sb strings.Builder
-       err := processCoverTestDirInternal(tgcd, textfile,
+       err := ProcessCoverTestDir(tgcd, textfile,
                testing.CoverMode(), "", &sb)
        if err != nil {
                t.Fatalf("bad: %v", err)
@@ -91,9 +91,9 @@ func thisFunctionOnlyCalledFromSnapshotTest(n int) int {
 // coverage is not enabled, the hook is designed to just return
 // zero.
 func TestCoverageSnapshot(t *testing.T) {
-       C1 := snapshot()
+       C1 := Snapshot()
        thisFunctionOnlyCalledFromSnapshotTest(15)
-       C2 := snapshot()
+       C2 := Snapshot()
        cond := "C1 > C2"
        val := C1 > C2
        if testing.CoverMode() != "" {
@@ -185,7 +185,7 @@ func TestAuxMetaDataFiles(t *testing.T) {
        // Kick off guts of test.
        var sb strings.Builder
        textfile := filepath.Join(td, "file2.txt")
-       err = processCoverTestDirInternal(tgcd, textfile,
+       err = ProcessCoverTestDir(tgcd, textfile,
                testing.CoverMode(), "", &sb)
        if err != nil {
                t.Fatalf("bad: %v", err)
diff --git a/src/runtime/coverage/coverage.go b/src/runtime/coverage/coverage.go
new file mode 100644 (file)
index 0000000..6b99a0b
--- /dev/null
@@ -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 (file)
index 7592859..0000000
+++ /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.
index 72842bdd94c88853206804c19ab6afb168928472..6dbc882d16e7afe0afca87e131057fd2a304bb3c 100644 (file)
@@ -9,8 +9,8 @@ import (
        "unsafe"
 )
 
-//go:linkname runtime_coverage_getCovCounterList runtime/coverage.getCovCounterList
-func runtime_coverage_getCovCounterList() []rtcov.CovCounterBlob {
+//go:linkname coverage_getCovCounterList internal/coverage/cfile.getCovCounterList
+func coverage_getCovCounterList() []rtcov.CovCounterBlob {
        res := []rtcov.CovCounterBlob{}
        u32sz := unsafe.Sizeof(uint32(0))
        for datap := &firstmoduledata; datap != nil; datap = datap.next {
index 54ef42ae1ff440bdcde58bd3700290383f074351..bfe43b84ab30259b61ef15b2f05e84089df7aead 100644 (file)
@@ -61,12 +61,12 @@ func addCovMeta(p unsafe.Pointer, dlen uint32, hash [16]byte, pkpath string, pki
        return uint32(slot + 1)
 }
 
-//go:linkname runtime_coverage_getCovMetaList runtime/coverage.getCovMetaList
-func runtime_coverage_getCovMetaList() []rtcov.CovMetaBlob {
+//go:linkname coverage_getCovMetaList internal/coverage/cfile.getCovMetaList
+func coverage_getCovMetaList() []rtcov.CovMetaBlob {
        return covMeta.metaList
 }
 
-//go:linkname runtime_coverage_getCovPkgMap runtime/coverage.getCovPkgMap
-func runtime_coverage_getCovPkgMap() map[int]int {
+//go:linkname coverage_getCovPkgMap internal/coverage/cfile.getCovPkgMap
+func coverage_getCovPkgMap() map[int]int {
        return covMeta.pkgMap
 }
index 868307550eab60005dbbf915d231aadb46271d4b..88c1b253eed52820d300e75a327e576e60a244da 100644 (file)
@@ -13,6 +13,7 @@ package testdeps
 import (
        "bufio"
        "context"
+       "internal/coverage/cfile"
        "internal/fuzz"
        "internal/testlog"
        "io"
@@ -26,6 +27,9 @@ import (
        "time"
 )
 
+// Cover indicates whether coverage is enabled.
+var Cover bool
+
 // TestDeps is an implementation of the testing.testDeps interface,
 // suitable for passing to [testing.MainStart].
 type TestDeps struct{}
@@ -197,3 +201,30 @@ func (TestDeps) ResetCoverage() {
 func (TestDeps) SnapshotCoverage() {
        fuzz.SnapshotCoverage()
 }
+
+var CoverMode string
+var Covered string
+
+func (TestDeps) InitRuntimeCoverage() (mode string, tearDown func(string, string) (string, error), snapcov func() float64) {
+       if CoverMode == "" {
+               return
+       }
+       return CoverMode, coverTearDown, cfile.Snapshot
+}
+
+func coverTearDown(coverprofile string, gocoverdir string) (string, error) {
+       var err error
+       if gocoverdir == "" {
+               gocoverdir, err = os.MkdirTemp("", "gocoverdir")
+               if err != nil {
+                       return "error setting GOCOVERDIR: bad os.MkdirTemp return", err
+               }
+               defer os.RemoveAll(gocoverdir)
+       }
+       cfile.MarkProfileEmitted(true)
+       cmode := CoverMode
+       if err := cfile.ProcessCoverTestDir(gocoverdir, coverprofile, cmode, Covered, os.Stdout); err != nil {
+               return "error generating coverage report", err
+       }
+       return "", nil
+}
index 7a70dcfffa5e8f7c843303fecbe033f8a4bbb31f..a7dbcfc65a19982f571c503689d309fb9dfb5a17 100644 (file)
@@ -21,13 +21,13 @@ var cover2 struct {
        snapshotcov func() float64
 }
 
-// registerCover2 is injected in testmain.
-//go:linkname registerCover2
-
-// registerCover2 is invoked during "go test -cover" runs by the test harness
-// code in _testmain.go; it is used to record a 'tear down' function
+// registerCover2 is invoked during "go test -cover" runs.
+// It is used to record a 'tear down' function
 // (to be called when the test is complete) and the coverage mode.
 func registerCover2(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64) {
+       if mode == "" {
+               return
+       }
        cover2.mode = mode
        cover2.tearDown = tearDown
        cover2.snapshotcov = snapcov
index 60f0c23137d6f03fa0a87368241453c2f2d540d0..78ad8dbaaca421b523600486af48c3644e1c207c 100644 (file)
@@ -1855,6 +1855,10 @@ func (f matchStringOnly) CheckCorpus([]any, []reflect.Type) error { return nil }
 func (f matchStringOnly) ResetCoverage()                          {}
 func (f matchStringOnly) SnapshotCoverage()                       {}
 
+func (f matchStringOnly) InitRuntimeCoverage() (mode string, tearDown func(string, string) (string, error), snapcov func() float64) {
+       return
+}
+
 // Main is an internal function, part of the implementation of the "go test" command.
 // It was exported because it is cross-package and predates "internal" packages.
 // It is no longer used by "go test" but preserved, as much as possible, for other
@@ -1902,12 +1906,14 @@ type testDeps interface {
        CheckCorpus([]any, []reflect.Type) error
        ResetCoverage()
        SnapshotCoverage()
+       InitRuntimeCoverage() (mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64)
 }
 
 // MainStart is meant for use by tests generated by 'go test'.
 // It is not meant to be called directly and is not subject to the Go 1 compatibility document.
 // It may change signature from release to release.
 func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M {
+       registerCover2(deps.InitRuntimeCoverage())
        Init()
        return &M{
                deps:        deps,