...

Source file src/runtime/coverage/emit.go

Documentation: runtime/coverage

     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  	"runtime"
    18  	"strconv"
    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  // emitMetaDataToDirectory 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  // emitCounterDataToDirectory 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  // emitCounterDataToWriter 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 + strconv.FormatInt(time.Now().UnixNano(), 10)
   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  	var blobs [][]byte
   450  	for _, e := range metalist {
   451  		sd := unsafe.Slice(e.P, int(e.Len))
   452  		blobs = append(blobs, sd)
   453  	}
   454  	return mfw.Write(finalHash, blobs, cmode, gran)
   455  }
   456  
   457  func (s *emitState) VisitFuncs(f encodecounter.CounterVisitorFn) error {
   458  	var tcounters []uint32
   459  
   460  	rdCounters := func(actrs []atomic.Uint32, ctrs []uint32) []uint32 {
   461  		ctrs = ctrs[:0]
   462  		for i := range actrs {
   463  			ctrs = append(ctrs, actrs[i].Load())
   464  		}
   465  		return ctrs
   466  	}
   467  
   468  	dpkg := uint32(0)
   469  	for _, c := range s.counterlist {
   470  		sd := unsafe.Slice((*atomic.Uint32)(unsafe.Pointer(c.Counters)), int(c.Len))
   471  		for i := 0; i < len(sd); i++ {
   472  			// Skip ahead until the next non-zero value.
   473  			sdi := sd[i].Load()
   474  			if sdi == 0 {
   475  				continue
   476  			}
   477  
   478  			// We found a function that was executed.
   479  			nCtrs := sd[i+coverage.NumCtrsOffset].Load()
   480  			pkgId := sd[i+coverage.PkgIdOffset].Load()
   481  			funcId := sd[i+coverage.FuncIdOffset].Load()
   482  			cst := i + coverage.FirstCtrOffset
   483  			counters := sd[cst : cst+int(nCtrs)]
   484  
   485  			// Check to make sure that we have at least one live
   486  			// counter. See the implementation note in ClearCoverageCounters
   487  			// for a description of why this is needed.
   488  			isLive := false
   489  			for i := 0; i < len(counters); i++ {
   490  				if counters[i].Load() != 0 {
   491  					isLive = true
   492  					break
   493  				}
   494  			}
   495  			if !isLive {
   496  				// Skip this function.
   497  				i += coverage.FirstCtrOffset + int(nCtrs) - 1
   498  				continue
   499  			}
   500  
   501  			if s.debug {
   502  				if pkgId != dpkg {
   503  					dpkg = pkgId
   504  					fmt.Fprintf(os.Stderr, "\n=+= %d: pk=%d visit live fcn",
   505  						i, pkgId)
   506  				}
   507  				fmt.Fprintf(os.Stderr, " {i=%d F%d NC%d}", i, funcId, nCtrs)
   508  			}
   509  
   510  			// Vet and/or fix up package ID. A package ID of zero
   511  			// indicates that there is some new package X that is a
   512  			// runtime dependency, and this package has code that
   513  			// executes before its corresponding init package runs.
   514  			// This is a fatal error that we should only see during
   515  			// Go development (e.g. tip).
   516  			ipk := int32(pkgId)
   517  			if ipk == 0 {
   518  				fmt.Fprintf(os.Stderr, "\n")
   519  				reportErrorInHardcodedList(int32(i), ipk, funcId, nCtrs)
   520  			} else if ipk < 0 {
   521  				if newId, ok := s.pkgmap[int(ipk)]; ok {
   522  					pkgId = uint32(newId)
   523  				} else {
   524  					fmt.Fprintf(os.Stderr, "\n")
   525  					reportErrorInHardcodedList(int32(i), ipk, funcId, nCtrs)
   526  				}
   527  			} else {
   528  				// The package ID value stored in the counter array
   529  				// has 1 added to it (so as to preclude the
   530  				// possibility of a zero value ; see
   531  				// runtime.addCovMeta), so subtract off 1 here to form
   532  				// the real package ID.
   533  				pkgId--
   534  			}
   535  
   536  			tcounters = rdCounters(counters, tcounters)
   537  			if err := f(pkgId, funcId, tcounters); err != nil {
   538  				return err
   539  			}
   540  
   541  			// Skip over this function.
   542  			i += coverage.FirstCtrOffset + int(nCtrs) - 1
   543  		}
   544  		if s.debug {
   545  			fmt.Fprintf(os.Stderr, "\n")
   546  		}
   547  	}
   548  	return nil
   549  }
   550  
   551  // captureOsArgs converts os.Args() into the format we use to store
   552  // this info in the counter data file (counter data file "args"
   553  // section is a generic key-value collection). See the 'args' section
   554  // in internal/coverage/defs.go for more info. The args map
   555  // is also used to capture GOOS + GOARCH values as well.
   556  func captureOsArgs() map[string]string {
   557  	m := make(map[string]string)
   558  	m["argc"] = strconv.Itoa(len(os.Args))
   559  	for k, a := range os.Args {
   560  		m[fmt.Sprintf("argv%d", k)] = a
   561  	}
   562  	m["GOOS"] = runtime.GOOS
   563  	m["GOARCH"] = runtime.GOARCH
   564  	return m
   565  }
   566  
   567  // emitCounterDataFile emits the counter data portion of a
   568  // coverage output file (to the file 's.cf').
   569  func (s *emitState) emitCounterDataFile(finalHash [16]byte, w io.Writer) error {
   570  	cfw := encodecounter.NewCoverageDataWriter(w, coverage.CtrULeb128)
   571  	if err := cfw.Write(finalHash, capturedOsArgs, s); err != nil {
   572  		return err
   573  	}
   574  	return nil
   575  }
   576  
   577  // markProfileEmitted signals the runtime/coverage machinery that
   578  // coverage data output files have already been written out, and there
   579  // is no need to take any additional action at exit time. This
   580  // function is called (via linknamed reference) from the
   581  // coverage-related boilerplate code in _testmain.go emitted for go
   582  // unit tests.
   583  func markProfileEmitted(val bool) {
   584  	covProfileAlreadyEmitted = val
   585  }
   586  
   587  func reportErrorInHardcodedList(slot, pkgID int32, fnID, nCtrs uint32) {
   588  	metaList := getCovMetaList()
   589  	pkgMap := getCovPkgMap()
   590  
   591  	println("internal error in coverage meta-data tracking:")
   592  	println("encountered bad pkgID:", pkgID, " at slot:", slot,
   593  		" fnID:", fnID, " numCtrs:", nCtrs)
   594  	println("list of hard-coded runtime package IDs needs revising.")
   595  	println("[see the comment on the 'rtPkgs' var in ")
   596  	println(" <goroot>/src/internal/coverage/pkid.go]")
   597  	println("registered list:")
   598  	for k, b := range metaList {
   599  		print("slot: ", k, " path='", b.PkgPath, "' ")
   600  		if b.PkgID != -1 {
   601  			print(" hard-coded id: ", b.PkgID)
   602  		}
   603  		println("")
   604  	}
   605  	println("remap table:")
   606  	for from, to := range pkgMap {
   607  		println("from ", from, " to ", to)
   608  	}
   609  }
   610  

View as plain text