// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package coverage

import (
	
	
	
	
	
	
	
	
	
	
	
	
	
	
)

// This file contains functions that support the writing of data files
// emitted at the end of code coverage testing runs, from instrumented
// executables.

// getCovMetaList returns a list of meta-data blobs registered
// for the currently executing instrumented program. It is defined in the
// runtime.
func getCovMetaList() []rtcov.CovMetaBlob

// getCovCounterList returns a list of counter-data blobs registered
// for the currently executing instrumented program. It is defined in the
// runtime.
func getCovCounterList() []rtcov.CovCounterBlob

// getCovPkgMap returns a map storing the remapped package IDs for
// hard-coded runtime packages (see internal/coverage/pkgid.go for
// more on why hard-coded package IDs are needed). This function
// is defined in the runtime.
func getCovPkgMap() map[int]int

// emitState holds useful state information during the emit process.
//
// When an instrumented program finishes execution and starts the
// process of writing out coverage data, it's possible that an
// existing meta-data file already exists in the output directory. In
// this case openOutputFiles() below will leave the 'mf' field below
// as nil. If a new meta-data file is needed, field 'mfname' will be
// the final desired path of the meta file, 'mftmp' will be a
// temporary file, and 'mf' will be an open os.File pointer for
// 'mftmp'. The meta-data file payload will be written to 'mf', the
// temp file will be then closed and renamed (from 'mftmp' to
// 'mfname'), so as to insure that the meta-data file is created
// atomically; we want this so that things work smoothly in cases
// where there are several instances of a given instrumented program
// all terminating at the same time and trying to create meta-data
// files simultaneously.
//
// For counter data files there is less chance of a collision, hence
// the openOutputFiles() stores the counter data file in 'cfname' and
// then places the *io.File into 'cf'.
type emitState struct {
	mfname string   // path of final meta-data output file
	mftmp  string   // path to meta-data temp file (if needed)
	mf     *os.File // open os.File for meta-data temp file
	cfname string   // path of final counter data file
	cftmp  string   // path to counter data temp file
	cf     *os.File // open os.File for counter data file
	outdir string   // output directory

	// List of meta-data symbols obtained from the runtime
	metalist []rtcov.CovMetaBlob

	// List of counter-data symbols obtained from the runtime
	counterlist []rtcov.CovCounterBlob

	// Table to use for remapping hard-coded pkg ids.
	pkgmap map[int]int

	// emit debug trace output
	debug bool
}

var (
	// finalHash is computed at init time from the list of meta-data
	// symbols registered during init. It is used both for writing the
	// meta-data file and counter-data files.
	finalHash [16]byte
	// Set to true when we've computed finalHash + finalMetaLen.
	finalHashComputed bool
	// Total meta-data length.
	finalMetaLen uint64
	// Records whether we've already attempted to write meta-data.
	metaDataEmitAttempted bool
	// Counter mode for this instrumented program run.
	cmode coverage.CounterMode
	// Counter granularity for this instrumented program run.
	cgran coverage.CounterGranularity
	// Cached value of GOCOVERDIR environment variable.
	goCoverDir string
	// Copy of os.Args made at init time, converted into map format.
	capturedOsArgs map[string]string
	// Flag used in tests to signal that coverage data already written.
	covProfileAlreadyEmitted bool
)

// fileType is used to select between counter-data files and
// meta-data files.
type fileType int

const (
	noFile = 1 << iota
	metaDataFile
	counterDataFile
)

