github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/coverage/cover.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 // This package contains support routines for coverage "fixup" in the 8 // compiler, which happens when compiling a package whose source code 9 // has been run through "cmd/cover" to add instrumentation. The two 10 // important entry points are FixupVars (called prior to package init 11 // generation) and FixupInit (called following package init 12 // generation). 13 14 import ( 15 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 16 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 17 "github.com/bir3/gocompiler/src/cmd/compile/internal/typecheck" 18 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 19 "github.com/bir3/gocompiler/src/cmd/internal/objabi" 20 "github.com/bir3/gocompiler/src/internal/coverage" 21 "strconv" 22 "strings" 23 ) 24 25 // Names records state information collected in the first fixup 26 // phase so that it can be passed to the second fixup phase. 27 type Names struct { 28 MetaVar *ir.Name 29 PkgIdVar *ir.Name 30 InitFn *ir.Func 31 CounterMode coverage.CounterMode 32 CounterGran coverage.CounterGranularity 33 } 34 35 // FixupVars is the first of two entry points for coverage compiler 36 // fixup. It collects and returns the package ID and meta-data 37 // variables being used for this "-cover" build, along with the 38 // coverage counter mode and granularity. It also reclassifies selected 39 // variables (for example, tagging coverage counter variables with 40 // flags so that they can be handled properly downstream). 41 func FixupVars() Names { 42 metaVarName := base.Flag.Cfg.CoverageInfo.MetaVar 43 pkgIdVarName := base.Flag.Cfg.CoverageInfo.PkgIdVar 44 counterMode := base.Flag.Cfg.CoverageInfo.CounterMode 45 counterGran := base.Flag.Cfg.CoverageInfo.CounterGranularity 46 counterPrefix := base.Flag.Cfg.CoverageInfo.CounterPrefix 47 var metavar *ir.Name 48 var pkgidvar *ir.Name 49 50 ckTypSanity := func(nm *ir.Name, tag string) { 51 if nm.Type() == nil || nm.Type().HasPointers() { 52 base.Fatalf("unsuitable %s %q mentioned in coveragecfg, improper type '%v'", tag, nm.Sym().Name, nm.Type()) 53 } 54 } 55 56 for _, n := range typecheck.Target.Decls { 57 as, ok := n.(*ir.AssignStmt) 58 if !ok { 59 continue 60 } 61 nm, ok := as.X.(*ir.Name) 62 if !ok { 63 continue 64 } 65 s := nm.Sym() 66 switch s.Name { 67 case metaVarName: 68 metavar = nm 69 ckTypSanity(nm, "metavar") 70 nm.MarkReadonly() 71 continue 72 case pkgIdVarName: 73 pkgidvar = nm 74 ckTypSanity(nm, "pkgidvar") 75 nm.SetCoverageAuxVar(true) 76 s := nm.Linksym() 77 s.Type = objabi.SCOVERAGE_AUXVAR 78 continue 79 } 80 if strings.HasPrefix(s.Name, counterPrefix) { 81 ckTypSanity(nm, "countervar") 82 nm.SetCoverageCounter(true) 83 s := nm.Linksym() 84 s.Type = objabi.SCOVERAGE_COUNTER 85 } 86 } 87 cm := coverage.ParseCounterMode(counterMode) 88 if cm == coverage.CtrModeInvalid { 89 base.Fatalf("bad setting %q for covermode in coveragecfg:", 90 counterMode) 91 } 92 var cg coverage.CounterGranularity 93 switch counterGran { 94 case "perblock": 95 cg = coverage.CtrGranularityPerBlock 96 case "perfunc": 97 cg = coverage.CtrGranularityPerFunc 98 default: 99 base.Fatalf("bad setting %q for covergranularity in coveragecfg:", 100 counterGran) 101 } 102 103 return Names{ 104 MetaVar: metavar, 105 PkgIdVar: pkgidvar, 106 CounterMode: cm, 107 CounterGran: cg, 108 } 109 } 110 111 // FixupInit is the second main entry point for coverage compiler 112 // fixup. It adds calls to the pkg init function as appropriate to 113 // register coverage-related variables with the runtime. 114 func FixupInit(cnames Names) { 115 for _, n := range typecheck.Target.Decls { 116 if fn, ok := n.(*ir.Func); ok && ir.FuncName(fn) == "init" { 117 cnames.InitFn = fn 118 break 119 } 120 } 121 if cnames.InitFn == nil { 122 panic("unexpected (no init func for -cover build)") 123 } 124 125 hashv, len := metaHashAndLen() 126 if cnames.CounterMode != coverage.CtrModeTestMain { 127 registerMeta(cnames, hashv, len) 128 } 129 if base.Ctxt.Pkgpath == "main" { 130 addInitHookCall(cnames.InitFn, cnames.CounterMode) 131 } 132 } 133 134 func metaHashAndLen() ([16]byte, int) { 135 136 // Read meta-data hash from config entry. 137 mhash := base.Flag.Cfg.CoverageInfo.MetaHash 138 if len(mhash) != 32 { 139 base.Fatalf("unexpected: got metahash length %d want 32", len(mhash)) 140 } 141 var hv [16]byte 142 for i := 0; i < 16; i++ { 143 nib := string(mhash[i*2 : i*2+2]) 144 x, err := strconv.ParseInt(nib, 16, 32) 145 if err != nil { 146 base.Fatalf("metahash bad byte %q", nib) 147 } 148 hv[i] = byte(x) 149 } 150 151 // Return hash and meta-data len 152 return hv, base.Flag.Cfg.CoverageInfo.MetaLen 153 } 154 155 func registerMeta(cnames Names, hashv [16]byte, mdlen int) { 156 // Materialize expression for hash (an array literal) 157 pos := cnames.InitFn.Pos() 158 elist := make([]ir.Node, 0, 16) 159 for i := 0; i < 16; i++ { 160 elem := ir.NewInt(int64(hashv[i])) 161 elist = append(elist, elem) 162 } 163 ht := types.NewArray(types.Types[types.TUINT8], 16) 164 hashx := ir.NewCompLitExpr(pos, ir.OCOMPLIT, ht, elist) 165 166 // Materalize expression corresponding to address of the meta-data symbol. 167 mdax := typecheck.NodAddr(cnames.MetaVar) 168 mdauspx := typecheck.ConvNop(mdax, types.Types[types.TUNSAFEPTR]) 169 170 // Materialize expression for length. 171 lenx := ir.NewInt(int64(mdlen)) // untyped 172 173 // Generate a call to runtime.addCovMeta, e.g. 174 // 175 // pkgIdVar = runtime.addCovMeta(&sym, len, hash, pkgpath, pkid, cmode, cgran) 176 // 177 fn := typecheck.LookupRuntime("addCovMeta") 178 pkid := coverage.HardCodedPkgID(base.Ctxt.Pkgpath) 179 pkIdNode := ir.NewInt(int64(pkid)) 180 cmodeNode := ir.NewInt(int64(cnames.CounterMode)) 181 cgranNode := ir.NewInt(int64(cnames.CounterGran)) 182 pkPathNode := ir.NewString(base.Ctxt.Pkgpath) 183 callx := typecheck.Call(pos, fn, []ir.Node{mdauspx, lenx, hashx, 184 pkPathNode, pkIdNode, cmodeNode, cgranNode}, false) 185 assign := callx 186 if pkid == coverage.NotHardCoded { 187 assign = typecheck.Stmt(ir.NewAssignStmt(pos, cnames.PkgIdVar, callx)) 188 } 189 190 // Tack the call onto the start of our init function. We do this 191 // early in the init since it's possible that instrumented function 192 // bodies (with counter updates) might be inlined into init. 193 cnames.InitFn.Body.Prepend(assign) 194 } 195 196 // addInitHookCall generates a call to runtime/coverage.initHook() and 197 // inserts it into the package main init function, which will kick off 198 // the process for coverage data writing (emit meta data, and register 199 // an exit hook to emit counter data). 200 func addInitHookCall(initfn *ir.Func, cmode coverage.CounterMode) { 201 typecheck.InitCoverage() 202 pos := initfn.Pos() 203 istest := cmode == coverage.CtrModeTestMain 204 initf := typecheck.LookupCoverage("initHook") 205 istestNode := ir.NewBool(istest) 206 args := []ir.Node{istestNode} 207 callx := typecheck.Call(pos, initf, args, false) 208 initfn.Body.Append(callx) 209 }