1
2
3
4
5 package main
6
7 import (
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
19 var verbflag = flag.Int("v", 0, "Verbose trace output level")
20 var hflag = flag.Bool("h", false, "Panic on fatal errors (for stack trace)")
21 var hwflag = flag.Bool("hw", false, "Panic on warnings (for stack trace)")
22 var indirsflag = flag.String("i", "", "Input dirs to examine (comma separated)")
23 var pkgpatflag = flag.String("pkg", "", "Restrict output to package(s) matching specified package pattern.")
24 var cpuprofileflag = flag.String("cpuprofile", "", "Write CPU profile to specified file")
25 var memprofileflag = flag.String("memprofile", "", "Write memory profile to specified file")
26 var memprofilerateflag = flag.Int("memprofilerate", 0, "Set memprofile sampling rate to value")
27
28 var matchpkg func(name string) bool
29
30 var atExitFuncs []func()
31
32 func atExit(f func()) {
33 atExitFuncs = append(atExitFuncs, f)
34 }
35
36 func 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
45 func dbgtrace(vlevel int, s string, a ...interface{}) {
46 if *verbflag >= vlevel {
47 fmt.Printf(s, a...)
48 fmt.Printf("\n")
49 }
50 }
51
52 func 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
61 func 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
71 func 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, `
77 Commands are:
78
79 textfmt convert coverage data to textual format
80 percent output total percentage of statements covered
81 pkglist output list of package import paths
82 func output coverage profile information for each function
83 merge merge data files together
84 subtract subtract one set of data files from another set
85 intersect generate intersection of two sets of data files
86 debugdump 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
93 type covOperation interface {
94 cov.CovDataVisitor
95 Setup()
96 Usage(string)
97 }
98
99
100 const (
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
111 func main() {
112 counter.Open()
113
114
115 if len(os.Args) < 2 {
116 usage("missing command selector")
117 }
118
119
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
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
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
211 runtime.MemProfileRate = 0
212 }
213
214
215 op.Setup()
216
217
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
View as plain text