github.com/bir3/gocompiler@v0.9.2202/src/internal/coverage/encodemeta/encode.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 // 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 "github.com/bir3/gocompiler/src/internal/coverage" 18 "github.com/bir3/gocompiler/src/internal/coverage/stringtab" 19 "github.com/bir3/gocompiler/src/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 }