1
2
3
4
5 package coverage
6
7 import (
8 "encoding/json"
9 "fmt"
10 "internal/coverage"
11 "internal/coverage/calloc"
12 "internal/coverage/cformat"
13 "internal/coverage/cmerge"
14 "internal/coverage/decodecounter"
15 "internal/coverage/decodemeta"
16 "internal/coverage/pods"
17 "io"
18 "os"
19 "path/filepath"
20 "runtime/internal/atomic"
21 "strings"
22 "unsafe"
23 )
24
25
26
27
28
29 func processCoverTestDir(dir string, cfile string, cm string, cpkg string) error {
30 return processCoverTestDirInternal(dir, cfile, cm, cpkg, os.Stdout)
31 }
32
33
34
35 func processCoverTestDirInternal(dir string, cfile string, cm string, cpkg string, w io.Writer) error {
36 cmode := coverage.ParseCounterMode(cm)
37 if cmode == coverage.CtrModeInvalid {
38 return fmt.Errorf("invalid counter mode %q", cm)
39 }
40
41
42 ml := getCovMetaList()
43 if len(ml) == 0 {
44
45
46
47 } else {
48 if err := emitMetaDataToDirectory(dir, ml); err != nil {
49 return err
50 }
51 if err := emitCounterDataToDirectory(dir); err != nil {
52 return err
53 }
54 }
55
56
57
58
59
60 podlist, err := pods.CollectPods([]string{dir}, false)
61 if err != nil {
62 return fmt.Errorf("reading from %s: %v", dir, err)
63 }
64
65
66 var tf *os.File
67 var tfClosed bool
68 if cfile != "" {
69 var err error
70 tf, err = os.Create(cfile)
71 if err != nil {
72 return fmt.Errorf("internal error: opening coverage data output file %q: %v", cfile, err)
73 }
74 defer func() {
75 if !tfClosed {
76 tfClosed = true
77 tf.Close()
78 }
79 }()
80 }
81
82
83 ts := &tstate{
84 cm: &cmerge.Merger{},
85 cf: cformat.NewFormatter(cmode),
86 cmode: cmode,
87 }
88
89
90
91
92 hashstring := fmt.Sprintf("%x", finalHash)
93 importpaths := make(map[string]struct{})
94 for _, p := range podlist {
95 if !strings.Contains(p.MetaFile, hashstring) {
96 continue
97 }
98 if err := ts.processPod(p, importpaths); err != nil {
99 return err
100 }
101 }
102
103 metafilespath := filepath.Join(dir, coverage.MetaFilesFileName)
104 if _, err := os.Stat(metafilespath); err == nil {
105 if err := ts.readAuxMetaFiles(metafilespath, importpaths); err != nil {
106 return err
107 }
108 }
109
110
111 if err := ts.cf.EmitPercent(w, cpkg, true, true); err != nil {
112 return err
113 }
114
115
116 if tf != nil {
117 if err := ts.cf.EmitTextual(tf); err != nil {
118 return err
119 }
120 tfClosed = true
121 if err := tf.Close(); err != nil {
122 return fmt.Errorf("closing %s: %v", cfile, err)
123 }
124 }
125
126 return nil
127 }
128
129 type tstate struct {
130 calloc.BatchCounterAlloc
131 cm *cmerge.Merger
132 cf *cformat.Formatter
133 cmode coverage.CounterMode
134 }
135
136
137 func (ts *tstate) processPod(p pods.Pod, importpaths map[string]struct{}) error {
138
139 f, err := os.Open(p.MetaFile)
140 if err != nil {
141 return fmt.Errorf("unable to open meta-data file %s: %v", p.MetaFile, err)
142 }
143 defer func() {
144 f.Close()
145 }()
146 var mfr *decodemeta.CoverageMetaFileReader
147 mfr, err = decodemeta.NewCoverageMetaFileReader(f, nil)
148 if err != nil {
149 return fmt.Errorf("error reading meta-data file %s: %v", p.MetaFile, err)
150 }
151 newmode := mfr.CounterMode()
152 if newmode != ts.cmode {
153 return fmt.Errorf("internal error: counter mode clash: %q from test harness, %q from data file %s", ts.cmode.String(), newmode.String(), p.MetaFile)
154 }
155 newgran := mfr.CounterGranularity()
156 if err := ts.cm.SetModeAndGranularity(p.MetaFile, cmode, newgran); err != nil {
157 return err
158 }
159
160
161 pmm := make(map[pkfunc][]uint32)
162
163
164 readcdf := func(cdf string) error {
165 cf, err := os.Open(cdf)
166 if err != nil {
167 return fmt.Errorf("opening counter data file %s: %s", cdf, err)
168 }
169 defer cf.Close()
170 var cdr *decodecounter.CounterDataReader
171 cdr, err = decodecounter.NewCounterDataReader(cdf, cf)
172 if err != nil {
173 return fmt.Errorf("reading counter data file %s: %s", cdf, err)
174 }
175 var data decodecounter.FuncPayload
176 for {
177 ok, err := cdr.NextFunc(&data)
178 if err != nil {
179 return fmt.Errorf("reading counter data file %s: %v", cdf, err)
180 }
181 if !ok {
182 break
183 }
184
185
186 key := pkfunc{pk: data.PkgIdx, fcn: data.FuncIdx}
187 if prev, found := pmm[key]; found {
188
189 if err, _ := ts.cm.MergeCounters(data.Counters, prev); err != nil {
190 return fmt.Errorf("processing counter data file %s: %v", cdf, err)
191 }
192 }
193 c := ts.AllocateCounters(len(data.Counters))
194 copy(c, data.Counters)
195 pmm[key] = c
196 }
197 return nil
198 }
199
200
201 for _, cdf := range p.CounterDataFiles {
202 if err := readcdf(cdf); err != nil {
203 return err
204 }
205 }
206
207
208 np := uint32(mfr.NumPackages())
209 payload := []byte{}
210 for pkIdx := uint32(0); pkIdx < np; pkIdx++ {
211 var pd *decodemeta.CoverageMetaDataDecoder
212 pd, payload, err = mfr.GetPackageDecoder(pkIdx, payload)
213 if err != nil {
214 return fmt.Errorf("reading pkg %d from meta-file %s: %s", pkIdx, p.MetaFile, err)
215 }
216 ts.cf.SetPackage(pd.PackagePath())
217 importpaths[pd.PackagePath()] = struct{}{}
218 var fd coverage.FuncDesc
219 nf := pd.NumFuncs()
220 for fnIdx := uint32(0); fnIdx < nf; fnIdx++ {
221 if err := pd.ReadFunc(fnIdx, &fd); err != nil {
222 return fmt.Errorf("reading meta-data file %s: %v",
223 p.MetaFile, err)
224 }
225 key := pkfunc{pk: pkIdx, fcn: fnIdx}
226 counters, haveCounters := pmm[key]
227 for i := 0; i < len(fd.Units); i++ {
228 u := fd.Units[i]
229
230
231 if u.Parent != 0 {
232 continue
233 }
234 count := uint32(0)
235 if haveCounters {
236 count = counters[i]
237 }
238 ts.cf.AddUnit(fd.Srcfile, fd.Funcname, fd.Lit, u, count)
239 }
240 }
241 }
242 return nil
243 }
244
245 type pkfunc struct {
246 pk, fcn uint32
247 }
248
249 func (ts *tstate) readAuxMetaFiles(metafiles string, importpaths map[string]struct{}) error {
250
251
252 var mfc coverage.MetaFileCollection
253 data, err := os.ReadFile(metafiles)
254 if err != nil {
255 return fmt.Errorf("error reading auxmetafiles file %q: %v", metafiles, err)
256 }
257 if err := json.Unmarshal(data, &mfc); err != nil {
258 return fmt.Errorf("error reading auxmetafiles file %q: %v", metafiles, err)
259 }
260
261
262
263
264
265
266 for i := range mfc.ImportPaths {
267 p := mfc.ImportPaths[i]
268 if _, ok := importpaths[p]; ok {
269 continue
270 }
271 var pod pods.Pod
272 pod.MetaFile = mfc.MetaFileFragments[i]
273 if err := ts.processPod(pod, importpaths); err != nil {
274 return err
275 }
276 }
277 return nil
278 }
279
280
281
282
283
284
285
286 func snapshot() float64 {
287 cl := getCovCounterList()
288 if len(cl) == 0 {
289
290 return 0.0
291 }
292
293 tot := uint64(0)
294 totExec := uint64(0)
295 for _, c := range cl {
296 sd := unsafe.Slice((*atomic.Uint32)(unsafe.Pointer(c.Counters)), c.Len)
297 tot += uint64(len(sd))
298 for i := 0; i < len(sd); i++ {
299
300 if sd[i].Load() == 0 {
301 continue
302 }
303
304 nCtrs := sd[i+coverage.NumCtrsOffset].Load()
305 cst := i + coverage.FirstCtrOffset
306
307 if cst+int(nCtrs) > len(sd) {
308 break
309 }
310 counters := sd[cst : cst+int(nCtrs)]
311 for i := range counters {
312 if counters[i].Load() != 0 {
313 totExec++
314 }
315 }
316 i += coverage.FirstCtrOffset + int(nCtrs) - 1
317 }
318 }
319 if tot == 0 {
320 return 0.0
321 }
322 return float64(totExec) / float64(tot)
323 }
324
View as plain text