github.com/bir3/gocompiler@v0.9.2202/src/cmd/internal/cov/readcovdata.go (about) 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 5 package cov 6 7 import ( 8 "github.com/bir3/gocompiler/src/cmd/internal/bio" 9 "fmt" 10 "github.com/bir3/gocompiler/src/internal/coverage" 11 "github.com/bir3/gocompiler/src/internal/coverage/decodecounter" 12 "github.com/bir3/gocompiler/src/internal/coverage/decodemeta" 13 "github.com/bir3/gocompiler/src/internal/coverage/pods" 14 "io" 15 "os" 16 ) 17 18 // CovDataReader is a general-purpose helper/visitor object for 19 // reading coverage data files in a structured way. Clients create a 20 // CovDataReader to process a given collection of coverage data file 21 // directories, then pass in a visitor object with methods that get 22 // invoked at various important points. CovDataReader is intended 23 // to facilitate common coverage data file operations such as 24 // merging or intersecting data files, analyzing data files, or 25 // dumping data files. 26 type CovDataReader struct { 27 vis CovDataVisitor 28 indirs []string 29 matchpkg func(name string) bool 30 flags CovDataReaderFlags 31 err error 32 verbosityLevel int 33 } 34 35 // MakeCovDataReader creates a CovDataReader object to process the 36 // given set of input directories. Here 'vis' is a visitor object 37 // providing methods to be invoked as we walk through the data, 38 // 'indirs' is the set of coverage data directories to examine, 39 // 'verbosityLevel' controls the level of debugging trace messages 40 // (zero for off, higher for more output), 'flags' stores flags that 41 // indicate what to do if errors are detected, and 'matchpkg' is a 42 // caller-provided function that can be used to select specific 43 // packages by name (if nil, then all packages are included). 44 func MakeCovDataReader(vis CovDataVisitor, indirs []string, verbosityLevel int, flags CovDataReaderFlags, matchpkg func(name string) bool) *CovDataReader { 45 return &CovDataReader{ 46 vis: vis, 47 indirs: indirs, 48 matchpkg: matchpkg, 49 verbosityLevel: verbosityLevel, 50 flags: flags, 51 } 52 } 53 54 // CovDataVisitor defines hooks for clients of CovDataReader. When the 55 // coverage data reader makes its way through a coverage meta-data 56 // file and counter data files, it will invoke the methods below to 57 // hand off info to the client. The normal sequence of expected 58 // visitor method invocations is: 59 // 60 // for each pod P { 61 // BeginPod(p) 62 // let MF be the meta-data file for P 63 // VisitMetaDataFile(MF) 64 // for each counter data file D in P { 65 // BeginCounterDataFile(D) 66 // for each live function F in D { 67 // VisitFuncCounterData(F) 68 // } 69 // EndCounterDataFile(D) 70 // } 71 // EndCounters(MF) 72 // for each package PK in MF { 73 // BeginPackage(PK) 74 // if <PK matched according to package pattern and/or modpath> { 75 // for each function PF in PK { 76 // VisitFunc(PF) 77 // } 78 // } 79 // EndPackage(PK) 80 // } 81 // EndPod(p) 82 // } 83 // Finish() 84 85 type CovDataVisitor interface { 86 // Invoked at the start and end of a given pod (a pod here is a 87 // specific coverage meta-data files with the counter data files 88 // that correspond to it). 89 BeginPod(p pods.Pod) 90 EndPod(p pods.Pod) 91 92 // Invoked when the reader is starting to examine the meta-data 93 // file for a pod. Here 'mdf' is the path of the file, and 'mfr' 94 // is an open meta-data reader. 95 VisitMetaDataFile(mdf string, mfr *decodemeta.CoverageMetaFileReader) 96 97 // Invoked when the reader processes a counter data file, first 98 // the 'begin' method at the start, then the 'end' method when 99 // we're done with the file. 100 BeginCounterDataFile(cdf string, cdr *decodecounter.CounterDataReader, dirIdx int) 101 EndCounterDataFile(cdf string, cdr *decodecounter.CounterDataReader, dirIdx int) 102 103 // Invoked once for each live function in the counter data file. 104 VisitFuncCounterData(payload decodecounter.FuncPayload) 105 106 // Invoked when we've finished processing the counter files in a 107 // POD (e.g. no more calls to VisitFuncCounterData). 108 EndCounters() 109 110 // Invoked for each package in the meta-data file for the pod, 111 // first the 'begin' method when processing of the package starts, 112 // then the 'end' method when we're done 113 BeginPackage(pd *decodemeta.CoverageMetaDataDecoder, pkgIdx uint32) 114 EndPackage(pd *decodemeta.CoverageMetaDataDecoder, pkgIdx uint32) 115 116 // Invoked for each function the package being visited. 117 VisitFunc(pkgIdx uint32, fnIdx uint32, fd *coverage.FuncDesc) 118 119 // Invoked when all counter + meta-data file processing is complete. 120 Finish() 121 } 122 123 type CovDataReaderFlags uint32 124 125 const ( 126 CovDataReaderNoFlags CovDataReaderFlags = 0 127 PanicOnError = 1 << iota 128 PanicOnWarning 129 ) 130 131 func (r *CovDataReader) Visit() error { 132 podlist, err := pods.CollectPods(r.indirs, false) 133 if err != nil { 134 return fmt.Errorf("reading inputs: %v", err) 135 } 136 if len(podlist) == 0 { 137 r.warn("no applicable files found in input directories") 138 } 139 for _, p := range podlist { 140 if err := r.visitPod(p); err != nil { 141 return err 142 } 143 } 144 r.vis.Finish() 145 return nil 146 } 147 148 func (r *CovDataReader) verb(vlevel int, s string, a ...interface{}) { 149 if r.verbosityLevel >= vlevel { 150 fmt.Fprintf(os.Stderr, s, a...) 151 fmt.Fprintf(os.Stderr, "\n") 152 } 153 } 154 155 func (r *CovDataReader) warn(s string, a ...interface{}) { 156 fmt.Fprintf(os.Stderr, "warning: ") 157 fmt.Fprintf(os.Stderr, s, a...) 158 fmt.Fprintf(os.Stderr, "\n") 159 if (r.flags & PanicOnWarning) != 0 { 160 panic("unexpected warning") 161 } 162 } 163 164 func (r *CovDataReader) fatal(s string, a ...interface{}) error { 165 if r.err != nil { 166 return nil 167 } 168 errstr := "error: " + fmt.Sprintf(s, a...) + "\n" 169 if (r.flags & PanicOnError) != 0 { 170 fmt.Fprintf(os.Stderr, "%s", errstr) 171 panic("fatal error") 172 } 173 r.err = fmt.Errorf("%s", errstr) 174 return r.err 175 } 176 177 // visitPod examines a coverage data 'pod', that is, a meta-data file and 178 // zero or more counter data files that refer to that meta-data file. 179 func (r *CovDataReader) visitPod(p pods.Pod) error { 180 r.verb(1, "visiting pod: metafile %s with %d counter files", 181 p.MetaFile, len(p.CounterDataFiles)) 182 r.vis.BeginPod(p) 183 184 // Open meta-file 185 f, err := os.Open(p.MetaFile) 186 if err != nil { 187 return r.fatal("unable to open meta-file %s", p.MetaFile) 188 } 189 defer f.Close() 190 br := bio.NewReader(f) 191 fi, err := f.Stat() 192 if err != nil { 193 return r.fatal("unable to stat metafile %s: %v", p.MetaFile, err) 194 } 195 fileView := br.SliceRO(uint64(fi.Size())) 196 br.MustSeek(0, io.SeekStart) 197 198 r.verb(1, "fileView for pod is length %d", len(fileView)) 199 200 var mfr *decodemeta.CoverageMetaFileReader 201 mfr, err = decodemeta.NewCoverageMetaFileReader(f, fileView) 202 if err != nil { 203 return r.fatal("decoding meta-file %s: %s", p.MetaFile, err) 204 } 205 r.vis.VisitMetaDataFile(p.MetaFile, mfr) 206 207 // Read counter data files. 208 for k, cdf := range p.CounterDataFiles { 209 cf, err := os.Open(cdf) 210 if err != nil { 211 return r.fatal("opening counter data file %s: %s", cdf, err) 212 } 213 defer func(f *os.File) { 214 f.Close() 215 }(cf) 216 var mr *MReader 217 mr, err = NewMreader(cf) 218 if err != nil { 219 return r.fatal("creating reader for counter data file %s: %s", cdf, err) 220 } 221 var cdr *decodecounter.CounterDataReader 222 cdr, err = decodecounter.NewCounterDataReader(cdf, mr) 223 if err != nil { 224 return r.fatal("reading counter data file %s: %s", cdf, err) 225 } 226 r.vis.BeginCounterDataFile(cdf, cdr, p.Origins[k]) 227 var data decodecounter.FuncPayload 228 for { 229 ok, err := cdr.NextFunc(&data) 230 if err != nil { 231 return r.fatal("reading counter data file %s: %v", cdf, err) 232 } 233 if !ok { 234 break 235 } 236 r.vis.VisitFuncCounterData(data) 237 } 238 r.vis.EndCounterDataFile(cdf, cdr, p.Origins[k]) 239 } 240 r.vis.EndCounters() 241 242 // NB: packages in the meta-file will be in dependency order (basically 243 // the order in which init files execute). Do we want an additional sort 244 // pass here, say by packagepath? 245 np := uint32(mfr.NumPackages()) 246 payload := []byte{} 247 for pkIdx := uint32(0); pkIdx < np; pkIdx++ { 248 var pd *decodemeta.CoverageMetaDataDecoder 249 pd, payload, err = mfr.GetPackageDecoder(pkIdx, payload) 250 if err != nil { 251 return r.fatal("reading pkg %d from meta-file %s: %s", pkIdx, p.MetaFile, err) 252 } 253 r.processPackage(p.MetaFile, pd, pkIdx) 254 } 255 r.vis.EndPod(p) 256 257 return nil 258 } 259 260 func (r *CovDataReader) processPackage(mfname string, pd *decodemeta.CoverageMetaDataDecoder, pkgIdx uint32) error { 261 if r.matchpkg != nil { 262 if !r.matchpkg(pd.PackagePath()) { 263 return nil 264 } 265 } 266 r.vis.BeginPackage(pd, pkgIdx) 267 nf := pd.NumFuncs() 268 var fd coverage.FuncDesc 269 for fidx := uint32(0); fidx < nf; fidx++ { 270 if err := pd.ReadFunc(fidx, &fd); err != nil { 271 return r.fatal("reading meta-data file %s: %v", mfname, err) 272 } 273 r.vis.VisitFunc(pkgIdx, fidx, &fd) 274 } 275 r.vis.EndPackage(pd, pkgIdx) 276 return nil 277 }