Source file src/internal/coverage/encodemeta/encode.go

     1  // Copyright 2021 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 encodemeta
     6  
     7  // This package contains APIs and helpers for encoding the meta-data
     8  // "blob" for a single Go package, created when coverage
     9  // instrumentation is turned on.
    10  
    11  import (
    12  	"bytes"
    13  	"crypto/md5"
    14  	"encoding/binary"
    15  	"fmt"
    16  	"hash"
    17  	"internal/coverage"
    18  	"internal/coverage/stringtab"
    19  	"internal/coverage/uleb128"
    20  	"io"
    21  	"os"
    22  )
    23  
    24  type CoverageMetaDataBuilder struct {
    25  	stab    stringtab.Writer
    26  	funcs   []funcDesc
    27  	tmp     []byte // temp work slice
    28  	h       hash.Hash
    29  	pkgpath uint32
    30  	pkgname uint32
    31  	modpath uint32
    32  	debug   bool
    33  	werr    error
    34  }
    35  
    36  func NewCoverageMetaDataBuilder(pkgpath string, pkgname string, modulepath string) (*CoverageMetaDataBuilder, error) {
    37  	if pkgpath == "" {
    38  		return nil, fmt.Errorf("invalid empty package path")
    39  	}
    40  	x := &CoverageMetaDataBuilder{
    41  		tmp: make([]byte, 0, 256),
    42  		h:   md5.New(),
    43  	}
    44  	x.stab.InitWriter()
    45  	x.stab.Lookup("")
    46  	x.pkgpath = x.stab.Lookup(pkgpath)
    47  	x.pkgname = x.stab.Lookup(pkgname)
    48  	x.modpath = x.stab.Lookup(modulepath)
    49  	io.WriteString(x.h, pkgpath)
    50  	io.WriteString(x.h, pkgname)
    51  	io.WriteString(x.h, modulepath)
    52  	return x, nil
    53  }
    54  
    55  func h32(x uint32, h hash.Hash, tmp []byte) {
    56  	tmp = tmp[:0]
    57  	tmp = append(tmp, []byte{0, 0, 0, 0}...)
    58  	binary.LittleEndian.PutUint32(tmp, x)
    59  	h.Write(tmp)
    60  }
    61  
    62  type funcDesc struct {
    63  	encoded []byte
    64  }
    65  
    66  // AddFunc registers a new function with the meta data builder.
    67  func (b *CoverageMetaDataBuilder) AddFunc(f coverage.FuncDesc) uint {
    68  	hashFuncDesc(b.h, &f, b.tmp)
    69  	fd := funcDesc{}
    70  	b.tmp = b.tmp[:0]
    71  	b.tmp = uleb128.AppendUleb128(b.tmp, uint(len(f.Units)))
    72  	b.tmp = uleb128.AppendUleb128(b.tmp, uint(b.stab.Lookup(f.Funcname)))
    73  	b.tmp = uleb128.AppendUleb128(b.tmp, uint(b.stab.Lookup(f.Srcfile)))
    74  	for _, u := range f.Units {
    75  		b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.StLine))
    76  		b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.StCol))
    77  		b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.EnLine))
    78  		b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.EnCol))
    79  		b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.NxStmts))
    80  	}
    81  	lit := uint(0)
    82  	if f.Lit {
    83  		lit = 1
    84  	}
    85  	b.tmp = uleb128.AppendUleb128(b.tmp, lit)
    86  	fd.encoded = bytes.Clone(b.tmp)
    87  	rv := uint(len(b.funcs))
    88  	b.funcs = append(b.funcs, fd)
    89  	return rv
    90  }
    91  
    92  func (b *CoverageMetaDataBuilder) emitFuncOffsets(w io.WriteSeeker, off int64) int64 {
    93  	nFuncs := len(b.funcs)
    94  	var foff int64 = coverage.CovMetaHeaderSize + int64(b.stab.Size()) + int64(nFuncs)*4
    95  	for idx := 0; idx < nFuncs; idx++ {
    96  		b.wrUint32(w, uint32(foff))
    97  		foff += int64(len(b.funcs[idx].encoded))
    98  	}
    99  	return off + (int64(len(b.funcs)) * 4)
   100  }
   101  
   102  func (b *CoverageMetaDataBuilder) emitFunc(w io.WriteSeeker, off int64, f funcDesc) (int64, error) {
   103  	ew := len(f.encoded)
   104  	if nw, err := w.Write(f.encoded); err != nil {
   105  		return 0, err
   106  	} else if ew != nw {
   107  		return 0, fmt.Errorf("short write emitting coverage meta-data")
   108  	}
   109  	return off + int64(ew), nil
   110  }
   111  
   112  func (b *CoverageMetaDataBuilder) reportWriteError(err error) {
   113  	if b.werr != nil {
   114  		b.werr = err
   115  	}
   116  }
   117  
   118  func (b *CoverageMetaDataBuilder) wrUint32(w io.WriteSeeker, v uint32) {
   119  	b.tmp = b.tmp[:0]
   120  	b.tmp = append(b.tmp, []byte{0, 0, 0, 0}...)
   121  	binary.LittleEndian.PutUint32(b.tmp, v)
   122  	if nw, err := w.Write(b.tmp); err != nil {
   123  		b.reportWriteError(err)
   124  	} else if nw != 4 {
   125  		b.reportWriteError(fmt.Errorf("short write"))
   126  	}
   127  }
   128  
   129  // Emit writes the meta-data accumulated so far in this builder to 'w'.
   130  // Returns a hash of the meta-data payload and an error.
   131  func (b *CoverageMetaDataBuilder) Emit(w io.WriteSeeker) ([16]byte, error) {
   132  	// Emit header.  Length will initially be zero, we'll
   133  	// back-patch it later.
   134  	var digest [16]byte
   135  	copy(digest[:], b.h.Sum(nil))
   136  	mh := coverage.MetaSymbolHeader{
   137  		// hash and length initially zero, will be back-patched
   138  		PkgPath:    uint32(b.pkgpath),
   139  		PkgName:    uint32(b.pkgname),
   140  		ModulePath: uint32(b.modpath),
   141  		NumFiles:   uint32(b.stab.Nentries()),
   142  		NumFuncs:   uint32(len(b.funcs)),
   143  		MetaHash:   digest,
   144  	}
   145  	if b.debug {
   146  		fmt.Fprintf(os.Stderr, "=-= writing header: %+v\n", mh)
   147  	}
   148  	if err := binary.Write(w, binary.LittleEndian, mh); err != nil {
   149  		return digest, fmt.Errorf("error writing meta-file header: %v", err)
   150  	}
   151  	off := int64(coverage.CovMetaHeaderSize)
   152  
   153  	// Write function offsets section
   154  	off = b.emitFuncOffsets(w, off)
   155  
   156  	// Check for any errors up to this point.
   157  	if b.werr != nil {
   158  		return digest, b.werr
   159  	}
   160  
   161  	// Write string table.
   162  	if err := b.stab.Write(w); err != nil {
   163  		return digest, err
   164  	}
   165  	off += int64(b.stab.Size())
   166  
   167  	// Write functions
   168  	for _, f := range b.funcs {
   169  		var err error
   170  		off, err = b.emitFunc(w, off, f)
   171  		if err != nil {
   172  			return digest, err
   173  		}
   174  	}
   175  
   176  	// Back-patch the length.
   177  	totalLength := uint32(off)
   178  	if _, err := w.Seek(0, io.SeekStart); err != nil {
   179  		return digest, err
   180  	}
   181  	b.wrUint32(w, totalLength)
   182  	if b.werr != nil {
   183  		return digest, b.werr
   184  	}
   185  	return digest, nil
   186  }
   187  
   188  // HashFuncDesc computes an md5 sum of a coverage.FuncDesc and returns
   189  // a digest for it.
   190  func HashFuncDesc(f *coverage.FuncDesc) [16]byte {
   191  	h := md5.New()
   192  	tmp := make([]byte, 0, 32)
   193  	hashFuncDesc(h, f, tmp)
   194  	var r [16]byte
   195  	copy(r[:], h.Sum(nil))
   196  	return r
   197  }
   198  
   199  // hashFuncDesc incorporates a given function 'f' into the hash 'h'.
   200  func hashFuncDesc(h hash.Hash, f *coverage.FuncDesc, tmp []byte) {
   201  	io.WriteString(h, f.Funcname)
   202  	io.WriteString(h, f.Srcfile)
   203  	for _, u := range f.Units {
   204  		h32(u.StLine, h, tmp)
   205  		h32(u.StCol, h, tmp)
   206  		h32(u.EnLine, h, tmp)
   207  		h32(u.EnCol, h, tmp)
   208  		h32(u.NxStmts, h, tmp)
   209  	}
   210  	lit := uint32(0)
   211  	if f.Lit {
   212  		lit = 1
   213  	}
   214  	h32(lit, h, tmp)
   215  }
   216  

View as plain text