| | |
| | |
| | |
| |
|
| | package cfile |
| |
|
| | import ( |
| | "encoding/json" |
| | "fmt" |
| | "internal/coverage" |
| | "internal/coverage/calloc" |
| | "internal/coverage/cformat" |
| | "internal/coverage/cmerge" |
| | "internal/coverage/decodecounter" |
| | "internal/coverage/decodemeta" |
| | "internal/coverage/pods" |
| | "internal/coverage/rtcov" |
| | "internal/runtime/atomic" |
| | "io" |
| | "os" |
| | "path/filepath" |
| | "strings" |
| | "unsafe" |
| | ) |
| |
|
| | |
| | |
| | |
| | |
| | func ProcessCoverTestDir(dir string, cfile string, cm string, cpkg string, w io.Writer, selpkgs []string) error { |
| | cmode := coverage.ParseCounterMode(cm) |
| | if cmode == coverage.CtrModeInvalid { |
| | return fmt.Errorf("invalid counter mode %q", cm) |
| | } |
| |
|
| | |
| | ml := rtcov.Meta.List |
| | if len(ml) == 0 { |
| | |
| | |
| | |
| | } else { |
| | if err := emitMetaDataToDirectory(dir, ml); err != nil { |
| | return err |
| | } |
| | if err := emitCounterDataToDirectory(dir); err != nil { |
| | return err |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | podlist, err := pods.CollectPods([]string{dir}, false) |
| | if err != nil { |
| | return fmt.Errorf("reading from %s: %v", dir, err) |
| | } |
| |
|
| | |
| | var tf *os.File |
| | var tfClosed bool |
| | if cfile != "" { |
| | var err error |
| | tf, err = os.Create(cfile) |
| | if err != nil { |
| | return fmt.Errorf("internal error: opening coverage data output file %q: %v", cfile, err) |
| | } |
| | defer func() { |
| | if !tfClosed { |
| | tfClosed = true |
| | tf.Close() |
| | } |
| | }() |
| | } |
| |
|
| | |
| | ts := &tstate{ |
| | cm: &cmerge.Merger{}, |
| | cf: cformat.NewFormatter(cmode), |
| | cmode: cmode, |
| | } |
| | |
| | |
| | |
| | |
| | hashstring := fmt.Sprintf("%x", finalHash) |
| | importpaths := make(map[string]struct{}) |
| | for _, p := range podlist { |
| | if !strings.Contains(p.MetaFile, hashstring) { |
| | continue |
| | } |
| | if err := ts.processPod(p, importpaths); err != nil { |
| | return err |
| | } |
| | } |
| |
|
| | metafilespath := filepath.Join(dir, coverage.MetaFilesFileName) |
| | if _, err := os.Stat(metafilespath); err == nil { |
| | if err := ts.readAuxMetaFiles(metafilespath, importpaths); err != nil { |
| | return err |
| | } |
| | } |
| |
|
| | |
| | if err := ts.cf.EmitPercent(w, selpkgs, cpkg, true, true); err != nil { |
| | return err |
| | } |
| |
|
| | |
| | if tf != nil { |
| | if err := ts.cf.EmitTextual(selpkgs, tf); err != nil { |
| | return err |
| | } |
| | tfClosed = true |
| | if err := tf.Close(); err != nil { |
| | return fmt.Errorf("closing %s: %v", cfile, err) |
| | } |
| | } |
| |
|
| | return nil |
| | } |
| |
|
| | type tstate struct { |
| | calloc.BatchCounterAlloc |
| | cm *cmerge.Merger |
| | cf *cformat.Formatter |
| | cmode coverage.CounterMode |
| | } |
| |
|
| | |
| | func (ts *tstate) processPod(p pods.Pod, importpaths map[string]struct{}) error { |
| | |
| | f, err := os.Open(p.MetaFile) |
| | if err != nil { |
| | return fmt.Errorf("unable to open meta-data file %s: %v", p.MetaFile, err) |
| | } |
| | defer func() { |
| | f.Close() |
| | }() |
| | var mfr *decodemeta.CoverageMetaFileReader |
| | mfr, err = decodemeta.NewCoverageMetaFileReader(f, nil) |
| | if err != nil { |
| | return fmt.Errorf("error reading meta-data file %s: %v", p.MetaFile, err) |
| | } |
| | newmode := mfr.CounterMode() |
| | if newmode != ts.cmode { |
| | return fmt.Errorf("internal error: counter mode clash: %q from test harness, %q from data file %s", ts.cmode.String(), newmode.String(), p.MetaFile) |
| | } |
| | newgran := mfr.CounterGranularity() |
| | if err := ts.cm.SetModeAndGranularity(p.MetaFile, cmode, newgran); err != nil { |
| | return err |
| | } |
| |
|
| | |
| | pmm := make(map[pkfunc][]uint32) |
| |
|
| | |
| | readcdf := func(cdf string) error { |
| | cf, err := os.Open(cdf) |
| | if err != nil { |
| | return fmt.Errorf("opening counter data file %s: %s", cdf, err) |
| | } |
| | defer cf.Close() |
| | var cdr *decodecounter.CounterDataReader |
| | cdr, err = decodecounter.NewCounterDataReader(cdf, cf) |
| | if err != nil { |
| | return fmt.Errorf("reading counter data file %s: %s", cdf, err) |
| | } |
| | var data decodecounter.FuncPayload |
| | for { |
| | ok, err := cdr.NextFunc(&data) |
| | if err != nil { |
| | return fmt.Errorf("reading counter data file %s: %v", cdf, err) |
| | } |
| | if !ok { |
| | break |
| | } |
| |
|
| | |
| | key := pkfunc{pk: data.PkgIdx, fcn: data.FuncIdx} |
| | if prev, found := pmm[key]; found { |
| | |
| | if err, _ := ts.cm.MergeCounters(data.Counters, prev); err != nil { |
| | return fmt.Errorf("processing counter data file %s: %v", cdf, err) |
| | } |
| | } |
| | c := ts.AllocateCounters(len(data.Counters)) |
| | copy(c, data.Counters) |
| | pmm[key] = c |
| | } |
| | return nil |
| | } |
| |
|
| | |
| | for _, cdf := range p.CounterDataFiles { |
| | if err := readcdf(cdf); err != nil { |
| | return err |
| | } |
| | } |
| |
|
| | |
| | np := uint32(mfr.NumPackages()) |
| | payload := []byte{} |
| | for pkIdx := uint32(0); pkIdx < np; pkIdx++ { |
| | var pd *decodemeta.CoverageMetaDataDecoder |
| | pd, payload, err = mfr.GetPackageDecoder(pkIdx, payload) |
| | if err != nil { |
| | return fmt.Errorf("reading pkg %d from meta-file %s: %s", pkIdx, p.MetaFile, err) |
| | } |
| | ts.cf.SetPackage(pd.PackagePath()) |
| | importpaths[pd.PackagePath()] = struct{}{} |
| | var fd coverage.FuncDesc |
| | nf := pd.NumFuncs() |
| | for fnIdx := uint32(0); fnIdx < nf; fnIdx++ { |
| | if err := pd.ReadFunc(fnIdx, &fd); err != nil { |
| | return fmt.Errorf("reading meta-data file %s: %v", |
| | p.MetaFile, err) |
| | } |
| | key := pkfunc{pk: pkIdx, fcn: fnIdx} |
| | counters, haveCounters := pmm[key] |
| | for i := 0; i < len(fd.Units); i++ { |
| | u := fd.Units[i] |
| | |
| | |
| | if u.Parent != 0 { |
| | continue |
| | } |
| | count := uint32(0) |
| | if haveCounters { |
| | count = counters[i] |
| | } |
| | ts.cf.AddUnit(fd.Srcfile, fd.Funcname, fd.Lit, u, count) |
| | } |
| | } |
| | } |
| | return nil |
| | } |
| |
|
| | type pkfunc struct { |
| | pk, fcn uint32 |
| | } |
| |
|
| | func (ts *tstate) readAuxMetaFiles(metafiles string, importpaths map[string]struct{}) error { |
| | |
| | |
| | var mfc coverage.MetaFileCollection |
| | data, err := os.ReadFile(metafiles) |
| | if err != nil { |
| | return fmt.Errorf("error reading auxmetafiles file %q: %v", metafiles, err) |
| | } |
| | if err := json.Unmarshal(data, &mfc); err != nil { |
| | return fmt.Errorf("error reading auxmetafiles file %q: %v", metafiles, err) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | for i := range mfc.ImportPaths { |
| | p := mfc.ImportPaths[i] |
| | if _, ok := importpaths[p]; ok { |
| | continue |
| | } |
| | var pod pods.Pod |
| | pod.MetaFile = mfc.MetaFileFragments[i] |
| | if err := ts.processPod(pod, importpaths); err != nil { |
| | return err |
| | } |
| | } |
| | return nil |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | func Snapshot() float64 { |
| | cl := getCovCounterList() |
| | if len(cl) == 0 { |
| | |
| | return 0.0 |
| | } |
| |
|
| | tot := uint64(0) |
| | totExec := uint64(0) |
| | for _, c := range cl { |
| | sd := unsafe.Slice((*atomic.Uint32)(unsafe.Pointer(c.Counters)), c.Len) |
| | tot += uint64(len(sd)) |
| | for i := 0; i < len(sd); i++ { |
| | |
| | if sd[i].Load() == 0 { |
| | continue |
| | } |
| | |
| | nCtrs := sd[i+coverage.NumCtrsOffset].Load() |
| | cst := i + coverage.FirstCtrOffset |
| |
|
| | if cst+int(nCtrs) > len(sd) { |
| | break |
| | } |
| | counters := sd[cst : cst+int(nCtrs)] |
| | for i := range counters { |
| | if counters[i].Load() != 0 { |
| | totExec++ |
| | } |
| | } |
| | i += coverage.FirstCtrOffset + int(nCtrs) - 1 |
| | } |
| | } |
| | if tot == 0 { |
| | return 0.0 |
| | } |
| | return float64(totExec) / float64(tot) |
| | } |
| |
|