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  }