summaryrefslogtreecommitdiffstats
path: root/src/cmd/covdata/subtractintersect.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
commitccd992355df7192993c666236047820244914598 (patch)
treef00fea65147227b7743083c6148396f74cd66935 /src/cmd/covdata/subtractintersect.go
parentInitial commit. (diff)
downloadgolang-1.21-ccd992355df7192993c666236047820244914598.tar.xz
golang-1.21-ccd992355df7192993c666236047820244914598.zip
Adding upstream version 1.21.8.upstream/1.21.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/covdata/subtractintersect.go')
-rw-r--r--src/cmd/covdata/subtractintersect.go196
1 files changed, 196 insertions, 0 deletions
diff --git a/src/cmd/covdata/subtractintersect.go b/src/cmd/covdata/subtractintersect.go
new file mode 100644
index 0000000..5d71e3d
--- /dev/null
+++ b/src/cmd/covdata/subtractintersect.go
@@ -0,0 +1,196 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// This file contains functions and apis to support the "subtract" and
+// "intersect" subcommands of "go tool covdata".
+
+import (
+ "flag"
+ "fmt"
+ "internal/coverage"
+ "internal/coverage/decodecounter"
+ "internal/coverage/decodemeta"
+ "internal/coverage/pods"
+ "os"
+ "strings"
+)
+
+// makeSubtractIntersectOp creates a subtract or intersect operation.
+// 'mode' here must be either "subtract" or "intersect".
+func makeSubtractIntersectOp(mode string) covOperation {
+ outdirflag = flag.String("o", "", "Output directory to write")
+ s := &sstate{
+ mode: mode,
+ mm: newMetaMerge(),
+ inidx: -1,
+ }
+ return s
+}
+
+// sstate holds state needed to implement subtraction and intersection
+// operations on code coverage data files. This type provides methods
+// to implement the CovDataVisitor interface, and is designed to be
+// used in concert with the CovDataReader utility, which abstracts
+// away most of the grubby details of reading coverage data files.
+type sstate struct {
+ mm *metaMerge
+ inidx int
+ mode string
+ // Used only for intersection; keyed by pkg/fn ID, it keeps track of
+ // just the set of functions for which we have data in the current
+ // input directory.
+ imm map[pkfunc]struct{}
+}
+
+func (s *sstate) Usage(msg string) {
+ if len(msg) > 0 {
+ fmt.Fprintf(os.Stderr, "error: %s\n", msg)
+ }
+ fmt.Fprintf(os.Stderr, "usage: go tool covdata %s -i=dir1,dir2 -o=<dir>\n\n", s.mode)
+ flag.PrintDefaults()
+ fmt.Fprintf(os.Stderr, "\nExamples:\n\n")
+ op := "from"
+ if s.mode == intersectMode {
+ op = "with"
+ }
+ fmt.Fprintf(os.Stderr, " go tool covdata %s -i=dir1,dir2 -o=outdir\n\n", s.mode)
+ fmt.Fprintf(os.Stderr, " \t%ss dir2 %s dir1, writing result\n", s.mode, op)
+ fmt.Fprintf(os.Stderr, " \tinto output dir outdir.\n")
+ os.Exit(2)
+}
+
+func (s *sstate) Setup() {
+ if *indirsflag == "" {
+ usage("select input directories with '-i' option")
+ }
+ indirs := strings.Split(*indirsflag, ",")
+ if s.mode == subtractMode && len(indirs) != 2 {
+ usage("supply exactly two input dirs for subtract operation")
+ }
+ if *outdirflag == "" {
+ usage("select output directory with '-o' option")
+ }
+}
+
+func (s *sstate) BeginPod(p pods.Pod) {
+ s.mm.beginPod()
+}
+
+func (s *sstate) EndPod(p pods.Pod) {
+ const pcombine = false
+ s.mm.endPod(pcombine)
+}
+
+func (s *sstate) EndCounters() {
+ if s.imm != nil {
+ s.pruneCounters()
+ }
+}
+
+// pruneCounters performs a function-level partial intersection using the
+// current POD counter data (s.mm.pod.pmm) and the intersected data from
+// PODs in previous dirs (s.imm).
+func (s *sstate) pruneCounters() {
+ pkeys := make([]pkfunc, 0, len(s.mm.pod.pmm))
+ for k := range s.mm.pod.pmm {
+ pkeys = append(pkeys, k)
+ }
+ // Remove anything from pmm not found in imm. We don't need to
+ // go the other way (removing things from imm not found in pmm)
+ // since we don't add anything to imm if there is no pmm entry.
+ for _, k := range pkeys {
+ if _, found := s.imm[k]; !found {
+ delete(s.mm.pod.pmm, k)
+ }
+ }
+ s.imm = nil
+}
+
+func (s *sstate) BeginCounterDataFile(cdf string, cdr *decodecounter.CounterDataReader, dirIdx int) {
+ dbgtrace(2, "visiting counter data file %s diridx %d", cdf, dirIdx)
+ if s.inidx != dirIdx {
+ if s.inidx > dirIdx {
+ // We're relying on having data files presented in
+ // the order they appear in the inputs (e.g. first all
+ // data files from input dir 0, then dir 1, etc).
+ panic("decreasing dir index, internal error")
+ }
+ if dirIdx == 0 {
+ // No need to keep track of the functions in the first
+ // directory, since that info will be replicated in
+ // s.mm.pod.pmm.
+ s.imm = nil
+ } else {
+ // We're now starting to visit the Nth directory, N != 0.
+ if s.mode == intersectMode {
+ if s.imm != nil {
+ s.pruneCounters()
+ }
+ s.imm = make(map[pkfunc]struct{})
+ }
+ }
+ s.inidx = dirIdx
+ }
+}
+
+func (s *sstate) EndCounterDataFile(cdf string, cdr *decodecounter.CounterDataReader, dirIdx int) {
+}
+
+func (s *sstate) VisitFuncCounterData(data decodecounter.FuncPayload) {
+ key := pkfunc{pk: data.PkgIdx, fcn: data.FuncIdx}
+
+ if *verbflag >= 5 {
+ fmt.Printf("ctr visit fid=%d pk=%d inidx=%d data.Counters=%+v\n", data.FuncIdx, data.PkgIdx, s.inidx, data.Counters)
+ }
+
+ // If we're processing counter data from the initial (first) input
+ // directory, then just install it into the counter data map
+ // as usual.
+ if s.inidx == 0 {
+ s.mm.visitFuncCounterData(data)
+ return
+ }
+
+ // If we're looking at counter data from a dir other than
+ // the first, then perform the intersect/subtract.
+ if val, ok := s.mm.pod.pmm[key]; ok {
+ if s.mode == subtractMode {
+ for i := 0; i < len(data.Counters); i++ {
+ if data.Counters[i] != 0 {
+ val.Counters[i] = 0
+ }
+ }
+ } else if s.mode == intersectMode {
+ s.imm[key] = struct{}{}
+ for i := 0; i < len(data.Counters); i++ {
+ if data.Counters[i] == 0 {
+ val.Counters[i] = 0
+ }
+ }
+ }
+ }
+}
+
+func (s *sstate) VisitMetaDataFile(mdf string, mfr *decodemeta.CoverageMetaFileReader) {
+ if s.mode == intersectMode {
+ s.imm = make(map[pkfunc]struct{})
+ }
+ s.mm.visitMetaDataFile(mdf, mfr)
+}
+
+func (s *sstate) BeginPackage(pd *decodemeta.CoverageMetaDataDecoder, pkgIdx uint32) {
+ s.mm.visitPackage(pd, pkgIdx, false)
+}
+
+func (s *sstate) EndPackage(pd *decodemeta.CoverageMetaDataDecoder, pkgIdx uint32) {
+}
+
+func (s *sstate) VisitFunc(pkgIdx uint32, fnIdx uint32, fd *coverage.FuncDesc) {
+ s.mm.visitFunc(pkgIdx, fnIdx, fd, s.mode, false)
+}
+
+func (s *sstate) Finish() {
+}