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