github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/runtime/coverage/testsupport.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 coverage 6 7 import ( 8 "fmt" 9 "internal/coverage" 10 "internal/coverage/calloc" 11 "internal/coverage/cformat" 12 "internal/coverage/cmerge" 13 "internal/coverage/decodecounter" 14 "internal/coverage/decodemeta" 15 "internal/coverage/pods" 16 "io" 17 "os" 18 ) 19 20 // processCoverTestDir is called (via a linknamed reference) from 21 // testmain code when "go test -cover" is in effect. It is not 22 // intended to be used other than internally by the Go command's 23 // generated code. 24 func processCoverTestDir(dir string, cfile string, cm string, cpkg string) error { 25 return processCoverTestDirInternal(dir, cfile, cm, cpkg, os.Stdout) 26 } 27 28 // processCoverTestDirInternal is an io.Writer version of processCoverTestDir, 29 // exposed for unit testing. 30 func processCoverTestDirInternal(dir string, cfile string, cm string, cpkg string, w io.Writer) error { 31 cmode := coverage.ParseCounterMode(cm) 32 if cmode == coverage.CtrModeInvalid { 33 return fmt.Errorf("invalid counter mode %q", cm) 34 } 35 36 // Emit meta-data and counter data. 37 ml := getCovMetaList() 38 if len(ml) == 0 { 39 // This corresponds to the case where we have a package that 40 // contains test code but no functions (which is fine). In this 41 // case there is no need to emit anything. 42 } else { 43 if err := emitMetaDataToDirectory(dir, ml); err != nil { 44 return err 45 } 46 if err := emitCounterDataToDirectory(dir); err != nil { 47 return err 48 } 49 } 50 51 // Collect pods from test run. For the majority of cases we would 52 // expect to see a single pod here, but allow for multiple pods in 53 // case the test harness is doing extra work to collect data files 54 // from builds that it kicks off as part of the testing. 55 podlist, err := pods.CollectPods([]string{dir}, false) 56 if err != nil { 57 return fmt.Errorf("reading from %s: %v", dir, err) 58 } 59 60 // Open text output file if appropriate. 61 var tf *os.File 62 var tfClosed bool 63 if cfile != "" { 64 var err error 65 tf, err = os.Create(cfile) 66 if err != nil { 67 return fmt.Errorf("internal error: opening coverage data output file %q: %v", cfile, err) 68 } 69 defer func() { 70 if !tfClosed { 71 tfClosed = true 72 tf.Close() 73 } 74 }() 75 } 76 77 // Read/process the pods. 78 ts := &tstate{ 79 cm: &cmerge.Merger{}, 80 cf: cformat.NewFormatter(cmode), 81 cmode: cmode, 82 } 83 for _, p := range podlist { 84 if err := ts.processPod(p); err != nil { 85 return err 86 } 87 } 88 89 // Emit percent. 90 if err := ts.cf.EmitPercent(w, cpkg, true); err != nil { 91 return err 92 } 93 94 // Emit text output. 95 if tf != nil { 96 if err := ts.cf.EmitTextual(tf); err != nil { 97 return err 98 } 99 tfClosed = true 100 if err := tf.Close(); err != nil { 101 return fmt.Errorf("closing %s: %v", cfile, err) 102 } 103 } 104 105 return nil 106 } 107 108 type tstate struct { 109 calloc.BatchCounterAlloc 110 cm *cmerge.Merger 111 cf *cformat.Formatter 112 cmode coverage.CounterMode 113 } 114 115 // processPod reads coverage counter data for a specific pod. 116 func (ts *tstate) processPod(p pods.Pod) error { 117 // Open meta-data file 118 f, err := os.Open(p.MetaFile) 119 if err != nil { 120 return fmt.Errorf("unable to open meta-data file %s: %v", p.MetaFile, err) 121 } 122 defer func() { 123 f.Close() 124 }() 125 var mfr *decodemeta.CoverageMetaFileReader 126 mfr, err = decodemeta.NewCoverageMetaFileReader(f, nil) 127 if err != nil { 128 return fmt.Errorf("error reading meta-data file %s: %v", p.MetaFile, err) 129 } 130 newmode := mfr.CounterMode() 131 if newmode != ts.cmode { 132 return fmt.Errorf("internal error: counter mode clash: %q from test harness, %q from data file %s", ts.cmode.String(), newmode.String(), p.MetaFile) 133 } 134 newgran := mfr.CounterGranularity() 135 if err := ts.cm.SetModeAndGranularity(p.MetaFile, cmode, newgran); err != nil { 136 return err 137 } 138 139 // Read counter data files. 140 pmm := make(map[pkfunc][]uint32) 141 for _, cdf := range p.CounterDataFiles { 142 cf, err := os.Open(cdf) 143 if err != nil { 144 return fmt.Errorf("opening counter data file %s: %s", cdf, err) 145 } 146 var cdr *decodecounter.CounterDataReader 147 cdr, err = decodecounter.NewCounterDataReader(cdf, cf) 148 if err != nil { 149 return fmt.Errorf("reading counter data file %s: %s", cdf, err) 150 } 151 var data decodecounter.FuncPayload 152 for { 153 ok, err := cdr.NextFunc(&data) 154 if err != nil { 155 return fmt.Errorf("reading counter data file %s: %v", cdf, err) 156 } 157 if !ok { 158 break 159 } 160 161 // NB: sanity check on pkg and func IDs? 162 key := pkfunc{pk: data.PkgIdx, fcn: data.FuncIdx} 163 if prev, found := pmm[key]; found { 164 // Note: no overflow reporting here. 165 if err, _ := ts.cm.MergeCounters(data.Counters, prev); err != nil { 166 return fmt.Errorf("processing counter data file %s: %v", cdf, err) 167 } 168 } 169 c := ts.AllocateCounters(len(data.Counters)) 170 copy(c, data.Counters) 171 pmm[key] = c 172 } 173 } 174 175 // Visit meta-data file. 176 np := uint32(mfr.NumPackages()) 177 payload := []byte{} 178 for pkIdx := uint32(0); pkIdx < np; pkIdx++ { 179 var pd *decodemeta.CoverageMetaDataDecoder 180 pd, payload, err = mfr.GetPackageDecoder(pkIdx, payload) 181 if err != nil { 182 return fmt.Errorf("reading pkg %d from meta-file %s: %s", pkIdx, p.MetaFile, err) 183 } 184 ts.cf.SetPackage(pd.PackagePath()) 185 var fd coverage.FuncDesc 186 nf := pd.NumFuncs() 187 for fnIdx := uint32(0); fnIdx < nf; fnIdx++ { 188 if err := pd.ReadFunc(fnIdx, &fd); err != nil { 189 return fmt.Errorf("reading meta-data file %s: %v", 190 p.MetaFile, err) 191 } 192 key := pkfunc{pk: pkIdx, fcn: fnIdx} 193 counters, haveCounters := pmm[key] 194 for i := 0; i < len(fd.Units); i++ { 195 u := fd.Units[i] 196 // Skip units with non-zero parent (no way to represent 197 // these in the existing format). 198 if u.Parent != 0 { 199 continue 200 } 201 count := uint32(0) 202 if haveCounters { 203 count = counters[i] 204 } 205 ts.cf.AddUnit(fd.Srcfile, fd.Funcname, fd.Lit, u, count) 206 } 207 } 208 } 209 return nil 210 } 211 212 type pkfunc struct { 213 pk, fcn uint32 214 }