// emitMetaData emits the meta-data output file for this coverage run.
// This entry point is intended to be invoked by the compiler from
// an instrumented program's main package init func.
func emitMetaData() {
	if covProfileAlreadyEmitted {
		return
	}
	,  := prepareForMetaEmit()
	if  != nil {
		fmt.Fprintf(os.Stderr, "error: coverage meta-data prep failed: %v\n", )
		if os.Getenv("GOCOVERDEBUG") != "" {
			panic("meta-data write failure")
		}
	}
	if len() == 0 {
		fmt.Fprintf(os.Stderr, "program not built with -cover\n")
		return
	}

	goCoverDir = os.Getenv("GOCOVERDIR")
	if goCoverDir == "" {
		fmt.Fprintf(os.Stderr, "warning: GOCOVERDIR not set, no coverage data emitted\n")
		return
	}

	if  := emitMetaDataToDirectory(goCoverDir, );  != nil {
		fmt.Fprintf(os.Stderr, "error: coverage meta-data emit failed: %v\n", )
		if os.Getenv("GOCOVERDEBUG") != "" {
			panic("meta-data write failure")
		}
	}
}

func modeClash( coverage.CounterMode) bool {
	if  == coverage.CtrModeRegOnly ||  == coverage.CtrModeTestMain {
		return false
	}
	if cmode == coverage.CtrModeInvalid {
		cmode = 
		return false
	}
	return cmode != 
}

func granClash( coverage.CounterGranularity) bool {
	if cgran == coverage.CtrGranularityInvalid {
		cgran = 
		return false
	}
	return cgran != 
}

// prepareForMetaEmit performs preparatory steps needed prior to
// emitting a meta-data file, notably computing a final hash of
// all meta-data blobs and capturing os args.
func prepareForMetaEmit() ([]rtcov.CovMetaBlob, error) {
	// Ask the runtime for the list of coverage meta-data symbols.
	 := getCovMetaList()

	// In the normal case (go build -o prog.exe ... ; ./prog.exe)
	// len(ml) will always be non-zero, but we check here since at
	// some point this function will be reachable via user-callable
	// APIs (for example, to write out coverage data from a server
	// program that doesn't ever call os.Exit).
	if len() == 0 {
		return nil, nil
	}

	 := &emitState{
		metalist: ,
		debug:    os.Getenv("GOCOVERDEBUG") != "",
	}

	// Capture os.Args() now so as to avoid issues if args
	// are rewritten during program execution.
	capturedOsArgs = captureOsArgs()

	if .debug {
		fmt.Fprintf(os.Stderr, "=+= GOCOVERDIR is %s\n", os.Getenv("GOCOVERDIR"))
		fmt.Fprintf(os.Stderr, "=+= contents of covmetalist:\n")
		for ,  := range  {
			fmt.Fprintf(os.Stderr, "=+= slot: %d path: %s ", , .PkgPath)
			if .PkgID != -1 {
				fmt.Fprintf(os.Stderr, " hcid: %d", .PkgID)
			}
			fmt.Fprintf(os.Stderr, "\n")
		}
		 := getCovPkgMap()
		fmt.Fprintf(os.Stderr, "=+= remap table:\n")
		for ,  := range  {
			fmt.Fprintf(os.Stderr, "=+= from %d to %d\n",
				uint32(), uint32())
		}
	}

	 := md5.New()
	 := uint64(unsafe.Sizeof(coverage.MetaFileHeader{}))
	for ,  := range  {
		if ,  := .Write(.Hash[:]);  != nil {
			return nil, 
		}
		 += uint64(.Len)
		 := coverage.CounterMode(.CounterMode)
		if modeClash() {
			return nil, fmt.Errorf("coverage counter mode clash: package %s uses mode=%d, but package %s uses mode=%s\n", [0].PkgPath, cmode, .PkgPath, )
		}
		 := coverage.CounterGranularity(.CounterGranularity)
		if granClash() {
			return nil, fmt.Errorf("coverage counter granularity clash: package %s uses gran=%d, but package %s uses gran=%s\n", [0].PkgPath, cgran, .PkgPath, )
		}
	}

	// Hash mode and granularity as well.
	.Write([]byte(cmode.String()))
	.Write([]byte(cgran.String()))

	// Compute final digest.
	 := .Sum(nil)
	copy(finalHash[:], )
	finalHashComputed = true
	finalMetaLen = 

	return , nil
}

