mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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>
This commit is contained in:
parent
647870becc
commit
180ea45566
20 changed files with 160 additions and 132 deletions
|
|
@ -902,9 +902,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
{{if .Cover}}
|
|
||||||
_ "unsafe"
|
|
||||||
{{end}}
|
|
||||||
{{if .TestMain}}
|
{{if .TestMain}}
|
||||||
"reflect"
|
"reflect"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
@ -944,45 +941,14 @@ var examples = []testing.InternalExample{
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
{{if .Cover}}
|
||||||
|
testdeps.CoverMode = {{printf "%q" .Cover.Mode}}
|
||||||
|
testdeps.Covered = {{printf "%q" .Covered}}
|
||||||
|
{{end}}
|
||||||
testdeps.ImportPath = {{.ImportPath | printf "%q"}}
|
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
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
{{if .Cover}}
|
|
||||||
testing_registerCover2({{printf "%q" .Cover.Mode}}, coverTearDown, runtime_coverage_snapshot)
|
|
||||||
{{end}}
|
|
||||||
m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples)
|
m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples)
|
||||||
{{with .TestMain}}
|
{{with .TestMain}}
|
||||||
{{.Package}}.{{.Name}}(m)
|
{{.Package}}.{{.Name}}(m)
|
||||||
|
|
|
||||||
|
|
@ -608,9 +608,6 @@ var depsRules = `
|
||||||
internal/godebug, math/rand, encoding/hex, crypto/sha256
|
internal/godebug, math/rand, encoding/hex, crypto/sha256
|
||||||
< internal/fuzz;
|
< internal/fuzz;
|
||||||
|
|
||||||
internal/fuzz, internal/testlog, runtime/pprof, regexp
|
|
||||||
< testing/internal/testdeps;
|
|
||||||
|
|
||||||
OS, flag, testing, internal/cfg, internal/platform, internal/goroot
|
OS, flag, testing, internal/cfg, internal/platform, internal/goroot
|
||||||
< internal/testenv;
|
< internal/testenv;
|
||||||
|
|
||||||
|
|
@ -691,8 +688,12 @@ var depsRules = `
|
||||||
internal/coverage/decodecounter, internal/coverage/decodemeta,
|
internal/coverage/decodecounter, internal/coverage/decodemeta,
|
||||||
internal/coverage/encodecounter, internal/coverage/encodemeta,
|
internal/coverage/encodecounter, internal/coverage/encodemeta,
|
||||||
internal/coverage/pods
|
internal/coverage/pods
|
||||||
|
< internal/coverage/cfile
|
||||||
< runtime/coverage;
|
< runtime/coverage;
|
||||||
|
|
||||||
|
internal/coverage/cfile, internal/fuzz, internal/testlog, runtime/pprof, regexp
|
||||||
|
< testing/internal/testdeps;
|
||||||
|
|
||||||
# Test-only packages can have anything they want
|
# Test-only packages can have anything they want
|
||||||
CGO, internal/syscall/unix < net/internal/cgotest;
|
CGO, internal/syscall/unix < net/internal/cgotest;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package coverage
|
package cfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -12,11 +12,7 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WriteMetaDir writes a coverage meta-data file for the currently
|
// WriteMetaDir implements [runtime/coverage.WriteMetaDir].
|
||||||
// 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 {
|
func WriteMetaDir(dir string) error {
|
||||||
if !finalHashComputed {
|
if !finalHashComputed {
|
||||||
return fmt.Errorf("error: no meta-data available (binary not built with -cover?)")
|
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())
|
return emitMetaDataToDirectory(dir, getCovMetaList())
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteMeta writes the meta-data content (the payload that would
|
// WriteMeta implements [runtime/coverage.WriteMeta].
|
||||||
// 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 {
|
func WriteMeta(w io.Writer) error {
|
||||||
if w == nil {
|
if w == nil {
|
||||||
return fmt.Errorf("error: nil writer in WriteMeta")
|
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)
|
return writeMetaData(w, ml, cmode, cgran, finalHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteCountersDir writes a coverage counter-data file for the
|
// WriteCountersDir implements [runtime/coverage.WriteCountersDir].
|
||||||
// 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 {
|
func WriteCountersDir(dir string) error {
|
||||||
if cmode != coverage.CtrModeAtomic {
|
if cmode != coverage.CtrModeAtomic {
|
||||||
return fmt.Errorf("WriteCountersDir invoked for program built with -covermode=%s (please use -covermode=atomic)", cmode.String())
|
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)
|
return emitCounterDataToDirectory(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteCounters writes coverage counter-data content for the
|
// WriteCounters implements [runtime/coverage.WriteCounters].
|
||||||
// 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 {
|
func WriteCounters(w io.Writer) error {
|
||||||
if w == nil {
|
if w == nil {
|
||||||
return fmt.Errorf("error: nil writer in WriteCounters")
|
return fmt.Errorf("error: nil writer in WriteCounters")
|
||||||
|
|
@ -85,12 +65,7 @@ func WriteCounters(w io.Writer) error {
|
||||||
return s.emitCounterDataToWriter(w)
|
return s.emitCounterDataToWriter(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearCounters clears/resets all coverage counter variables in the
|
// ClearCounters implements [runtime/coverage.ClearCounters].
|
||||||
// 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 {
|
func ClearCounters() error {
|
||||||
cl := getCovCounterList()
|
cl := getCovCounterList()
|
||||||
if len(cl) == 0 {
|
if len(cl) == 0 {
|
||||||
|
|
@ -2,7 +2,11 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// 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 (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
|
|
@ -28,17 +32,20 @@ import (
|
||||||
// getCovMetaList returns a list of meta-data blobs registered
|
// getCovMetaList returns a list of meta-data blobs registered
|
||||||
// for the currently executing instrumented program. It is defined in the
|
// for the currently executing instrumented program. It is defined in the
|
||||||
// runtime.
|
// runtime.
|
||||||
|
//go:linkname getCovMetaList
|
||||||
func getCovMetaList() []rtcov.CovMetaBlob
|
func getCovMetaList() []rtcov.CovMetaBlob
|
||||||
|
|
||||||
// getCovCounterList returns a list of counter-data blobs registered
|
// getCovCounterList returns a list of counter-data blobs registered
|
||||||
// for the currently executing instrumented program. It is defined in the
|
// for the currently executing instrumented program. It is defined in the
|
||||||
// runtime.
|
// runtime.
|
||||||
|
//go:linkname getCovCounterList
|
||||||
func getCovCounterList() []rtcov.CovCounterBlob
|
func getCovCounterList() []rtcov.CovCounterBlob
|
||||||
|
|
||||||
// getCovPkgMap returns a map storing the remapped package IDs for
|
// getCovPkgMap returns a map storing the remapped package IDs for
|
||||||
// hard-coded runtime packages (see internal/coverage/pkgid.go for
|
// hard-coded runtime packages (see internal/coverage/pkgid.go for
|
||||||
// more on why hard-coded package IDs are needed). This function
|
// more on why hard-coded package IDs are needed). This function
|
||||||
// is defined in the runtime.
|
// is defined in the runtime.
|
||||||
|
//go:linkname getCovPkgMap
|
||||||
func getCovPkgMap() map[int]int
|
func getCovPkgMap() map[int]int
|
||||||
|
|
||||||
// emitState holds useful state information during the emit process.
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// markProfileEmitted is injected to testmain via linkname.
|
// MarkProfileEmitted signals the coverage machinery that
|
||||||
//go:linkname markProfileEmitted
|
|
||||||
|
|
||||||
// markProfileEmitted signals the runtime/coverage machinery that
|
|
||||||
// coverage data output files have already been written out, and there
|
// coverage data output files have already been written out, and there
|
||||||
// is no need to take any additional action at exit time. This
|
// is no need to take any additional action at exit time. This
|
||||||
// function is called (via linknamed reference) from the
|
// function is called from the coverage-related boilerplate code in _testmain.go
|
||||||
// coverage-related boilerplate code in _testmain.go emitted for go
|
// emitted for go unit tests.
|
||||||
// unit tests.
|
func MarkProfileEmitted(val bool) {
|
||||||
func markProfileEmitted(val bool) {
|
|
||||||
covProfileAlreadyEmitted = val
|
covProfileAlreadyEmitted = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package coverage
|
package cfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -484,7 +484,7 @@ func TestIssue56006EmitDataRaceCoverRunningGoroutine(t *testing.T) {
|
||||||
cmd.Dir = filepath.Join("testdata", "issue56006")
|
cmd.Dir = filepath.Join("testdata", "issue56006")
|
||||||
b, err := cmd.CombinedOutput()
|
b, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
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.
|
// 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")
|
cmd.Dir = filepath.Join("testdata", "issue59563")
|
||||||
b, err := cmd.CombinedOutput()
|
b, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
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)
|
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
|
// We're only interested in the specific function "large" for
|
||||||
// the testcase being built. See the #59563 for details on why
|
// the testcase being built. See the #59563 for details on why
|
||||||
// size matters.
|
// 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
|
continue
|
||||||
}
|
}
|
||||||
nfound++
|
nfound++
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package coverage
|
package cfile
|
||||||
|
|
||||||
import _ "unsafe"
|
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
|
// 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
|
// If 'istest' is false, it indicates we're building a regular program
|
||||||
// ("go build -cover ..."), in which case we immediately try to write
|
// ("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
|
// emitCounterData as exit hooks. In the normal case (e.g. regular "go
|
||||||
// test -cover" run) the testmain.go boilerplate will run at the end
|
// test -cover" run) the testmain.go boilerplate will run at the end
|
||||||
// of the test, write out the coverage percentage, and then invoke
|
// 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
|
// 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
|
// 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
|
// being tested, hence we do want to run exit hooks when the program
|
||||||
// terminates.
|
// terminates.
|
||||||
func initHook(istest bool) {
|
func InitHook(istest bool) {
|
||||||
// Note: hooks are run in reverse registration order, so
|
// Note: hooks are run in reverse registration order, so
|
||||||
// register the counter data hook before the meta-data hook
|
// register the counter data hook before the meta-data hook
|
||||||
// (in the case where two hooks are needed).
|
// (in the case where two hooks are needed).
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package coverage
|
package cfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
@ -22,20 +22,11 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// processCoverTestDir is injected in testmain.
|
// ProcessCoverTestDir is called from
|
||||||
//go:linkname processCoverTestDir
|
|
||||||
|
|
||||||
// processCoverTestDir is called (via a linknamed reference) from
|
|
||||||
// testmain code when "go test -cover" is in effect. It is not
|
// testmain code when "go test -cover" is in effect. It is not
|
||||||
// intended to be used other than internally by the Go command's
|
// intended to be used other than internally by the Go command's
|
||||||
// generated code.
|
// generated code.
|
||||||
func processCoverTestDir(dir string, cfile string, cm string, cpkg string) error {
|
func ProcessCoverTestDir(dir string, cfile string, cm string, cpkg string, w io.Writer) 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)
|
cmode := coverage.ParseCounterMode(cm)
|
||||||
if cmode == coverage.CtrModeInvalid {
|
if cmode == coverage.CtrModeInvalid {
|
||||||
return fmt.Errorf("invalid counter mode %q", cm)
|
return fmt.Errorf("invalid counter mode %q", cm)
|
||||||
|
|
@ -280,16 +271,13 @@ func (ts *tstate) readAuxMetaFiles(metafiles string, importpaths map[string]stru
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// snapshot is injected in testmain.
|
// Snapshot returns a snapshot of coverage percentage at a moment of
|
||||||
//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()
|
// time within a running test, so as to support the testing.Coverage()
|
||||||
// function. This version doesn't examine coverage meta-data, so the
|
// function. This version doesn't examine coverage meta-data, so the
|
||||||
// result it returns will be less accurate (more "slop") due to 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
|
// fact that we don't look at the meta data to see how many statements
|
||||||
// are associated with each counter.
|
// are associated with each counter.
|
||||||
func snapshot() float64 {
|
func Snapshot() float64 {
|
||||||
cl := getCovCounterList()
|
cl := getCovCounterList()
|
||||||
if len(cl) == 0 {
|
if len(cl) == 0 {
|
||||||
// no work to do here.
|
// no work to do here.
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package coverage
|
package cfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
@ -29,7 +29,7 @@ func testGoCoverDir(t *testing.T) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTestSupport does a basic verification of the functionality in
|
// 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
|
// relying on other test paths will provide a better signal when
|
||||||
// running "go test -cover" for this package).
|
// running "go test -cover" for this package).
|
||||||
func TestTestSupport(t *testing.T) {
|
func TestTestSupport(t *testing.T) {
|
||||||
|
|
@ -45,7 +45,7 @@ func TestTestSupport(t *testing.T) {
|
||||||
|
|
||||||
textfile := filepath.Join(t.TempDir(), "file.txt")
|
textfile := filepath.Join(t.TempDir(), "file.txt")
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
err := processCoverTestDirInternal(tgcd, textfile,
|
err := ProcessCoverTestDir(tgcd, textfile,
|
||||||
testing.CoverMode(), "", &sb)
|
testing.CoverMode(), "", &sb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("bad: %v", err)
|
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
|
// coverage is not enabled, the hook is designed to just return
|
||||||
// zero.
|
// zero.
|
||||||
func TestCoverageSnapshot(t *testing.T) {
|
func TestCoverageSnapshot(t *testing.T) {
|
||||||
C1 := snapshot()
|
C1 := Snapshot()
|
||||||
thisFunctionOnlyCalledFromSnapshotTest(15)
|
thisFunctionOnlyCalledFromSnapshotTest(15)
|
||||||
C2 := snapshot()
|
C2 := Snapshot()
|
||||||
cond := "C1 > C2"
|
cond := "C1 > C2"
|
||||||
val := C1 > C2
|
val := C1 > C2
|
||||||
if testing.CoverMode() != "" {
|
if testing.CoverMode() != "" {
|
||||||
|
|
@ -185,7 +185,7 @@ func TestAuxMetaDataFiles(t *testing.T) {
|
||||||
// Kick off guts of test.
|
// Kick off guts of test.
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
textfile := filepath.Join(td, "file2.txt")
|
textfile := filepath.Join(td, "file2.txt")
|
||||||
err = processCoverTestDirInternal(tgcd, textfile,
|
err = ProcessCoverTestDir(tgcd, textfile,
|
||||||
testing.CoverMode(), "", &sb)
|
testing.CoverMode(), "", &sb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("bad: %v", err)
|
t.Fatalf("bad: %v", err)
|
||||||
66
src/runtime/coverage/coverage.go
Normal file
66
src/runtime/coverage/coverage.go
Normal file
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:linkname runtime_coverage_getCovCounterList runtime/coverage.getCovCounterList
|
//go:linkname coverage_getCovCounterList internal/coverage/cfile.getCovCounterList
|
||||||
func runtime_coverage_getCovCounterList() []rtcov.CovCounterBlob {
|
func coverage_getCovCounterList() []rtcov.CovCounterBlob {
|
||||||
res := []rtcov.CovCounterBlob{}
|
res := []rtcov.CovCounterBlob{}
|
||||||
u32sz := unsafe.Sizeof(uint32(0))
|
u32sz := unsafe.Sizeof(uint32(0))
|
||||||
for datap := &firstmoduledata; datap != nil; datap = datap.next {
|
for datap := &firstmoduledata; datap != nil; datap = datap.next {
|
||||||
|
|
|
||||||
|
|
@ -61,12 +61,12 @@ func addCovMeta(p unsafe.Pointer, dlen uint32, hash [16]byte, pkpath string, pki
|
||||||
return uint32(slot + 1)
|
return uint32(slot + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname runtime_coverage_getCovMetaList runtime/coverage.getCovMetaList
|
//go:linkname coverage_getCovMetaList internal/coverage/cfile.getCovMetaList
|
||||||
func runtime_coverage_getCovMetaList() []rtcov.CovMetaBlob {
|
func coverage_getCovMetaList() []rtcov.CovMetaBlob {
|
||||||
return covMeta.metaList
|
return covMeta.metaList
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname runtime_coverage_getCovPkgMap runtime/coverage.getCovPkgMap
|
//go:linkname coverage_getCovPkgMap internal/coverage/cfile.getCovPkgMap
|
||||||
func runtime_coverage_getCovPkgMap() map[int]int {
|
func coverage_getCovPkgMap() map[int]int {
|
||||||
return covMeta.pkgMap
|
return covMeta.pkgMap
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ package testdeps
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"internal/coverage/cfile"
|
||||||
"internal/fuzz"
|
"internal/fuzz"
|
||||||
"internal/testlog"
|
"internal/testlog"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -26,6 +27,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Cover indicates whether coverage is enabled.
|
||||||
|
var Cover bool
|
||||||
|
|
||||||
// TestDeps is an implementation of the testing.testDeps interface,
|
// TestDeps is an implementation of the testing.testDeps interface,
|
||||||
// suitable for passing to [testing.MainStart].
|
// suitable for passing to [testing.MainStart].
|
||||||
type TestDeps struct{}
|
type TestDeps struct{}
|
||||||
|
|
@ -197,3 +201,30 @@ func (TestDeps) ResetCoverage() {
|
||||||
func (TestDeps) SnapshotCoverage() {
|
func (TestDeps) SnapshotCoverage() {
|
||||||
fuzz.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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,13 @@ var cover2 struct {
|
||||||
snapshotcov func() float64
|
snapshotcov func() float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// registerCover2 is injected in testmain.
|
// registerCover2 is invoked during "go test -cover" runs.
|
||||||
//go:linkname registerCover2
|
// It is used to record a 'tear down' function
|
||||||
|
|
||||||
// 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
|
|
||||||
// (to be called when the test is complete) and the coverage mode.
|
// (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) {
|
func registerCover2(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64) {
|
||||||
|
if mode == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
cover2.mode = mode
|
cover2.mode = mode
|
||||||
cover2.tearDown = tearDown
|
cover2.tearDown = tearDown
|
||||||
cover2.snapshotcov = snapcov
|
cover2.snapshotcov = snapcov
|
||||||
|
|
|
||||||
|
|
@ -1855,6 +1855,10 @@ func (f matchStringOnly) CheckCorpus([]any, []reflect.Type) error { return nil }
|
||||||
func (f matchStringOnly) ResetCoverage() {}
|
func (f matchStringOnly) ResetCoverage() {}
|
||||||
func (f matchStringOnly) SnapshotCoverage() {}
|
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.
|
// 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 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
|
// 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
|
CheckCorpus([]any, []reflect.Type) error
|
||||||
ResetCoverage()
|
ResetCoverage()
|
||||||
SnapshotCoverage()
|
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'.
|
// 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 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.
|
// It may change signature from release to release.
|
||||||
func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M {
|
func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M {
|
||||||
|
registerCover2(deps.InitRuntimeCoverage())
|
||||||
Init()
|
Init()
|
||||||
return &M{
|
return &M{
|
||||||
deps: deps,
|
deps: deps,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue