summaryrefslogtreecommitdiffstats
path: root/src/internal/coverage/encodemeta
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
commitccd992355df7192993c666236047820244914598 (patch)
treef00fea65147227b7743083c6148396f74cd66935 /src/internal/coverage/encodemeta
parentInitial commit. (diff)
downloadgolang-1.21-ccd992355df7192993c666236047820244914598.tar.xz
golang-1.21-ccd992355df7192993c666236047820244914598.zip
Adding upstream version 1.21.8.upstream/1.21.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/internal/coverage/encodemeta')
-rw-r--r--src/internal/coverage/encodemeta/encode.go215
-rw-r--r--src/internal/coverage/encodemeta/encodefile.go132
2 files changed, 347 insertions, 0 deletions
diff --git a/src/internal/coverage/encodemeta/encode.go b/src/internal/coverage/encodemeta/encode.go
new file mode 100644
index 0000000..d211c7c
--- /dev/null
+++ b/src/internal/coverage/encodemeta/encode.go
@@ -0,0 +1,215 @@
+// Copyright 2021 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 encodemeta
+
+// This package contains APIs and helpers for encoding the meta-data
+// "blob" for a single Go package, created when coverage
+// instrumentation is turned on.
+
+import (
+ "bytes"
+ "crypto/md5"
+ "encoding/binary"
+ "fmt"
+ "hash"
+ "internal/coverage"
+ "internal/coverage/stringtab"
+ "internal/coverage/uleb128"
+ "io"
+ "os"
+)
+
+type CoverageMetaDataBuilder struct {
+ stab stringtab.Writer
+ funcs []funcDesc
+ tmp []byte // temp work slice
+ h hash.Hash
+ pkgpath uint32
+ pkgname uint32
+ modpath uint32
+ debug bool
+ werr error
+}
+
+func NewCoverageMetaDataBuilder(pkgpath string, pkgname string, modulepath string) (*CoverageMetaDataBuilder, error) {
+ if pkgpath == "" {
+ return nil, fmt.Errorf("invalid empty package path")
+ }
+ x := &CoverageMetaDataBuilder{
+ tmp: make([]byte, 0, 256),
+ h: md5.New(),
+ }
+ x.stab.InitWriter()
+ x.stab.Lookup("")
+ x.pkgpath = x.stab.Lookup(pkgpath)
+ x.pkgname = x.stab.Lookup(pkgname)
+ x.modpath = x.stab.Lookup(modulepath)
+ io.WriteString(x.h, pkgpath)
+ io.WriteString(x.h, pkgname)
+ io.WriteString(x.h, modulepath)
+ return x, nil
+}
+
+func h32(x uint32, h hash.Hash, tmp []byte) {
+ tmp = tmp[:0]
+ tmp = append(tmp, []byte{0, 0, 0, 0}...)
+ binary.LittleEndian.PutUint32(tmp, x)
+ h.Write(tmp)
+}
+
+type funcDesc struct {
+ encoded []byte
+}
+
+// AddFunc registers a new function with the meta data builder.
+func (b *CoverageMetaDataBuilder) AddFunc(f coverage.FuncDesc) uint {
+ hashFuncDesc(b.h, &f, b.tmp)
+ fd := funcDesc{}
+ b.tmp = b.tmp[:0]
+ b.tmp = uleb128.AppendUleb128(b.tmp, uint(len(f.Units)))
+ b.tmp = uleb128.AppendUleb128(b.tmp, uint(b.stab.Lookup(f.Funcname)))
+ b.tmp = uleb128.AppendUleb128(b.tmp, uint(b.stab.Lookup(f.Srcfile)))
+ for _, u := range f.Units {
+ b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.StLine))
+ b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.StCol))
+ b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.EnLine))
+ b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.EnCol))
+ b.tmp = uleb128.AppendUleb128(b.tmp, uint(u.NxStmts))
+ }
+ lit := uint(0)
+ if f.Lit {
+ lit = 1
+ }
+ b.tmp = uleb128.AppendUleb128(b.tmp, lit)
+ fd.encoded = bytes.Clone(b.tmp)
+ rv := uint(len(b.funcs))
+ b.funcs = append(b.funcs, fd)
+ return rv
+}
+
+func (b *CoverageMetaDataBuilder) emitFuncOffsets(w io.WriteSeeker, off int64) int64 {
+ nFuncs := len(b.funcs)
+ var foff int64 = coverage.CovMetaHeaderSize + int64(b.stab.Size()) + int64(nFuncs)*4
+ for idx := 0; idx < nFuncs; idx++ {
+ b.wrUint32(w, uint32(foff))
+ foff += int64(len(b.funcs[idx].encoded))
+ }
+ return off + (int64(len(b.funcs)) * 4)
+}
+
+func (b *CoverageMetaDataBuilder) emitFunc(w io.WriteSeeker, off int64, f funcDesc) (int64, error) {
+ ew := len(f.encoded)
+ if nw, err := w.Write(f.encoded); err != nil {
+ return 0, err
+ } else if ew != nw {
+ return 0, fmt.Errorf("short write emitting coverage meta-data")
+ }
+ return off + int64(ew), nil
+}
+
+func (b *CoverageMetaDataBuilder) reportWriteError(err error) {
+ if b.werr != nil {
+ b.werr = err
+ }
+}
+
+func (b *CoverageMetaDataBuilder) wrUint32(w io.WriteSeeker, v uint32) {
+ b.tmp = b.tmp[:0]
+ b.tmp = append(b.tmp, []byte{0, 0, 0, 0}...)
+ binary.LittleEndian.PutUint32(b.tmp, v)
+ if nw, err := w.Write(b.tmp); err != nil {
+ b.reportWriteError(err)
+ } else if nw != 4 {
+ b.reportWriteError(fmt.Errorf("short write"))
+ }
+}
+
+// Emit writes the meta-data accumulated so far in this builder to 'w'.
+// Returns a hash of the meta-data payload and an error.
+func (b *CoverageMetaDataBuilder) Emit(w io.WriteSeeker) ([16]byte, error) {
+ // Emit header. Length will initially be zero, we'll
+ // back-patch it later.
+ var digest [16]byte
+ copy(digest[:], b.h.Sum(nil))
+ mh := coverage.MetaSymbolHeader{
+ // hash and length initially zero, will be back-patched
+ PkgPath: uint32(b.pkgpath),
+ PkgName: uint32(b.pkgname),
+ ModulePath: uint32(b.modpath),
+ NumFiles: uint32(b.stab.Nentries()),
+ NumFuncs: uint32(len(b.funcs)),
+ MetaHash: digest,
+ }
+ if b.debug {
+ fmt.Fprintf(os.Stderr, "=-= writing header: %+v\n", mh)
+ }
+ if err := binary.Write(w, binary.LittleEndian, mh); err != nil {
+ return digest, fmt.Errorf("error writing meta-file header: %v", err)
+ }
+ off := int64(coverage.CovMetaHeaderSize)
+
+ // Write function offsets section
+ off = b.emitFuncOffsets(w, off)
+
+ // Check for any errors up to this point.
+ if b.werr != nil {
+ return digest, b.werr
+ }
+
+ // Write string table.
+ if err := b.stab.Write(w); err != nil {
+ return digest, err
+ }
+ off += int64(b.stab.Size())
+
+ // Write functions
+ for _, f := range b.funcs {
+ var err error
+ off, err = b.emitFunc(w, off, f)
+ if err != nil {
+ return digest, err
+ }
+ }
+
+ // Back-patch the length.
+ totalLength := uint32(off)
+ if _, err := w.Seek(0, io.SeekStart); err != nil {
+ return digest, err
+ }
+ b.wrUint32(w, totalLength)
+ if b.werr != nil {
+ return digest, b.werr
+ }
+ return digest, nil
+}
+
+// HashFuncDesc computes an md5 sum of a coverage.FuncDesc and returns
+// a digest for it.
+func HashFuncDesc(f *coverage.FuncDesc) [16]byte {
+ h := md5.New()
+ tmp := make([]byte, 0, 32)
+ hashFuncDesc(h, f, tmp)
+ var r [16]byte
+ copy(r[:], h.Sum(nil))
+ return r
+}
+
+// hashFuncDesc incorporates a given function 'f' into the hash 'h'.
+func hashFuncDesc(h hash.Hash, f *coverage.FuncDesc, tmp []byte) {
+ io.WriteString(h, f.Funcname)
+ io.WriteString(h, f.Srcfile)
+ for _, u := range f.Units {
+ h32(u.StLine, h, tmp)
+ h32(u.StCol, h, tmp)
+ h32(u.EnLine, h, tmp)
+ h32(u.EnCol, h, tmp)
+ h32(u.NxStmts, h, tmp)
+ }
+ lit := uint32(0)
+ if f.Lit {
+ lit = 1
+ }
+ h32(lit, h, tmp)
+}
diff --git a/src/internal/coverage/encodemeta/encodefile.go b/src/internal/coverage/encodemeta/encodefile.go
new file mode 100644
index 0000000..38ae46e
--- /dev/null
+++ b/src/internal/coverage/encodemeta/encodefile.go
@@ -0,0 +1,132 @@
+// Copyright 2021 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 encodemeta
+
+import (
+ "bufio"
+ "crypto/md5"
+ "encoding/binary"
+ "fmt"
+ "internal/coverage"
+ "internal/coverage/stringtab"
+ "io"
+ "os"
+ "unsafe"
+)
+
+// This package contains APIs and helpers for writing out a meta-data
+// file (composed of a file header, offsets/lengths, and then a series of
+// meta-data blobs emitted by the compiler, one per Go package).
+
+type CoverageMetaFileWriter struct {
+ stab stringtab.Writer
+ mfname string
+ w *bufio.Writer
+ tmp []byte
+ debug bool
+}
+
+func NewCoverageMetaFileWriter(mfname string, w io.Writer) *CoverageMetaFileWriter {
+ r := &CoverageMetaFileWriter{
+ mfname: mfname,
+ w: bufio.NewWriter(w),
+ tmp: make([]byte, 64),
+ }
+ r.stab.InitWriter()
+ r.stab.Lookup("")
+ return r
+}
+
+func (m *CoverageMetaFileWriter) Write(finalHash [16]byte, blobs [][]byte, mode coverage.CounterMode, granularity coverage.CounterGranularity) error {
+ mhsz := uint64(unsafe.Sizeof(coverage.MetaFileHeader{}))
+ stSize := m.stab.Size()
+ stOffset := mhsz + uint64(16*len(blobs))
+ preambleLength := stOffset + uint64(stSize)
+
+ if m.debug {
+ fmt.Fprintf(os.Stderr, "=+= sizeof(MetaFileHeader)=%d\n", mhsz)
+ fmt.Fprintf(os.Stderr, "=+= preambleLength=%d stSize=%d\n", preambleLength, stSize)
+ }
+
+ // Compute total size
+ tlen := preambleLength
+ for i := 0; i < len(blobs); i++ {
+ tlen += uint64(len(blobs[i]))
+ }
+
+ // Emit header
+ mh := coverage.MetaFileHeader{
+ Magic: coverage.CovMetaMagic,
+ Version: coverage.MetaFileVersion,
+ TotalLength: tlen,
+ Entries: uint64(len(blobs)),
+ MetaFileHash: finalHash,
+ StrTabOffset: uint32(stOffset),
+ StrTabLength: stSize,
+ CMode: mode,
+ CGranularity: granularity,
+ }
+ var err error
+ if err = binary.Write(m.w, binary.LittleEndian, mh); err != nil {
+ return fmt.Errorf("error writing %s: %v", m.mfname, err)
+ }
+
+ if m.debug {
+ fmt.Fprintf(os.Stderr, "=+= len(blobs) is %d\n", mh.Entries)
+ }
+
+ // Emit package offsets section followed by package lengths section.
+ off := preambleLength
+ off2 := mhsz
+ buf := make([]byte, 8)
+ for _, blob := range blobs {
+ binary.LittleEndian.PutUint64(buf, off)
+ if _, err = m.w.Write(buf); err != nil {
+ return fmt.Errorf("error writing %s: %v", m.mfname, err)
+ }
+ if m.debug {
+ fmt.Fprintf(os.Stderr, "=+= pkg offset %d 0x%x\n", off, off)
+ }
+ off += uint64(len(blob))
+ off2 += 8
+ }
+ for _, blob := range blobs {
+ bl := uint64(len(blob))
+ binary.LittleEndian.PutUint64(buf, bl)
+ if _, err = m.w.Write(buf); err != nil {
+ return fmt.Errorf("error writing %s: %v", m.mfname, err)
+ }
+ if m.debug {
+ fmt.Fprintf(os.Stderr, "=+= pkg len %d 0x%x\n", bl, bl)
+ }
+ off2 += 8
+ }
+
+ // Emit string table
+ if err = m.stab.Write(m.w); err != nil {
+ return err
+ }
+
+ // Now emit blobs themselves.
+ for k, blob := range blobs {
+ if m.debug {
+ fmt.Fprintf(os.Stderr, "=+= writing blob %d len %d at off=%d hash %s\n", k, len(blob), off2, fmt.Sprintf("%x", md5.Sum(blob)))
+ }
+ if _, err = m.w.Write(blob); err != nil {
+ return fmt.Errorf("error writing %s: %v", m.mfname, err)
+ }
+ if m.debug {
+ fmt.Fprintf(os.Stderr, "=+= wrote package payload of %d bytes\n",
+ len(blob))
+ }
+ off2 += uint64(len(blob))
+ }
+
+ // Flush writer, and we're done.
+ if err = m.w.Flush(); err != nil {
+ return fmt.Errorf("error writing %s: %v", m.mfname, err)
+ }
+ return nil
+}