// emitMetaDataToDirectory emits the meta-data output file to the specified
// directory, returning an error if something went wrong.
func emitMetaDataToDirectory( string,  []rtcov.CovMetaBlob) error {
	,  := prepareForMetaEmit()
	if  != nil {
		return 
	}
	if len() == 0 {
		return nil
	}

	metaDataEmitAttempted = true

	 := &emitState{
		metalist: ,
		debug:    os.Getenv("GOCOVERDEBUG") != "",
		outdir:   ,
	}

	// Open output files.
	if  := .openOutputFiles(finalHash, finalMetaLen, metaDataFile);  != nil {
		return 
	}

	// Emit meta-data file only if needed (may already be present).
	if .needMetaDataFile() {
		if  := .emitMetaDataFile(finalHash, finalMetaLen);  != nil {
			return 
		}
	}
	return nil
}

// emitCounterData emits the counter data output file for this coverage run.
// This entry point is intended to be invoked by the runtime when an
// instrumented program is terminating or calling os.Exit().
func emitCounterData() {
	if goCoverDir == "" || !finalHashComputed || covProfileAlreadyEmitted {
		return
	}
	if  := emitCounterDataToDirectory(goCoverDir);  != nil {
		fmt.Fprintf(os.Stderr, "error: coverage counter data emit failed: %v\n", )
		if os.Getenv("GOCOVERDEBUG") != "" {
			panic("counter-data write failure")
		}
	}
}

// emitCounterDataToDirectory emits the counter-data output file for this coverage run.
func emitCounterDataToDirectory( string) error {
	// Ask the runtime for the list of coverage counter symbols.
	 := getCovCounterList()
	if len() == 0 {
		// no work to do here.
		return nil
	}

	if !finalHashComputed {
		return fmt.Errorf("error: meta-data not available (binary not built with -cover?)")
	}

	// Ask the runtime for the list of coverage counter symbols.
	 := getCovPkgMap()
	 := &emitState{
		counterlist: ,
		pkgmap:      ,
		outdir:      ,
		debug:       os.Getenv("GOCOVERDEBUG") != "",
	}

	// Open output file.
	if  := .openOutputFiles(finalHash, finalMetaLen, counterDataFile);  != nil {
		return 
	}
	if .cf == nil {
		return fmt.Errorf("counter data output file open failed (no additional info")
	}

	// Emit counter data file.
	if  := .emitCounterDataFile(finalHash, .cf);  != nil {
		return 
	}
	if  := .cf.Close();  != nil {
		return fmt.Errorf("closing counter data file: %v", )
	}

	// Counter file has now been closed. Rename the temp to the
	// final desired path.
	if  := os.Rename(.cftmp, .cfname);  != nil {
		return fmt.Errorf("writing %s: rename from %s failed: %v\n", .cfname, .cftmp, )
	}

	return nil
}

// emitCounterDataToWriter emits counter data for this coverage run to an io.Writer.
func ( *emitState) ( io.Writer) error {
	if  := .emitCounterDataFile(finalHash, );  != nil {
		return 
	}
	return nil
}

// openMetaFile determines whether we need to emit a meta-data output
// file, or whether we can reuse the existing file in the coverage out
// dir. It updates mfname/mftmp/mf fields in 's', returning an error
// if something went wrong. See the comment on the emitState type
// definition above for more on how file opening is managed.
func ( *emitState) ( [16]byte,  uint64) error {

	// Open meta-outfile for reading to see if it exists.
	 := fmt.Sprintf("%s.%x", coverage.MetaFilePref, )
	.mfname = filepath.Join(.outdir, )
	,  := os.Stat(.mfname)
	if  != nil || .Size() != int64() {
		// We need a new meta-file.
		 := "tmp." +  + strconv.FormatInt(time.Now().UnixNano(), 10)
		.mftmp = filepath.Join(.outdir, )
		.mf,  = os.Create(.mftmp)
		if  != nil {
			return fmt.Errorf("creating meta-data file %s: %v", .mftmp, )
		}
	}
	return nil
}

