mirror of https://go.googlesource.com/go
238 lines
5.7 KiB
Go
238 lines
5.7 KiB
Go
// 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"
|
|
"cmd/internal/telemetry"
|
|
"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() {
|
|
telemetry.Start()
|
|
|
|
// 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()
|
|
telemetry.Inc("covdata/invocations")
|
|
telemetry.CountFlags("covdata/flag:", *flag.CommandLine)
|
|
|
|
// 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(func() {
|
|
pprof.StopCPUProfile()
|
|
if err = f.Close(); err != nil {
|
|
fatal("error closing cpu profile: %v", err)
|
|
}
|
|
})
|
|
}
|
|
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)
|
|
}
|
|
if err = f.Close(); err != nil {
|
|
fatal("error closing memory profile: %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)
|
|
}
|