| // 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/counter" | |
| "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 ...any) { | |
| if *verbflag >= vlevel { | |
| fmt.Printf(s, a...) | |
| fmt.Printf("\n") | |
| } | |
| } | |
| func warn(s string, a ...any) { | |
| 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 ...any) { | |
| 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() { | |
| counter.Open() | |
| // 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() | |
| counter.Inc("covdata/invocations") | |
| counter.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) | |
| } |