1
2
3
4
5 package main
6
7
8
9
10
11
12 import (
13 "flag"
14 "fmt"
15 "internal/coverage"
16 "internal/coverage/calloc"
17 "internal/coverage/cformat"
18 "internal/coverage/cmerge"
19 "internal/coverage/decodecounter"
20 "internal/coverage/decodemeta"
21 "internal/coverage/pods"
22 "os"
23 "sort"
24 "strings"
25 )
26
27 var textfmtoutflag *string
28 var liveflag *bool
29
30 func makeDumpOp(cmd string) covOperation {
31 if cmd == textfmtMode || cmd == percentMode {
32 textfmtoutflag = flag.String("o", "", "Output text format to file")
33 }
34 if cmd == debugDumpMode {
35 liveflag = flag.Bool("live", false, "Select only live (executed) functions for dump output.")
36 }
37 d := &dstate{
38 cmd: cmd,
39 cm: &cmerge.Merger{},
40 }
41
42
43
44
45
46 if d.cmd == percentMode || d.cmd == funcMode || d.cmd == pkglistMode {
47 d.cm.SetModeMergePolicy(cmerge.ModeMergeRelaxed)
48 }
49 if d.cmd == pkglistMode {
50 d.pkgpaths = make(map[string]struct{})
51 }
52 return d
53 }
54
55
56
57
58
59
60 type dstate struct {
61
62 calloc.BatchCounterAlloc
63
64
65 cm *cmerge.Merger
66
67
68 format *cformat.Formatter
69
70
71
72
73 mm map[pkfunc]decodecounter.FuncPayload
74
75
76
77
78
79 pkm map[uint32]uint32
80
81
82
83
84 pkgpaths map[string]struct{}
85
86
87 pkgName string
88 pkgImportPath string
89
90
91 modulePath string
92
93
94 cmd string
95
96
97 textfmtoutf *os.File
98
99
100 totalStmts, coveredStmts int
101
102
103
104 preambleEmitted bool
105 }
106
107 func (d *dstate) Usage(msg string) {
108 if len(msg) > 0 {
109 fmt.Fprintf(os.Stderr, "error: %s\n", msg)
110 }
111 fmt.Fprintf(os.Stderr, "usage: go tool covdata %s -i=<directories>\n\n", d.cmd)
112 flag.PrintDefaults()
113 fmt.Fprintf(os.Stderr, "\nExamples:\n\n")
114 switch d.cmd {
115 case pkglistMode:
116 fmt.Fprintf(os.Stderr, " go tool covdata pkglist -i=dir1,dir2\n\n")
117 fmt.Fprintf(os.Stderr, " \treads coverage data files from dir1+dirs2\n")
118 fmt.Fprintf(os.Stderr, " \tand writes out a list of the import paths\n")
119 fmt.Fprintf(os.Stderr, " \tof all compiled packages.\n")
120 case textfmtMode:
121 fmt.Fprintf(os.Stderr, " go tool covdata textfmt -i=dir1,dir2 -o=out.txt\n\n")
122 fmt.Fprintf(os.Stderr, " \tmerges data from input directories dir1+dir2\n")
123 fmt.Fprintf(os.Stderr, " \tand emits text format into file 'out.txt'\n")
124 case percentMode:
125 fmt.Fprintf(os.Stderr, " go tool covdata percent -i=dir1,dir2\n\n")
126 fmt.Fprintf(os.Stderr, " \tmerges data from input directories dir1+dir2\n")
127 fmt.Fprintf(os.Stderr, " \tand emits percentage of statements covered\n\n")
128 case funcMode:
129 fmt.Fprintf(os.Stderr, " go tool covdata func -i=dir1,dir2\n\n")
130 fmt.Fprintf(os.Stderr, " \treads coverage data files from dir1+dirs2\n")
131 fmt.Fprintf(os.Stderr, " \tand writes out coverage profile data for\n")
132 fmt.Fprintf(os.Stderr, " \teach function.\n")
133 case debugDumpMode:
134 fmt.Fprintf(os.Stderr, " go tool covdata debugdump [flags] -i=dir1,dir2\n\n")
135 fmt.Fprintf(os.Stderr, " \treads coverage data from dir1+dir2 and dumps\n")
136 fmt.Fprintf(os.Stderr, " \tcontents in human-readable form to stdout, for\n")
137 fmt.Fprintf(os.Stderr, " \tdebugging purposes.\n")
138 default:
139 panic("unexpected")
140 }
141 Exit(2)
142 }
143
144
145
146 func (d *dstate) Setup() {
147 if *indirsflag == "" {
148 d.Usage("select input directories with '-i' option")
149 }
150 if d.cmd == textfmtMode || (d.cmd == percentMode && *textfmtoutflag != "") {
151 if *textfmtoutflag == "" {
152 d.Usage("select output file name with '-o' option")
153 }
154 var err error
155 d.textfmtoutf, err = os.Create(*textfmtoutflag)
156 if err != nil {
157 d.Usage(fmt.Sprintf("unable to open textfmt output file %q: %v", *textfmtoutflag, err))
158 }
159 }
160 if d.cmd == debugDumpMode {
161 fmt.Printf("/* WARNING: the format of this dump is not stable and is\n")
162 fmt.Printf(" * expected to change from one Go release to the next.\n")
163 fmt.Printf(" *\n")
164 fmt.Printf(" * produced by:\n")
165 args := append([]string{os.Args[0]}, debugDumpMode)
166 args = append(args, os.Args[1:]...)
167 fmt.Printf(" *\t%s\n", strings.Join(args, " "))
168 fmt.Printf(" */\n")
169 }
170 }
171
172 func (d *dstate) BeginPod(p pods.Pod) {
173 d.mm = make(map[pkfunc]decodecounter.FuncPayload)
174 }
175
176 func (d *dstate) EndPod(p pods.Pod) {
177 if d.cmd == debugDumpMode {
178 d.cm.ResetModeAndGranularity()
179 }
180 }
181
182 func (d *dstate) BeginCounterDataFile(cdf string, cdr *decodecounter.CounterDataReader, dirIdx int) {
183 dbgtrace(2, "visit counter data file %s dirIdx %d", cdf, dirIdx)
184 if d.cmd == debugDumpMode {
185 fmt.Printf("data file %s", cdf)
186 if cdr.Goos() != "" {
187 fmt.Printf(" GOOS=%s", cdr.Goos())
188 }
189 if cdr.Goarch() != "" {
190 fmt.Printf(" GOARCH=%s", cdr.Goarch())
191 }
192 if len(cdr.OsArgs()) != 0 {
193 fmt.Printf(" program args: %+v\n", cdr.OsArgs())
194 }
195 fmt.Printf("\n")
196 }
197 }
198
199 func (d *dstate) EndCounterDataFile(cdf string, cdr *decodecounter.CounterDataReader, dirIdx int) {
200 }
201
202 func (d *dstate) VisitFuncCounterData(data decodecounter.FuncPayload) {
203 if nf, ok := d.pkm[data.PkgIdx]; !ok || data.FuncIdx > nf {
204 warn("func payload inconsistency: id [p=%d,f=%d] nf=%d len(ctrs)=%d in VisitFuncCounterData, ignored", data.PkgIdx, data.FuncIdx, nf, len(data.Counters))
205 return
206 }
207 key := pkfunc{pk: data.PkgIdx, fcn: data.FuncIdx}
208 val, found := d.mm[key]
209
210 dbgtrace(5, "ctr visit pk=%d fid=%d found=%v len(val.ctrs)=%d len(data.ctrs)=%d", data.PkgIdx, data.FuncIdx, found, len(val.Counters), len(data.Counters))
211
212 if len(val.Counters) < len(data.Counters) {
213 t := val.Counters
214 val.Counters = d.AllocateCounters(len(data.Counters))
215 copy(val.Counters, t)
216 }
217 err, overflow := d.cm.MergeCounters(val.Counters, data.Counters)
218 if err != nil {
219 fatal("%v", err)
220 }
221 if overflow {
222 warn("uint32 overflow during counter merge")
223 }
224 d.mm[key] = val
225 }
226
227 func (d *dstate) EndCounters() {
228 }
229
230 func (d *dstate) VisitMetaDataFile(mdf string, mfr *decodemeta.CoverageMetaFileReader) {
231 newgran := mfr.CounterGranularity()
232 newmode := mfr.CounterMode()
233 if err := d.cm.SetModeAndGranularity(mdf, newmode, newgran); err != nil {
234 fatal("%v", err)
235 }
236 if d.cmd == debugDumpMode {
237 fmt.Printf("Cover mode: %s\n", newmode.String())
238 fmt.Printf("Cover granularity: %s\n", newgran.String())
239 }
240 if d.format == nil {
241 d.format = cformat.NewFormatter(mfr.CounterMode())
242 }
243
244
245
246
247
248 d.pkm = make(map[uint32]uint32)
249 np := uint32(mfr.NumPackages())
250 payload := []byte{}
251 for pkIdx := uint32(0); pkIdx < np; pkIdx++ {
252 var pd *decodemeta.CoverageMetaDataDecoder
253 var err error
254 pd, payload, err = mfr.GetPackageDecoder(pkIdx, payload)
255 if err != nil {
256 fatal("reading pkg %d from meta-file %s: %s", pkIdx, mdf, err)
257 }
258 d.pkm[pkIdx] = pd.NumFuncs()
259 }
260 }
261
262 func (d *dstate) BeginPackage(pd *decodemeta.CoverageMetaDataDecoder, pkgIdx uint32) {
263 d.preambleEmitted = false
264 d.pkgImportPath = pd.PackagePath()
265 d.pkgName = pd.PackageName()
266 d.modulePath = pd.ModulePath()
267 if d.cmd == pkglistMode {
268 d.pkgpaths[d.pkgImportPath] = struct{}{}
269 }
270 d.format.SetPackage(pd.PackagePath())
271 }
272
273 func (d *dstate) EndPackage(pd *decodemeta.CoverageMetaDataDecoder, pkgIdx uint32) {
274 }
275
276 func (d *dstate) VisitFunc(pkgIdx uint32, fnIdx uint32, fd *coverage.FuncDesc) {
277 var counters []uint32
278 key := pkfunc{pk: pkgIdx, fcn: fnIdx}
279 v, haveCounters := d.mm[key]
280
281 dbgtrace(5, "meta visit pk=%d fid=%d fname=%s file=%s found=%v len(val.ctrs)=%d", pkgIdx, fnIdx, fd.Funcname, fd.Srcfile, haveCounters, len(v.Counters))
282
283 suppressOutput := false
284 if haveCounters {
285 counters = v.Counters
286 } else if d.cmd == debugDumpMode && *liveflag {
287 suppressOutput = true
288 }
289
290 if d.cmd == debugDumpMode && !suppressOutput {
291 if !d.preambleEmitted {
292 fmt.Printf("\nPackage path: %s\n", d.pkgImportPath)
293 fmt.Printf("Package name: %s\n", d.pkgName)
294 fmt.Printf("Module path: %s\n", d.modulePath)
295 d.preambleEmitted = true
296 }
297 fmt.Printf("\nFunc: %s\n", fd.Funcname)
298 fmt.Printf("Srcfile: %s\n", fd.Srcfile)
299 fmt.Printf("Literal: %v\n", fd.Lit)
300 }
301 for i := 0; i < len(fd.Units); i++ {
302 u := fd.Units[i]
303 var count uint32
304 if counters != nil {
305 count = counters[i]
306 }
307 d.format.AddUnit(fd.Srcfile, fd.Funcname, fd.Lit, u, count)
308 if d.cmd == debugDumpMode && !suppressOutput {
309 fmt.Printf("%d: L%d:C%d -- L%d:C%d ",
310 i, u.StLine, u.StCol, u.EnLine, u.EnCol)
311 if u.Parent != 0 {
312 fmt.Printf("Parent:%d = %d\n", u.Parent, count)
313 } else {
314 fmt.Printf("NS=%d = %d\n", u.NxStmts, count)
315 }
316 }
317 d.totalStmts += int(u.NxStmts)
318 if count != 0 {
319 d.coveredStmts += int(u.NxStmts)
320 }
321 }
322 }
323
324 func (d *dstate) Finish() {
325
326 if d.format != nil {
327 if d.cmd == percentMode {
328 d.format.EmitPercent(os.Stdout, nil, "", false, false)
329 }
330 if d.cmd == funcMode {
331 d.format.EmitFuncs(os.Stdout)
332 }
333 if d.textfmtoutf != nil {
334 if err := d.format.EmitTextual(d.textfmtoutf); err != nil {
335 fatal("writing to %s: %v", *textfmtoutflag, err)
336 }
337 }
338 }
339 if d.textfmtoutf != nil {
340 if err := d.textfmtoutf.Close(); err != nil {
341 fatal("closing textfmt output file %s: %v", *textfmtoutflag, err)
342 }
343 }
344 if d.cmd == debugDumpMode {
345 fmt.Printf("totalStmts: %d coveredStmts: %d\n", d.totalStmts, d.coveredStmts)
346 }
347 if d.cmd == pkglistMode {
348 pkgs := make([]string, 0, len(d.pkgpaths))
349 for p := range d.pkgpaths {
350 pkgs = append(pkgs, p)
351 }
352 sort.Strings(pkgs)
353 for _, p := range pkgs {
354 fmt.Printf("%s\n", p)
355 }
356 }
357 }
358
View as plain text