summaryrefslogtreecommitdiffstats
path: root/src/cmd/covdata/covdata.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/covdata/covdata.go')
-rw-r--r--src/cmd/covdata/covdata.go224
1 files changed, 224 insertions, 0 deletions
diff --git a/src/cmd/covdata/covdata.go b/src/cmd/covdata/covdata.go
new file mode 100644
index 0000000..95bc30d
--- /dev/null
+++ b/src/cmd/covdata/covdata.go
@@ -0,0 +1,224 @@
+// 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 (
+ "cmd/internal/cov"
+ "cmd/internal/pkgpattern"
+ "flag"
+ "fmt"
+ "os"
+ "runtime"
+ "runtime/pprof"
+ "strings"
+)
+
+var verbflag = flag.Int("v", 0, "Verbose trace output level")
+var hflag = flag.Bool("h", false, "Panic on fatal errors (for stack trace)")
+var hwflag = flag.Bool("hw", false, "Panic on warnings (for stack trace)")
+var indirsflag = flag.String("i", "", "Input dirs to examine (comma separated)")
+var pkgpatflag = flag.String("pkg", "", "Restrict output to package(s) matching specified package pattern.")
+var cpuprofileflag = flag.String("cpuprofile", "", "Write CPU profile to specified file")
+var memprofileflag = flag.String("memprofile", "", "Write memory profile to specified file")
+var memprofilerateflag = flag.Int("memprofilerate", 0, "Set memprofile sampling rate to value")
+
+var matchpkg func(name string) bool
+
+var atExitFuncs []func()
+
+func atExit(f func()) {
+ atExitFuncs = append(atExitFuncs, f)
+}
+
+func Exit(code int) {
+ for i := len(atExitFuncs) - 1; i >= 0; i-- {
+ f := atExitFuncs[i]
+ atExitFuncs = atExitFuncs[:i]
+ f()
+ }
+ os.Exit(code)
+}
+
+func dbgtrace(vlevel int, s string, a ...interface{}) {
+ if *verbflag >= vlevel {
+ fmt.Printf(s, a...)
+ fmt.Printf("\n")
+ }
+}
+
+func warn(s string, a ...interface{}) {
+ fmt.Fprintf(os.Stderr, "warning: ")
+ fmt.Fprintf(os.Stderr, s, a...)
+ fmt.Fprintf(os.Stderr, "\n")
+ if *hwflag {
+ panic("unexpected warning")
+ }
+}
+
+func fatal(s string, a ...interface{}) {
+ fmt.Fprintf(os.Stderr, "error: ")
+ fmt.Fprintf(os.Stderr, s, a...)
+ fmt.Fprintf(os.Stderr, "\n")
+ if *hflag {
+ panic("fatal error")
+ }
+ Exit(1)
+}
+
+func usage(msg string) {
+ if len(msg) > 0 {
+ fmt.Fprintf(os.Stderr, "error: %s\n", msg)
+ }
+ fmt.Fprintf(os.Stderr, "usage: go tool covdata [command]\n")
+ fmt.Fprintf(os.Stderr, `
+Commands are:
+
+textfmt convert coverage data to textual format
+percent output total percentage of statements covered
+pkglist output list of package import paths
+func output coverage profile information for each function
+merge merge data files together
+subtract subtract one set of data files from another set
+intersect generate intersection of two sets of data files
+debugdump dump data in human-readable format for debugging purposes
+`)
+ fmt.Fprintf(os.Stderr, "\nFor help on a specific subcommand, try:\n")
+ fmt.Fprintf(os.Stderr, "\ngo tool covdata <cmd> -help\n")
+ Exit(2)
+}
+
+type covOperation interface {
+ cov.CovDataVisitor
+ Setup()
+ Usage(string)
+}
+
+// Modes of operation.
+const (
+ funcMode = "func"
+ mergeMode = "merge"
+ intersectMode = "intersect"
+ subtractMode = "subtract"
+ percentMode = "percent"
+ pkglistMode = "pkglist"
+ textfmtMode = "textfmt"
+ debugDumpMode = "debugdump"
+)
+
+func main() {
+ // First argument should be mode/subcommand.
+ if len(os.Args) < 2 {
+ usage("missing command selector")
+ }
+
+ // Select mode
+ var op covOperation
+ cmd := os.Args[1]
+ switch cmd {
+ case mergeMode:
+ op = makeMergeOp()
+ case debugDumpMode:
+ op = makeDumpOp(debugDumpMode)
+ case textfmtMode:
+ op = makeDumpOp(textfmtMode)
+ case percentMode:
+ op = makeDumpOp(percentMode)
+ case funcMode:
+ op = makeDumpOp(funcMode)
+ case pkglistMode:
+ op = makeDumpOp(pkglistMode)
+ case subtractMode:
+ op = makeSubtractIntersectOp(subtractMode)
+ case intersectMode:
+ op = makeSubtractIntersectOp(intersectMode)
+ default:
+ usage(fmt.Sprintf("unknown command selector %q", cmd))
+ }
+
+ // Edit out command selector, then parse flags.
+ os.Args = append(os.Args[:1], os.Args[2:]...)
+ flag.Usage = func() {
+ op.Usage("")
+ }
+ flag.Parse()
+
+ // Mode-independent flag setup
+ dbgtrace(1, "starting mode-independent setup")
+ if flag.NArg() != 0 {
+ op.Usage("unknown extra arguments")
+ }
+ if *pkgpatflag != "" {
+ pats := strings.Split(*pkgpatflag, ",")
+ matchers := []func(name string) bool{}
+ for _, p := range pats {
+ if p == "" {
+ continue
+ }
+ f := pkgpattern.MatchSimplePattern(p)
+ matchers = append(matchers, f)
+ }
+ matchpkg = func(name string) bool {
+ for _, f := range matchers {
+ if f(name) {
+ return true
+ }
+ }
+ return false
+ }
+ }
+ if *cpuprofileflag != "" {
+ f, err := os.Create(*cpuprofileflag)
+ if err != nil {
+ fatal("%v", err)
+ }
+ if err := pprof.StartCPUProfile(f); err != nil {
+ fatal("%v", err)
+ }
+ atExit(pprof.StopCPUProfile)
+ }
+ if *memprofileflag != "" {
+ if *memprofilerateflag != 0 {
+ runtime.MemProfileRate = *memprofilerateflag
+ }
+ f, err := os.Create(*memprofileflag)
+ if err != nil {
+ fatal("%v", err)
+ }
+ atExit(func() {
+ runtime.GC()
+ const writeLegacyFormat = 1
+ if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil {
+ fatal("%v", err)
+ }
+ })
+ } else {
+ // Not doing memory profiling; disable it entirely.
+ runtime.MemProfileRate = 0
+ }
+
+ // Mode-dependent setup.
+ op.Setup()
+
+ // ... off and running now.
+ dbgtrace(1, "starting perform")
+
+ indirs := strings.Split(*indirsflag, ",")
+ vis := cov.CovDataVisitor(op)
+ var flags cov.CovDataReaderFlags
+ if *hflag {
+ flags |= cov.PanicOnError
+ }
+ if *hwflag {
+ flags |= cov.PanicOnWarning
+ }
+ reader := cov.MakeCovDataReader(vis, indirs, *verbflag, flags, matchpkg)
+ st := 0
+ if err := reader.Visit(); err != nil {
+ fmt.Fprintf(os.Stderr, "error: %v\n", err)
+ st = 1
+ }
+ dbgtrace(1, "leaving main")
+ Exit(st)
+}