|  | // 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) | 
|  | } |