github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/covdata/subtractintersect.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 covdata 6 7 // This file contains functions and apis to support the "subtract" and 8 // "intersect" subcommands of "go tool covdata". 9 10 import ( 11 "flag" 12 "fmt" 13 "os" 14 "strings" 15 16 "github.com/go-asm/go/coverage" 17 "github.com/go-asm/go/coverage/decodecounter" 18 "github.com/go-asm/go/coverage/decodemeta" 19 "github.com/go-asm/go/coverage/pods" 20 ) 21 22 // makeSubtractIntersectOp creates a subtract or intersect operation. 23 // 'mode' here must be either "subtract" or "intersect". 24 func makeSubtractIntersectOp(mode string) covOperation { 25 outdirflag = flag.String("o", "", "Output directory to write") 26 s := &sstate{ 27 mode: mode, 28 mm: newMetaMerge(), 29 inidx: -1, 30 } 31 return s 32 } 33 34 // sstate holds state needed to implement subtraction and intersection 35 // operations on code coverage data files. This type provides methods 36 // to implement the CovDataVisitor interface, and is designed to be 37 // used in concert with the CovDataReader utility, which abstracts 38 // away most of the grubby details of reading coverage data files. 39 type sstate struct { 40 mm *metaMerge 41 inidx int 42 mode string 43 // Used only for intersection; keyed by pkg/fn ID, it keeps track of 44 // just the set of functions for which we have data in the current 45 // input directory. 46 imm map[pkfunc]struct{} 47 } 48 49 func (s *sstate) Usage(msg string) { 50 if len(msg) > 0 { 51 fmt.Fprintf(os.Stderr, "error: %s\n", msg) 52 } 53 fmt.Fprintf(os.Stderr, "usage: go tool covdata %s -i=dir1,dir2 -o=<dir>\n\n", s.mode) 54 flag.PrintDefaults() 55 fmt.Fprintf(os.Stderr, "\nExamples:\n\n") 56 op := "from" 57 if s.mode == intersectMode { 58 op = "with" 59 } 60 fmt.Fprintf(os.Stderr, " go tool covdata %s -i=dir1,dir2 -o=outdir\n\n", s.mode) 61 fmt.Fprintf(os.Stderr, " \t%ss dir2 %s dir1, writing result\n", s.mode, op) 62 fmt.Fprintf(os.Stderr, " \tinto output dir outdir.\n") 63 os.Exit(2) 64 } 65 66 func (s *sstate) Setup() { 67 if *indirsflag == "" { 68 usage("select input directories with '-i' option") 69 } 70 indirs := strings.Split(*indirsflag, ",") 71 if s.mode == subtractMode && len(indirs) != 2 { 72 usage("supply exactly two input dirs for subtract operation") 73 } 74 if *outdirflag == "" { 75 usage("select output directory with '-o' option") 76 } 77 } 78 79 func (s *sstate) BeginPod(p pods.Pod) { 80 s.mm.beginPod() 81 } 82 83 func (s *sstate) EndPod(p pods.Pod) { 84 const pcombine = false 85 s.mm.endPod(pcombine) 86 } 87 88 func (s *sstate) EndCounters() { 89 if s.imm != nil { 90 s.pruneCounters() 91 } 92 } 93 94 // pruneCounters performs a function-level partial intersection using the 95 // current POD counter data (s.mm.pod.pmm) and the intersected data from 96 // PODs in previous dirs (s.imm). 97 func (s *sstate) pruneCounters() { 98 pkeys := make([]pkfunc, 0, len(s.mm.pod.pmm)) 99 for k := range s.mm.pod.pmm { 100 pkeys = append(pkeys, k) 101 } 102 // Remove anything from pmm not found in imm. We don't need to 103 // go the other way (removing things from imm not found in pmm) 104 // since we don't add anything to imm if there is no pmm entry. 105 for _, k := range pkeys { 106 if _, found := s.imm[k]; !found { 107 delete(s.mm.pod.pmm, k) 108 } 109 } 110 s.imm = nil 111 } 112 113 func (s *sstate) BeginCounterDataFile(cdf string, cdr *decodecounter.CounterDataReader, dirIdx int) { 114 dbgtrace(2, "visiting counter data file %s diridx %d", cdf, dirIdx) 115 if s.inidx != dirIdx { 116 if s.inidx > dirIdx { 117 // We're relying on having data files presented in 118 // the order they appear in the inputs (e.g. first all 119 // data files from input dir 0, then dir 1, etc). 120 panic("decreasing dir index, internal error") 121 } 122 if dirIdx == 0 { 123 // No need to keep track of the functions in the first 124 // directory, since that info will be replicated in 125 // s.mm.pod.pmm. 126 s.imm = nil 127 } else { 128 // We're now starting to visit the Nth directory, N != 0. 129 if s.mode == intersectMode { 130 if s.imm != nil { 131 s.pruneCounters() 132 } 133 s.imm = make(map[pkfunc]struct{}) 134 } 135 } 136 s.inidx = dirIdx 137 } 138 } 139 140 func (s *sstate) EndCounterDataFile(cdf string, cdr *decodecounter.CounterDataReader, dirIdx int) { 141 } 142 143 func (s *sstate) VisitFuncCounterData(data decodecounter.FuncPayload) { 144 key := pkfunc{pk: data.PkgIdx, fcn: data.FuncIdx} 145 146 if *verbflag >= 5 { 147 fmt.Printf("ctr visit fid=%d pk=%d inidx=%d data.Counters=%+v\n", data.FuncIdx, data.PkgIdx, s.inidx, data.Counters) 148 } 149 150 // If we're processing counter data from the initial (first) input 151 // directory, then just install it into the counter data map 152 // as usual. 153 if s.inidx == 0 { 154 s.mm.visitFuncCounterData(data) 155 return 156 } 157 158 // If we're looking at counter data from a dir other than 159 // the first, then perform the intersect/subtract. 160 if val, ok := s.mm.pod.pmm[key]; ok { 161 if s.mode == subtractMode { 162 for i := 0; i < len(data.Counters); i++ { 163 if data.Counters[i] != 0 { 164 val.Counters[i] = 0 165 } 166 } 167 } else if s.mode == intersectMode { 168 s.imm[key] = struct{}{} 169 for i := 0; i < len(data.Counters); i++ { 170 if data.Counters[i] == 0 { 171 val.Counters[i] = 0 172 } 173 } 174 } 175 } 176 } 177 178 func (s *sstate) VisitMetaDataFile(mdf string, mfr *decodemeta.CoverageMetaFileReader) { 179 if s.mode == intersectMode { 180 s.imm = make(map[pkfunc]struct{}) 181 } 182 s.mm.visitMetaDataFile(mdf, mfr) 183 } 184 185 func (s *sstate) BeginPackage(pd *decodemeta.CoverageMetaDataDecoder, pkgIdx uint32) { 186 s.mm.visitPackage(pd, pkgIdx, false) 187 } 188 189 func (s *sstate) EndPackage(pd *decodemeta.CoverageMetaDataDecoder, pkgIdx uint32) { 190 } 191 192 func (s *sstate) VisitFunc(pkgIdx uint32, fnIdx uint32, fd *coverage.FuncDesc) { 193 s.mm.visitFunc(pkgIdx, fnIdx, fd, s.mode, false) 194 } 195 196 func (s *sstate) Finish() { 197 }