1// Copyright 2022 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package main 6 7import ( 8 "cmd/internal/cov" 9 "cmd/internal/pkgpattern" 10 "cmd/internal/telemetry/counter" 11 "flag" 12 "fmt" 13 "os" 14 "runtime" 15 "runtime/pprof" 16 "strings" 17) 18 19var verbflag = flag.Int("v", 0, "Verbose trace output level") 20var hflag = flag.Bool("h", false, "Panic on fatal errors (for stack trace)") 21var hwflag = flag.Bool("hw", false, "Panic on warnings (for stack trace)") 22var indirsflag = flag.String("i", "", "Input dirs to examine (comma separated)") 23var pkgpatflag = flag.String("pkg", "", "Restrict output to package(s) matching specified package pattern.") 24var cpuprofileflag = flag.String("cpuprofile", "", "Write CPU profile to specified file") 25var memprofileflag = flag.String("memprofile", "", "Write memory profile to specified file") 26var memprofilerateflag = flag.Int("memprofilerate", 0, "Set memprofile sampling rate to value") 27 28var matchpkg func(name string) bool 29 30var atExitFuncs []func() 31 32func atExit(f func()) { 33 atExitFuncs = append(atExitFuncs, f) 34} 35 36func Exit(code int) { 37 for i := len(atExitFuncs) - 1; i >= 0; i-- { 38 f := atExitFuncs[i] 39 atExitFuncs = atExitFuncs[:i] 40 f() 41 } 42 os.Exit(code) 43} 44 45func dbgtrace(vlevel int, s string, a ...interface{}) { 46 if *verbflag >= vlevel { 47 fmt.Printf(s, a...) 48 fmt.Printf("\n") 49 } 50} 51 52func warn(s string, a ...interface{}) { 53 fmt.Fprintf(os.Stderr, "warning: ") 54 fmt.Fprintf(os.Stderr, s, a...) 55 fmt.Fprintf(os.Stderr, "\n") 56 if *hwflag { 57 panic("unexpected warning") 58 } 59} 60 61func fatal(s string, a ...interface{}) { 62 fmt.Fprintf(os.Stderr, "error: ") 63 fmt.Fprintf(os.Stderr, s, a...) 64 fmt.Fprintf(os.Stderr, "\n") 65 if *hflag { 66 panic("fatal error") 67 } 68 Exit(1) 69} 70 71func usage(msg string) { 72 if len(msg) > 0 { 73 fmt.Fprintf(os.Stderr, "error: %s\n", msg) 74 } 75 fmt.Fprintf(os.Stderr, "usage: go tool covdata [command]\n") 76 fmt.Fprintf(os.Stderr, ` 77Commands are: 78 79textfmt convert coverage data to textual format 80percent output total percentage of statements covered 81pkglist output list of package import paths 82func output coverage profile information for each function 83merge merge data files together 84subtract subtract one set of data files from another set 85intersect generate intersection of two sets of data files 86debugdump dump data in human-readable format for debugging purposes 87`) 88 fmt.Fprintf(os.Stderr, "\nFor help on a specific subcommand, try:\n") 89 fmt.Fprintf(os.Stderr, "\ngo tool covdata <cmd> -help\n") 90 Exit(2) 91} 92 93type covOperation interface { 94 cov.CovDataVisitor 95 Setup() 96 Usage(string) 97} 98 99// Modes of operation. 100const ( 101 funcMode = "func" 102 mergeMode = "merge" 103 intersectMode = "intersect" 104 subtractMode = "subtract" 105 percentMode = "percent" 106 pkglistMode = "pkglist" 107 textfmtMode = "textfmt" 108 debugDumpMode = "debugdump" 109) 110 111func main() { 112 counter.Open() 113 114 // First argument should be mode/subcommand. 115 if len(os.Args) < 2 { 116 usage("missing command selector") 117 } 118 119 // Select mode 120 var op covOperation 121 cmd := os.Args[1] 122 switch cmd { 123 case mergeMode: 124 op = makeMergeOp() 125 case debugDumpMode: 126 op = makeDumpOp(debugDumpMode) 127 case textfmtMode: 128 op = makeDumpOp(textfmtMode) 129 case percentMode: 130 op = makeDumpOp(percentMode) 131 case funcMode: 132 op = makeDumpOp(funcMode) 133 case pkglistMode: 134 op = makeDumpOp(pkglistMode) 135 case subtractMode: 136 op = makeSubtractIntersectOp(subtractMode) 137 case intersectMode: 138 op = makeSubtractIntersectOp(intersectMode) 139 default: 140 usage(fmt.Sprintf("unknown command selector %q", cmd)) 141 } 142 143 // Edit out command selector, then parse flags. 144 os.Args = append(os.Args[:1], os.Args[2:]...) 145 flag.Usage = func() { 146 op.Usage("") 147 } 148 flag.Parse() 149 counter.Inc("covdata/invocations") 150 counter.CountFlags("covdata/flag:", *flag.CommandLine) 151 152 // Mode-independent flag setup 153 dbgtrace(1, "starting mode-independent setup") 154 if flag.NArg() != 0 { 155 op.Usage("unknown extra arguments") 156 } 157 if *pkgpatflag != "" { 158 pats := strings.Split(*pkgpatflag, ",") 159 matchers := []func(name string) bool{} 160 for _, p := range pats { 161 if p == "" { 162 continue 163 } 164 f := pkgpattern.MatchSimplePattern(p) 165 matchers = append(matchers, f) 166 } 167 matchpkg = func(name string) bool { 168 for _, f := range matchers { 169 if f(name) { 170 return true 171 } 172 } 173 return false 174 } 175 } 176 if *cpuprofileflag != "" { 177 f, err := os.Create(*cpuprofileflag) 178 if err != nil { 179 fatal("%v", err) 180 } 181 if err := pprof.StartCPUProfile(f); err != nil { 182 fatal("%v", err) 183 } 184 atExit(func() { 185 pprof.StopCPUProfile() 186 if err = f.Close(); err != nil { 187 fatal("error closing cpu profile: %v", err) 188 } 189 }) 190 } 191 if *memprofileflag != "" { 192 if *memprofilerateflag != 0 { 193 runtime.MemProfileRate = *memprofilerateflag 194 } 195 f, err := os.Create(*memprofileflag) 196 if err != nil { 197 fatal("%v", err) 198 } 199 atExit(func() { 200 runtime.GC() 201 const writeLegacyFormat = 1 202 if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil { 203 fatal("%v", err) 204 } 205 if err = f.Close(); err != nil { 206 fatal("error closing memory profile: %v", err) 207 } 208 }) 209 } else { 210 // Not doing memory profiling; disable it entirely. 211 runtime.MemProfileRate = 0 212 } 213 214 // Mode-dependent setup. 215 op.Setup() 216 217 // ... off and running now. 218 dbgtrace(1, "starting perform") 219 220 indirs := strings.Split(*indirsflag, ",") 221 vis := cov.CovDataVisitor(op) 222 var flags cov.CovDataReaderFlags 223 if *hflag { 224 flags |= cov.PanicOnError 225 } 226 if *hwflag { 227 flags |= cov.PanicOnWarning 228 } 229 reader := cov.MakeCovDataReader(vis, indirs, *verbflag, flags, matchpkg) 230 st := 0 231 if err := reader.Visit(); err != nil { 232 fmt.Fprintf(os.Stderr, "error: %v\n", err) 233 st = 1 234 } 235 dbgtrace(1, "leaving main") 236 Exit(st) 237} 238