// openCounterFile opens an output file for the counter data portion
// of a test coverage run. If updates the 'cfname' and 'cf' fields in
// 's', returning an error if something went wrong.
func ( *emitState) ( [16]byte) error {
	 := os.Getpid()
	 := fmt.Sprintf(coverage.CounterFileTempl, coverage.CounterFilePref, , , time.Now().UnixNano())
	.cfname = filepath.Join(.outdir, )
	.cftmp = filepath.Join(.outdir, "tmp."+)
	var  error
	.cf,  = os.Create(.cftmp)
	if  != nil {
		return fmt.Errorf("creating counter data file %s: %v", .cftmp, )
	}
	return nil
}

// openOutputFiles opens output files in preparation for emitting
// coverage data. In the case of the meta-data file, openOutputFiles
// may determine that we can reuse an existing meta-data file in the
// outdir, in which case it will leave the 'mf' field in the state
// struct as nil. If a new meta-file is needed, the field 'mfname'
// will be the final desired path of the meta file, 'mftmp' will be a
// temporary file, and 'mf' will be an open os.File pointer for
// 'mftmp'. The idea is that the client/caller will write content into
// 'mf', close it, and then rename 'mftmp' to 'mfname'. This function
// also opens the counter data output file, setting 'cf' and 'cfname'
// in the state struct.
func ( *emitState) ( [16]byte,  uint64,  fileType) error {
	,  := os.Stat(.outdir)
	if  != nil {
		return fmt.Errorf("output directory %q inaccessible (err: %v); no coverage data written", .outdir, )
	}
	if !.IsDir() {
		return fmt.Errorf("output directory %q not a directory; no coverage data written", .outdir)
	}

	if ( & metaDataFile) != 0 {
		if  := .openMetaFile(, );  != nil {
			return 
		}
	}
	if ( & counterDataFile) != 0 {
		if  := .openCounterFile();  != nil {
			return 
		}
	}
	return nil
}

// emitMetaDataFile emits coverage meta-data to a previously opened
// temporary file (s.mftmp), then renames the generated file to the
// final path (s.mfname).
func ( *emitState) ( [16]byte,  uint64) error {
	if  := writeMetaData(.mf, .metalist, cmode, cgran, );  != nil {
		return fmt.Errorf("writing %s: %v\n", .mftmp, )
	}
	if  := .mf.Close();  != nil {
		return fmt.Errorf("closing meta data temp file: %v", )
	}

	// Temp file has now been flushed and closed. Rename the temp to the
	// final desired path.
	if  := os.Rename(.mftmp, .mfname);  != nil {
		return fmt.Errorf("writing %s: rename from %s failed: %v\n", .mfname, .mftmp, )
	}

	return nil
}

// needMetaDataFile returns TRUE if we need to emit a meta-data file
// for this program run. It should be used only after
// openOutputFiles() has been invoked.
func ( *emitState) () bool {
	return .mf != nil
}

func writeMetaData( io.Writer,  []rtcov.CovMetaBlob,  coverage.CounterMode,  coverage.CounterGranularity,  [16]byte) error {
	 := encodemeta.NewCoverageMetaFileWriter("<io.Writer>", )

	var  [][]byte
	for ,  := range  {
		 := unsafe.Slice(.P, int(.Len))
		 = append(, )
	}
	return .Write(, , , )
}

