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