github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/coverage/encodemeta/encodefile.go (about) 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 import ( 8 "bufio" 9 "crypto/md5" 10 "encoding/binary" 11 "fmt" 12 "io" 13 "os" 14 "unsafe" 15 16 "github.com/go-asm/go/coverage" 17 "github.com/go-asm/go/coverage/stringtab" 18 ) 19 20 // This package contains APIs and helpers for writing out a meta-data 21 // file (composed of a file header, offsets/lengths, and then a series of 22 // meta-data blobs emitted by the compiler, one per Go package). 23 24 type CoverageMetaFileWriter struct { 25 stab stringtab.Writer 26 mfname string 27 w *bufio.Writer 28 tmp []byte 29 debug bool 30 } 31 32 func NewCoverageMetaFileWriter(mfname string, w io.Writer) *CoverageMetaFileWriter { 33 r := &CoverageMetaFileWriter{ 34 mfname: mfname, 35 w: bufio.NewWriter(w), 36 tmp: make([]byte, 64), 37 } 38 r.stab.InitWriter() 39 r.stab.Lookup("") 40 return r 41 } 42 43 func (m *CoverageMetaFileWriter) Write(finalHash [16]byte, blobs [][]byte, mode coverage.CounterMode, granularity coverage.CounterGranularity) error { 44 mhsz := uint64(unsafe.Sizeof(coverage.MetaFileHeader{})) 45 stSize := m.stab.Size() 46 stOffset := mhsz + uint64(16*len(blobs)) 47 preambleLength := stOffset + uint64(stSize) 48 49 if m.debug { 50 fmt.Fprintf(os.Stderr, "=+= sizeof(MetaFileHeader)=%d\n", mhsz) 51 fmt.Fprintf(os.Stderr, "=+= preambleLength=%d stSize=%d\n", preambleLength, stSize) 52 } 53 54 // Compute total size 55 tlen := preambleLength 56 for i := 0; i < len(blobs); i++ { 57 tlen += uint64(len(blobs[i])) 58 } 59 60 // Emit header 61 mh := coverage.MetaFileHeader{ 62 Magic: coverage.CovMetaMagic, 63 Version: coverage.MetaFileVersion, 64 TotalLength: tlen, 65 Entries: uint64(len(blobs)), 66 MetaFileHash: finalHash, 67 StrTabOffset: uint32(stOffset), 68 StrTabLength: stSize, 69 CMode: mode, 70 CGranularity: granularity, 71 } 72 var err error 73 if err = binary.Write(m.w, binary.LittleEndian, mh); err != nil { 74 return fmt.Errorf("error writing %s: %v", m.mfname, err) 75 } 76 77 if m.debug { 78 fmt.Fprintf(os.Stderr, "=+= len(blobs) is %d\n", mh.Entries) 79 } 80 81 // Emit package offsets section followed by package lengths section. 82 off := preambleLength 83 off2 := mhsz 84 buf := make([]byte, 8) 85 for _, blob := range blobs { 86 binary.LittleEndian.PutUint64(buf, off) 87 if _, err = m.w.Write(buf); err != nil { 88 return fmt.Errorf("error writing %s: %v", m.mfname, err) 89 } 90 if m.debug { 91 fmt.Fprintf(os.Stderr, "=+= pkg offset %d 0x%x\n", off, off) 92 } 93 off += uint64(len(blob)) 94 off2 += 8 95 } 96 for _, blob := range blobs { 97 bl := uint64(len(blob)) 98 binary.LittleEndian.PutUint64(buf, bl) 99 if _, err = m.w.Write(buf); err != nil { 100 return fmt.Errorf("error writing %s: %v", m.mfname, err) 101 } 102 if m.debug { 103 fmt.Fprintf(os.Stderr, "=+= pkg len %d 0x%x\n", bl, bl) 104 } 105 off2 += 8 106 } 107 108 // Emit string table 109 if err = m.stab.Write(m.w); err != nil { 110 return err 111 } 112 113 // Now emit blobs themselves. 114 for k, blob := range blobs { 115 if m.debug { 116 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))) 117 } 118 if _, err = m.w.Write(blob); err != nil { 119 return fmt.Errorf("error writing %s: %v", m.mfname, err) 120 } 121 if m.debug { 122 fmt.Fprintf(os.Stderr, "=+= wrote package payload of %d bytes\n", 123 len(blob)) 124 } 125 off2 += uint64(len(blob)) 126 } 127 128 // Flush writer, and we're done. 129 if err = m.w.Flush(); err != nil { 130 return fmt.Errorf("error writing %s: %v", m.mfname, err) 131 } 132 return nil 133 }