func ( *emitState) ( encodecounter.CounterVisitorFn) error {
	var  []uint32

	 := func( []atomic.Uint32,  []uint32) []uint32 {
		 = [:0]
		for  := range  {
			 = append(, [].Load())
		}
		return 
	}

	 := uint32(0)
	for ,  := range .counterlist {
		 := unsafe.Slice((*atomic.Uint32)(unsafe.Pointer(.Counters)), int(.Len))
		for  := 0;  < len(); ++ {
			// Skip ahead until the next non-zero value.
			 := [].Load()
			if  == 0 {
				continue
			}

			// We found a function that was executed.
			 := [+coverage.NumCtrsOffset].Load()
			 := [+coverage.PkgIdOffset].Load()
			 := [+coverage.FuncIdOffset].Load()
			 :=  + coverage.FirstCtrOffset
			 := [ : +int()]

			// Check to make sure that we have at least one live
			// counter. See the implementation note in ClearCoverageCounters
			// for a description of why this is needed.
			 := false
			for  := 0;  < len(); ++ {
				if [].Load() != 0 {
					 = true
					break
				}
			}
			if ! {
				// Skip this function.
				 += coverage.FirstCtrOffset + int() - 1
				continue
			}

			if .debug {
				if  !=  {
					 = 
					fmt.Fprintf(os.Stderr, "\n=+= %d: pk=%d visit live fcn",
						, )
				}
				fmt.Fprintf(os.Stderr, " {i=%d F%d NC%d}", , , )
			}

			// Vet and/or fix up package ID. A package ID of zero
			// indicates that there is some new package X that is a
			// runtime dependency, and this package has code that
			// executes before its corresponding init package runs.
			// This is a fatal error that we should only see during
			// Go development (e.g. tip).
			 := int32()
			if  == 0 {
				fmt.Fprintf(os.Stderr, "\n")
				reportErrorInHardcodedList(int32(), , , )
			} else if  < 0 {
				if ,  := .pkgmap[int()];  {
					 = uint32()
				} else {
					fmt.Fprintf(os.Stderr, "\n")
					reportErrorInHardcodedList(int32(), , , )
				}
			} else {
				// The package ID value stored in the counter array
				// has 1 added to it (so as to preclude the
				// possibility of a zero value ; see
				// runtime.addCovMeta), so subtract off 1 here to form
				// the real package ID.
				--
			}

			 = (, )
			if  := (, , );  != nil {
				return 
			}

			// Skip over this function.
			 += coverage.FirstCtrOffset + int() - 1
		}
		if .debug {
			fmt.Fprintf(os.Stderr, "\n")
		}
	}
	return nil
}

// captureOsArgs converts os.Args() into the format we use to store
// this info in the counter data file (counter data file "args"
// section is a generic key-value collection). See the 'args' section
// in internal/coverage/defs.go for more info. The args map
// is also used to capture GOOS + GOARCH values as well.
func captureOsArgs() map[string]string {
	 := make(map[string]string)
	["argc"] = strconv.Itoa(len(os.Args))
	for ,  := range os.Args {
		[fmt.Sprintf("argv%d", )] = 
	}
	["GOOS"] = runtime.GOOS
	["GOARCH"] = runtime.GOARCH
	return 
}

// emitCounterDataFile emits the counter data portion of a
// coverage output file (to the file 's.cf').
func ( *emitState) ( [16]byte,  io.Writer) error {
	 := encodecounter.NewCoverageDataWriter(, coverage.CtrULeb128)
	if  := .Write(, capturedOsArgs, );  != nil {
		return 
	}
	return nil
}

// markProfileEmitted signals the runtime/coverage machinery that
// coverage data output files have already been written out, and there
// is no need to take any additional action at exit time. This
// function is called (via linknamed reference) from the
// coverage-related boilerplate code in _testmain.go emitted for go
// unit tests.
func markProfileEmitted( bool) {
	covProfileAlreadyEmitted = 
}

func reportErrorInHardcodedList(,  int32, ,  uint32) {
	 := getCovMetaList()
	 := getCovPkgMap()

	println("internal error in coverage meta-data tracking:")
	println("encountered bad pkgID:", , " at slot:", ,
		" fnID:", , " numCtrs:", )
	println("list of hard-coded runtime package IDs needs revising.")
	println("[see the comment on the 'rtPkgs' var in ")
	println(" <goroot>/src/internal/coverage/pkid.go]")
	println("registered list:")
	for ,  := range  {
		print("slot: ", , " path='", .PkgPath, "' ")
		if .PkgID != -1 {
			print(" hard-coded id: ", .PkgID)
		}
		println("")
	}
	println("remap table:")
	for ,  := range  {
		println("from ", , " to ", )
	}
}