github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/runtime/coverage/emit.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  import (
     8  	"crypto/md5"
     9  	"fmt"
    10  	"internal/coverage"
    11  	"internal/coverage/encodecounter"
    12  	"internal/coverage/encodemeta"
    13  	"internal/coverage/rtcov"
    14  	"io"
    15  	"os"
    16  	"path/filepath"
    17  	"reflect"
    18  	"runtime"
    19  	"sync/atomic"
    20  	"time"
    21  	"unsafe"
    22  )
    23  
    24  // This file contains functions that support the writing of data files
    25  // emitted at the end of code coverage testing runs, from instrumented
    26  // executables.
    27  
    28  // getCovMetaList returns a list of meta-data blobs registered
    29  // for the currently executing instrumented program. It is defined in the
    30  // runtime.
    31  func getCovMetaList() []rtcov.CovMetaBlob
    32  
    33  // getCovCounterList returns a list of counter-data blobs registered
    34  // for the currently executing instrumented program. It is defined in the
    35  // runtime.
    36  func getCovCounterList() []rtcov.CovCounterBlob
    37  
    38  // getCovPkgMap returns a map storing the remapped package IDs for
    39  // hard-coded runtime packages (see internal/coverage/pkgid.go for
    40  // more on why hard-coded package IDs are needed). This function
    41  // is defined in the runtime.
    42  func getCovPkgMap() map[int]int
    43  
    44  // emitState holds useful state information during the emit process.
    45  //
    46  // When an instrumented program finishes execution and starts the
    47  // process of writing out coverage data, it's possible that an
    48  // existing meta-data file already exists in the output directory. In
    49  // this case openOutputFiles() below will leave the 'mf' field below
    50  // as nil. If a new meta-data file is needed, field 'mfname' will be
    51  // the final desired path of the meta file, 'mftmp' will be a
    52  // temporary file, and 'mf' will be an open os.File pointer for
    53  // 'mftmp'. The meta-data file payload will be written to 'mf', the
    54  // temp file will be then closed and renamed (from 'mftmp' to
    55  // 'mfname'), so as to insure that the meta-data file is created
    56  // atomically; we want this so that things work smoothly in cases
    57  // where there are several instances of a given instrumented program
    58  // all terminating at the same time and trying to create meta-data
    59  // files simultaneously.
    60  //
    61  // For counter data files there is less chance of a collision, hence
    62  // the openOutputFiles() stores the counter data file in 'cfname' and
    63  // then places the *io.File into 'cf'.
    64  type emitState struct {
    65  	mfname string   // path of final meta-data output file
    66  	mftmp  string   // path to meta-data temp file (if needed)
    67  	mf     *os.File // open os.File for meta-data temp file
    68  	cfname string   // path of final counter data file
    69  	cftmp  string   // path to counter data temp file
    70  	cf     *os.File // open os.File for counter data file
    71  	outdir string   // output directory
    72  
    73  	// List of meta-data symbols obtained from the runtime
    74  	metalist []rtcov.CovMetaBlob
    75  
    76  	// List of counter-data symbols obtained from the runtime
    77  	counterlist []rtcov.CovCounterBlob
    78  
    79  	// Table to use for remapping hard-coded pkg ids.
    80  	pkgmap map[int]int
    81  
    82  	// emit debug trace output
    83  	debug bool
    84  }
    85  
    86  var (
    87  	// finalHash is computed at init time from the list of meta-data
    88  	// symbols registered during init. It is used both for writing the
    89  	// meta-data file and counter-data files.
    90  	finalHash [16]byte
    91  	// Set to true when we've computed finalHash + finalMetaLen.
    92  	finalHashComputed bool
    93  	// Total meta-data length.
    94  	finalMetaLen uint64
    95  	// Records whether we've already attempted to write meta-data.
    96  	metaDataEmitAttempted bool
    97  	// Counter mode for this instrumented program run.
    98  	cmode coverage.CounterMode
    99  	// Counter granularity for this instrumented program run.
   100  	cgran coverage.CounterGranularity
   101  	// Cached value of GOCOVERDIR environment variable.
   102  	goCoverDir string
   103  	// Copy of os.Args made at init time, converted into map format.
   104  	capturedOsArgs map[string]string
   105  	// Flag used in tests to signal that coverage data already written.
   106  	covProfileAlreadyEmitted bool
   107  )
   108  
   109  // fileType is used to select between counter-data files and
   110  // meta-data files.
   111  type fileType int
   112  
   113  const (
   114  	noFile = 1 << iota
   115  	metaDataFile
   116  	counterDataFile
   117  )
   118  
   119  // emitMetaData emits the meta-data output file for this coverage run.
   120  // This entry point is intended to be invoked by the compiler from
   121  // an instrumented program's main package init func.
   122  func emitMetaData() {
   123  	if covProfileAlreadyEmitted {
   124  		return
   125  	}
   126  	ml, err := prepareForMetaEmit()
   127  	if err != nil {
   128  		fmt.Fprintf(os.Stderr, "error: coverage meta-data prep failed: %v\n", err)
   129  		if os.Getenv("GOCOVERDEBUG") != "" {
   130  			panic("meta-data write failure")
   131  		}
   132  	}
   133  	if len(ml) == 0 {
   134  		fmt.Fprintf(os.Stderr, "program not built with -cover\n")
   135  		return
   136  	}
   137  
   138  	goCoverDir = os.Getenv("GOCOVERDIR")
   139  	if goCoverDir == "" {
   140  		fmt.Fprintf(os.Stderr, "warning: GOCOVERDIR not set, no coverage data emitted\n")
   141  		return
   142  	}
   143  
   144  	if err := emitMetaDataToDirectory(goCoverDir, ml); err != nil {
   145  		fmt.Fprintf(os.Stderr, "error: coverage meta-data emit failed: %v\n", err)
   146  		if os.Getenv("GOCOVERDEBUG") != "" {
   147  			panic("meta-data write failure")
   148  		}
   149  	}
   150  }
   151  
   152  func modeClash(m coverage.CounterMode) bool {
   153  	if m == coverage.CtrModeRegOnly || m == coverage.CtrModeTestMain {
   154  		return false
   155  	}
   156  	if cmode == coverage.CtrModeInvalid {
   157  		cmode = m
   158  		return false
   159  	}
   160  	return cmode != m
   161  }
   162  
   163  func granClash(g coverage.CounterGranularity) bool {
   164  	if cgran == coverage.CtrGranularityInvalid {
   165  		cgran = g
   166  		return false
   167  	}
   168  	return cgran != g
   169  }
   170  
   171  // prepareForMetaEmit performs preparatory steps needed prior to
   172  // emitting a meta-data file, notably computing a final hash of
   173  // all meta-data blobs and capturing os args.
   174  func prepareForMetaEmit() ([]rtcov.CovMetaBlob, error) {
   175  	// Ask the runtime for the list of coverage meta-data symbols.
   176  	ml := getCovMetaList()
   177  
   178  	// In the normal case (go build -o prog.exe ... ; ./prog.exe)
   179  	// len(ml) will always be non-zero, but we check here since at
   180  	// some point this function will be reachable via user-callable
   181  	// APIs (for example, to write out coverage data from a server
   182  	// program that doesn't ever call os.Exit).
   183  	if len(ml) == 0 {
   184  		return nil, nil
   185  	}
   186  
   187  	s := &emitState{
   188  		metalist: ml,
   189  		debug:    os.Getenv("GOCOVERDEBUG") != "",
   190  	}
   191  
   192  	// Capture os.Args() now so as to avoid issues if args
   193  	// are rewritten during program execution.
   194  	capturedOsArgs = captureOsArgs()
   195  
   196  	if s.debug {
   197  		fmt.Fprintf(os.Stderr, "=+= GOCOVERDIR is %s\n", os.Getenv("GOCOVERDIR"))
   198  		fmt.Fprintf(os.Stderr, "=+= contents of covmetalist:\n")
   199  		for k, b := range ml {
   200  			fmt.Fprintf(os.Stderr, "=+= slot: %d path: %s ", k, b.PkgPath)
   201  			if b.PkgID != -1 {
   202  				fmt.Fprintf(os.Stderr, " hcid: %d", b.PkgID)
   203  			}
   204  			fmt.Fprintf(os.Stderr, "\n")
   205  		}
   206  		pm := getCovPkgMap()
   207  		fmt.Fprintf(os.Stderr, "=+= remap table:\n")
   208  		for from, to := range pm {
   209  			fmt.Fprintf(os.Stderr, "=+= from %d to %d\n",
   210  				uint32(from), uint32(to))
   211  		}
   212  	}
   213  
   214  	h := md5.New()
   215  	tlen := uint64(unsafe.Sizeof(coverage.MetaFileHeader{}))
   216  	for _, entry := range ml {
   217  		if _, err := h.Write(entry.Hash[:]); err != nil {
   218  			return nil, err
   219  		}
   220  		tlen += uint64(entry.Len)
   221  		ecm := coverage.CounterMode(entry.CounterMode)
   222  		if modeClash(ecm) {
   223  			return nil, fmt.Errorf("coverage counter mode clash: package %s uses mode=%d, but package %s uses mode=%s\n", ml[0].PkgPath, cmode, entry.PkgPath, ecm)
   224  		}
   225  		ecg := coverage.CounterGranularity(entry.CounterGranularity)
   226  		if granClash(ecg) {
   227  			return nil, fmt.Errorf("coverage counter granularity clash: package %s uses gran=%d, but package %s uses gran=%s\n", ml[0].PkgPath, cgran, entry.PkgPath, ecg)
   228  		}
   229  	}
   230  
   231  	// Hash mode and granularity as well.
   232  	h.Write([]byte(cmode.String()))
   233  	h.Write([]byte(cgran.String()))
   234  
   235  	// Compute final digest.
   236  	fh := h.Sum(nil)
   237  	copy(finalHash[:], fh)
   238  	finalHashComputed = true
   239  	finalMetaLen = tlen
   240  
   241  	return ml, nil
   242  }
   243  
   244  // emitMetaData emits the meta-data output file to the specified
   245  // directory, returning an error if something went wrong.
   246  func emitMetaDataToDirectory(outdir string, ml []rtcov.CovMetaBlob) error {
   247  	ml, err := prepareForMetaEmit()
   248  	if err != nil {
   249  		return err
   250  	}
   251  	if len(ml) == 0 {
   252  		return nil
   253  	}
   254  
   255  	metaDataEmitAttempted = true
   256  
   257  	s := &emitState{
   258  		metalist: ml,
   259  		debug:    os.Getenv("GOCOVERDEBUG") != "",
   260  		outdir:   outdir,
   261  	}
   262  
   263  	// Open output files.
   264  	if err := s.openOutputFiles(finalHash, finalMetaLen, metaDataFile); err != nil {
   265  		return err
   266  	}
   267  
   268  	// Emit meta-data file only if needed (may already be present).
   269  	if s.needMetaDataFile() {
   270  		if err := s.emitMetaDataFile(finalHash, finalMetaLen); err != nil {
   271  			return err
   272  		}
   273  	}
   274  	return nil
   275  }
   276  
   277  // emitCounterData emits the counter data output file for this coverage run.
   278  // This entry point is intended to be invoked by the runtime when an
   279  // instrumented program is terminating or calling os.Exit().
   280  func emitCounterData() {
   281  	if goCoverDir == "" || !finalHashComputed || covProfileAlreadyEmitted {
   282  		return
   283  	}
   284  	if err := emitCounterDataToDirectory(goCoverDir); err != nil {
   285  		fmt.Fprintf(os.Stderr, "error: coverage counter data emit failed: %v\n", err)
   286  		if os.Getenv("GOCOVERDEBUG") != "" {
   287  			panic("counter-data write failure")
   288  		}
   289  	}
   290  }
   291  
   292  // emitMetaData emits the counter-data output file for this coverage run.
   293  func emitCounterDataToDirectory(outdir string) error {
   294  	// Ask the runtime for the list of coverage counter symbols.
   295  	cl := getCovCounterList()
   296  	if len(cl) == 0 {
   297  		// no work to do here.
   298  		return nil
   299  	}
   300  
   301  	if !finalHashComputed {
   302  		return fmt.Errorf("error: meta-data not available (binary not built with -cover?)")
   303  	}
   304  
   305  	// Ask the runtime for the list of coverage counter symbols.
   306  	pm := getCovPkgMap()
   307  	s := &emitState{
   308  		counterlist: cl,
   309  		pkgmap:      pm,
   310  		outdir:      outdir,
   311  		debug:       os.Getenv("GOCOVERDEBUG") != "",
   312  	}
   313  
   314  	// Open output file.
   315  	if err := s.openOutputFiles(finalHash, finalMetaLen, counterDataFile); err != nil {
   316  		return err
   317  	}
   318  	if s.cf == nil {
   319  		return fmt.Errorf("counter data output file open failed (no additional info")
   320  	}
   321  
   322  	// Emit counter data file.
   323  	if err := s.emitCounterDataFile(finalHash, s.cf); err != nil {
   324  		return err
   325  	}
   326  	if err := s.cf.Close(); err != nil {
   327  		return fmt.Errorf("closing counter data file: %v", err)
   328  	}
   329  
   330  	// Counter file has now been closed. Rename the temp to the
   331  	// final desired path.
   332  	if err := os.Rename(s.cftmp, s.cfname); err != nil {
   333  		return fmt.Errorf("writing %s: rename from %s failed: %v\n", s.cfname, s.cftmp, err)
   334  	}
   335  
   336  	return nil
   337  }
   338  
   339  // emitMetaData emits counter data for this coverage run to an io.Writer.
   340  func (s *emitState) emitCounterDataToWriter(w io.Writer) error {
   341  	if err := s.emitCounterDataFile(finalHash, w); err != nil {
   342  		return err
   343  	}
   344  	return nil
   345  }
   346  
   347  // openMetaFile determines whether we need to emit a meta-data output
   348  // file, or whether we can reuse the existing file in the coverage out
   349  // dir. It updates mfname/mftmp/mf fields in 's', returning an error
   350  // if something went wrong. See the comment on the emitState type
   351  // definition above for more on how file opening is managed.
   352  func (s *emitState) openMetaFile(metaHash [16]byte, metaLen uint64) error {
   353  
   354  	// Open meta-outfile for reading to see if it exists.
   355  	fn := fmt.Sprintf("%s.%x", coverage.MetaFilePref, metaHash)
   356  	s.mfname = filepath.Join(s.outdir, fn)
   357  	fi, err := os.Stat(s.mfname)
   358  	if err != nil || fi.Size() != int64(metaLen) {
   359  		// We need a new meta-file.
   360  		tname := "tmp." + fn + fmt.Sprintf("%d", time.Now().UnixNano())
   361  		s.mftmp = filepath.Join(s.outdir, tname)
   362  		s.mf, err = os.Create(s.mftmp)
   363  		if err != nil {
   364  			return fmt.Errorf("creating meta-data file %s: %v", s.mftmp, err)
   365  		}
   366  	}
   367  	return nil
   368  }
   369  
   370  // openCounterFile opens an output file for the counter data portion
   371  // of a test coverage run. If updates the 'cfname' and 'cf' fields in
   372  // 's', returning an error if something went wrong.
   373  func (s *emitState) openCounterFile(metaHash [16]byte) error {
   374  	processID := os.Getpid()
   375  	fn := fmt.Sprintf(coverage.CounterFileTempl, coverage.CounterFilePref, metaHash, processID, time.Now().UnixNano())
   376  	s.cfname = filepath.Join(s.outdir, fn)
   377  	s.cftmp = filepath.Join(s.outdir, "tmp."+fn)
   378  	var err error
   379  	s.cf, err = os.Create(s.cftmp)
   380  	if err != nil {
   381  		return fmt.Errorf("creating counter data file %s: %v", s.cftmp, err)
   382  	}
   383  	return nil
   384  }
   385  
   386  // openOutputFiles opens output files in preparation for emitting
   387  // coverage data. In the case of the meta-data file, openOutputFiles
   388  // may determine that we can reuse an existing meta-data file in the
   389  // outdir, in which case it will leave the 'mf' field in the state
   390  // struct as nil. If a new meta-file is needed, the field 'mfname'
   391  // will be the final desired path of the meta file, 'mftmp' will be a
   392  // temporary file, and 'mf' will be an open os.File pointer for
   393  // 'mftmp'. The idea is that the client/caller will write content into
   394  // 'mf', close it, and then rename 'mftmp' to 'mfname'. This function
   395  // also opens the counter data output file, setting 'cf' and 'cfname'
   396  // in the state struct.
   397  func (s *emitState) openOutputFiles(metaHash [16]byte, metaLen uint64, which fileType) error {
   398  	fi, err := os.Stat(s.outdir)
   399  	if err != nil {
   400  		return fmt.Errorf("output directory %q inaccessible (err: %v); no coverage data written", s.outdir, err)
   401  	}
   402  	if !fi.IsDir() {
   403  		return fmt.Errorf("output directory %q not a directory; no coverage data written", s.outdir)
   404  	}
   405  
   406  	if (which & metaDataFile) != 0 {
   407  		if err := s.openMetaFile(metaHash, metaLen); err != nil {
   408  			return err
   409  		}
   410  	}
   411  	if (which & counterDataFile) != 0 {
   412  		if err := s.openCounterFile(metaHash); err != nil {
   413  			return err
   414  		}
   415  	}
   416  	return nil
   417  }
   418  
   419  // emitMetaDataFile emits coverage meta-data to a previously opened
   420  // temporary file (s.mftmp), then renames the generated file to the
   421  // final path (s.mfname).
   422  func (s *emitState) emitMetaDataFile(finalHash [16]byte, tlen uint64) error {
   423  	if err := writeMetaData(s.mf, s.metalist, cmode, cgran, finalHash); err != nil {
   424  		return fmt.Errorf("writing %s: %v\n", s.mftmp, err)
   425  	}
   426  	if err := s.mf.Close(); err != nil {
   427  		return fmt.Errorf("closing meta data temp file: %v", err)
   428  	}
   429  
   430  	// Temp file has now been flushed and closed. Rename the temp to the
   431  	// final desired path.
   432  	if err := os.Rename(s.mftmp, s.mfname); err != nil {
   433  		return fmt.Errorf("writing %s: rename from %s failed: %v\n", s.mfname, s.mftmp, err)
   434  	}
   435  
   436  	return nil
   437  }
   438  
   439  // needMetaDataFile returns TRUE if we need to emit a meta-data file
   440  // for this program run. It should be used only after
   441  // openOutputFiles() has been invoked.
   442  func (s *emitState) needMetaDataFile() bool {
   443  	return s.mf != nil
   444  }
   445  
   446  func writeMetaData(w io.Writer, metalist []rtcov.CovMetaBlob, cmode coverage.CounterMode, gran coverage.CounterGranularity, finalHash [16]byte) error {
   447  	mfw := encodemeta.NewCoverageMetaFileWriter("<io.Writer>", w)
   448  
   449  	// Note: "sd" is re-initialized on each iteration of the loop
   450  	// below, and would normally be declared inside the loop, but
   451  	// placed here escape analysis since we capture it in bufHdr.
   452  	var sd []byte
   453  	bufHdr := (*reflect.SliceHeader)(unsafe.Pointer(&sd))
   454  
   455  	var blobs [][]byte
   456  	for _, e := range metalist {
   457  		bufHdr.Data = uintptr(unsafe.Pointer(e.P))
   458  		bufHdr.Len = int(e.Len)
   459  		bufHdr.Cap = int(e.Len)
   460  		blobs = append(blobs, sd)
   461  	}
   462  	return mfw.Write(finalHash, blobs, cmode, gran)
   463  }
   464  
   465  func (s *emitState) NumFuncs() (int, error) {
   466  	var sd []atomic.Uint32
   467  	bufHdr := (*reflect.SliceHeader)(unsafe.Pointer(&sd))
   468  
   469  	totalFuncs := 0
   470  	for _, c := range s.counterlist {
   471  		bufHdr.Data = uintptr(unsafe.Pointer(c.Counters))
   472  		bufHdr.Len = int(c.Len)
   473  		bufHdr.Cap = int(c.Len)
   474  		for i := 0; i < len(sd); i++ {
   475  			// Skip ahead until the next non-zero value.
   476  			sdi := sd[i].Load()
   477  			if sdi == 0 {
   478  				continue
   479  			}
   480  
   481  			// We found a function that was executed.
   482  			nCtrs := sdi
   483  
   484  			// Check to make sure that we have at least one live
   485  			// counter. See the implementation note in ClearCoverageCounters
   486  			// for a description of why this is needed.
   487  			isLive := false
   488  			st := i + coverage.FirstCtrOffset
   489  			counters := sd[st : st+int(nCtrs)]
   490  			for i := 0; i < len(counters); i++ {
   491  				if counters[i].Load() != 0 {
   492  					isLive = true
   493  					break
   494  				}
   495  			}
   496  			if !isLive {
   497  				// Skip this function.
   498  				i += coverage.FirstCtrOffset + int(nCtrs) - 1
   499  				continue
   500  			}
   501  
   502  			totalFuncs++
   503  
   504  			// Move to the next function.
   505  			i += coverage.FirstCtrOffset + int(nCtrs) - 1
   506  		}
   507  	}
   508  	return totalFuncs, nil
   509  }
   510  
   511  func (s *emitState) VisitFuncs(f encodecounter.CounterVisitorFn) error {
   512  	var sd []atomic.Uint32
   513  	var tcounters []uint32
   514  	bufHdr := (*reflect.SliceHeader)(unsafe.Pointer(&sd))
   515  
   516  	rdCounters := func(actrs []atomic.Uint32, ctrs []uint32) []uint32 {
   517  		ctrs = ctrs[:0]
   518  		for i := range actrs {
   519  			ctrs = append(ctrs, actrs[i].Load())
   520  		}
   521  		return ctrs
   522  	}
   523  
   524  	dpkg := uint32(0)
   525  	for _, c := range s.counterlist {
   526  		bufHdr.Data = uintptr(unsafe.Pointer(c.Counters))
   527  		bufHdr.Len = int(c.Len)
   528  		bufHdr.Cap = int(c.Len)
   529  		for i := 0; i < len(sd); i++ {
   530  			// Skip ahead until the next non-zero value.
   531  			sdi := sd[i].Load()
   532  			if sdi == 0 {
   533  				continue
   534  			}
   535  
   536  			// We found a function that was executed.
   537  			nCtrs := sd[i+coverage.NumCtrsOffset].Load()
   538  			pkgId := sd[i+coverage.PkgIdOffset].Load()
   539  			funcId := sd[i+coverage.FuncIdOffset].Load()
   540  			cst := i + coverage.FirstCtrOffset
   541  			counters := sd[cst : cst+int(nCtrs)]
   542  
   543  			// Check to make sure that we have at least one live
   544  			// counter. See the implementation note in ClearCoverageCounters
   545  			// for a description of why this is needed.
   546  			isLive := false
   547  			for i := 0; i < len(counters); i++ {
   548  				if counters[i].Load() != 0 {
   549  					isLive = true
   550  					break
   551  				}
   552  			}
   553  			if !isLive {
   554  				// Skip this function.
   555  				i += coverage.FirstCtrOffset + int(nCtrs) - 1
   556  				continue
   557  			}
   558  
   559  			if s.debug {
   560  				if pkgId != dpkg {
   561  					dpkg = pkgId
   562  					fmt.Fprintf(os.Stderr, "\n=+= %d: pk=%d visit live fcn",
   563  						i, pkgId)
   564  				}
   565  				fmt.Fprintf(os.Stderr, " {i=%d F%d NC%d}", i, funcId, nCtrs)
   566  			}
   567  
   568  			// Vet and/or fix up package ID. A package ID of zero
   569  			// indicates that there is some new package X that is a
   570  			// runtime dependency, and this package has code that
   571  			// executes before its corresponding init package runs.
   572  			// This is a fatal error that we should only see during
   573  			// Go development (e.g. tip).
   574  			ipk := int32(pkgId)
   575  			if ipk == 0 {
   576  				fmt.Fprintf(os.Stderr, "\n")
   577  				reportErrorInHardcodedList(int32(i), ipk, funcId, nCtrs)
   578  			} else if ipk < 0 {
   579  				if newId, ok := s.pkgmap[int(ipk)]; ok {
   580  					pkgId = uint32(newId)
   581  				} else {
   582  					fmt.Fprintf(os.Stderr, "\n")
   583  					reportErrorInHardcodedList(int32(i), ipk, funcId, nCtrs)
   584  				}
   585  			} else {
   586  				// The package ID value stored in the counter array
   587  				// has 1 added to it (so as to preclude the
   588  				// possibility of a zero value ; see
   589  				// runtime.addCovMeta), so subtract off 1 here to form
   590  				// the real package ID.
   591  				pkgId--
   592  			}
   593  
   594  			tcounters = rdCounters(counters, tcounters)
   595  			if err := f(pkgId, funcId, tcounters); err != nil {
   596  				return err
   597  			}
   598  
   599  			// Skip over this function.
   600  			i += coverage.FirstCtrOffset + int(nCtrs) - 1
   601  		}
   602  		if s.debug {
   603  			fmt.Fprintf(os.Stderr, "\n")
   604  		}
   605  	}
   606  	return nil
   607  }
   608  
   609  // captureOsArgs converts os.Args() into the format we use to store
   610  // this info in the counter data file (counter data file "args"
   611  // section is a generic key-value collection). See the 'args' section
   612  // in internal/coverage/defs.go for more info. The args map
   613  // is also used to capture GOOS + GOARCH values as well.
   614  func captureOsArgs() map[string]string {
   615  	m := make(map[string]string)
   616  	m["argc"] = fmt.Sprintf("%d", len(os.Args))
   617  	for k, a := range os.Args {
   618  		m[fmt.Sprintf("argv%d", k)] = a
   619  	}
   620  	m["GOOS"] = runtime.GOOS
   621  	m["GOARCH"] = runtime.GOARCH
   622  	return m
   623  }
   624  
   625  // emitCounterDataFile emits the counter data portion of a
   626  // coverage output file (to the file 's.cf').
   627  func (s *emitState) emitCounterDataFile(finalHash [16]byte, w io.Writer) error {
   628  	cfw := encodecounter.NewCoverageDataWriter(w, coverage.CtrULeb128)
   629  	if err := cfw.Write(finalHash, capturedOsArgs, s); err != nil {
   630  		return err
   631  	}
   632  	return nil
   633  }
   634  
   635  // markProfileEmitted signals the runtime/coverage machinery that
   636  // coverate data output files have already been written out, and there
   637  // is no need to take any additional action at exit time. This
   638  // function is called (via linknamed reference) from the
   639  // coverage-related boilerplate code in _testmain.go emitted for go
   640  // unit tests.
   641  func markProfileEmitted(val bool) {
   642  	covProfileAlreadyEmitted = val
   643  }
   644  
   645  func reportErrorInHardcodedList(slot, pkgID int32, fnID, nCtrs uint32) {
   646  	metaList := getCovMetaList()
   647  	pkgMap := getCovPkgMap()
   648  
   649  	println("internal error in coverage meta-data tracking:")
   650  	println("encountered bad pkgID:", pkgID, " at slot:", slot,
   651  		" fnID:", fnID, " numCtrs:", nCtrs)
   652  	println("list of hard-coded runtime package IDs needs revising.")
   653  	println("[see the comment on the 'rtPkgs' var in ")
   654  	println(" <goroot>/src/internal/coverage/pkid.go]")
   655  	println("registered list:")
   656  	for k, b := range metaList {
   657  		print("slot: ", k, " path='", b.PkgPath, "' ")
   658  		if b.PkgID != -1 {
   659  			print(" hard-coded id: ", b.PkgID)
   660  		}
   661  		println("")
   662  	}
   663  	println("remap table:")
   664  	for from, to := range pkgMap {
   665  		println("from ", from, " to ", to)
   666  	}
   667  }