github.com/bir3/gocompiler@v0.9.2202/src/internal/coverage/decodemeta/decodefile.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 decodemeta 6 7 // This package contains APIs and helpers for reading and decoding 8 // meta-data output files emitted by the runtime when a 9 // coverage-instrumented binary executes. A meta-data file contains 10 // top-level info (counter mode, number of packages) and then a 11 // separate self-contained meta-data section for each Go package. 12 13 import ( 14 "bufio" 15 "crypto/md5" 16 "encoding/binary" 17 "fmt" 18 "github.com/bir3/gocompiler/src/internal/coverage" 19 "github.com/bir3/gocompiler/src/internal/coverage/slicereader" 20 "github.com/bir3/gocompiler/src/internal/coverage/stringtab" 21 "io" 22 "os" 23 ) 24 25 // CoverageMetaFileReader provides state and methods for reading 26 // a meta-data file from a code coverage run. 27 type CoverageMetaFileReader struct { 28 f *os.File 29 hdr coverage.MetaFileHeader 30 tmp []byte 31 pkgOffsets []uint64 32 pkgLengths []uint64 33 strtab *stringtab.Reader 34 fileRdr *bufio.Reader 35 fileView []byte 36 debug bool 37 } 38 39 // NewCoverageMetaFileReader returns a new helper object for reading 40 // the coverage meta-data output file 'f'. The param 'fileView' is a 41 // read-only slice containing the contents of 'f' obtained by mmap'ing 42 // the file read-only; 'fileView' may be nil, in which case the helper 43 // will read the contents of the file using regular file Read 44 // operations. 45 func NewCoverageMetaFileReader(f *os.File, fileView []byte) (*CoverageMetaFileReader, error) { 46 r := &CoverageMetaFileReader{ 47 f: f, 48 fileView: fileView, 49 tmp: make([]byte, 256), 50 } 51 52 if err := r.readFileHeader(); err != nil { 53 return nil, err 54 } 55 return r, nil 56 } 57 58 func (r *CoverageMetaFileReader) readFileHeader() error { 59 var err error 60 61 r.fileRdr = bufio.NewReader(r.f) 62 63 // Read file header. 64 if err := binary.Read(r.fileRdr, binary.LittleEndian, &r.hdr); err != nil { 65 return err 66 } 67 68 // Verify magic string 69 m := r.hdr.Magic 70 g := coverage.CovMetaMagic 71 if m[0] != g[0] || m[1] != g[1] || m[2] != g[2] || m[3] != g[3] { 72 return fmt.Errorf("invalid meta-data file magic string") 73 } 74 75 // Vet the version. If this is a meta-data file from the future, 76 // we won't be able to read it. 77 if r.hdr.Version > coverage.MetaFileVersion { 78 return fmt.Errorf("meta-data file withn unknown version %d (expected %d)", r.hdr.Version, coverage.MetaFileVersion) 79 } 80 81 // Read package offsets for good measure 82 r.pkgOffsets = make([]uint64, r.hdr.Entries) 83 for i := uint64(0); i < r.hdr.Entries; i++ { 84 if r.pkgOffsets[i], err = r.rdUint64(); err != nil { 85 return err 86 } 87 if r.pkgOffsets[i] > r.hdr.TotalLength { 88 return fmt.Errorf("insane pkg offset %d: %d > totlen %d", 89 i, r.pkgOffsets[i], r.hdr.TotalLength) 90 } 91 } 92 r.pkgLengths = make([]uint64, r.hdr.Entries) 93 for i := uint64(0); i < r.hdr.Entries; i++ { 94 if r.pkgLengths[i], err = r.rdUint64(); err != nil { 95 return err 96 } 97 if r.pkgLengths[i] > r.hdr.TotalLength { 98 return fmt.Errorf("insane pkg length %d: %d > totlen %d", 99 i, r.pkgLengths[i], r.hdr.TotalLength) 100 } 101 } 102 103 // Read string table. 104 b := make([]byte, r.hdr.StrTabLength) 105 nr, err := r.fileRdr.Read(b) 106 if err != nil { 107 return err 108 } 109 if nr != int(r.hdr.StrTabLength) { 110 return fmt.Errorf("error: short read on string table") 111 } 112 slr := slicereader.NewReader(b, false /* not readonly */) 113 r.strtab = stringtab.NewReader(slr) 114 r.strtab.Read() 115 116 if r.debug { 117 fmt.Fprintf(os.Stderr, "=-= read-in header is: %+v\n", *r) 118 } 119 120 return nil 121 } 122 123 func (r *CoverageMetaFileReader) rdUint64() (uint64, error) { 124 r.tmp = r.tmp[:0] 125 r.tmp = append(r.tmp, make([]byte, 8)...) 126 n, err := r.fileRdr.Read(r.tmp) 127 if err != nil { 128 return 0, err 129 } 130 if n != 8 { 131 return 0, fmt.Errorf("premature end of file on read") 132 } 133 v := binary.LittleEndian.Uint64(r.tmp) 134 return v, nil 135 } 136 137 // NumPackages returns the number of packages for which this file 138 // contains meta-data. 139 func (r *CoverageMetaFileReader) NumPackages() uint64 { 140 return r.hdr.Entries 141 } 142 143 // CounterMode returns the counter mode (set, count, atomic) used 144 // when building for coverage for the program that produce this 145 // meta-data file. 146 func (r *CoverageMetaFileReader) CounterMode() coverage.CounterMode { 147 return r.hdr.CMode 148 } 149 150 // CounterGranularity returns the counter granularity (single counter per 151 // function, or counter per block) selected when building for coverage 152 // for the program that produce this meta-data file. 153 func (r *CoverageMetaFileReader) CounterGranularity() coverage.CounterGranularity { 154 return r.hdr.CGranularity 155 } 156 157 // FileHash returns the hash computed for all of the package meta-data 158 // blobs. Coverage counter data files refer to this hash, and the 159 // hash will be encoded into the meta-data file name. 160 func (r *CoverageMetaFileReader) FileHash() [16]byte { 161 return r.hdr.MetaFileHash 162 } 163 164 // GetPackageDecoder requests a decoder object for the package within 165 // the meta-data file whose index is 'pkIdx'. If the 166 // CoverageMetaFileReader was set up with a read-only file view, a 167 // pointer into that file view will be returned, otherwise the buffer 168 // 'payloadbuf' will be written to (or if it is not of sufficient 169 // size, a new buffer will be allocated). Return value is the decoder, 170 // a byte slice with the encoded meta-data, and an error. 171 func (r *CoverageMetaFileReader) GetPackageDecoder(pkIdx uint32, payloadbuf []byte) (*CoverageMetaDataDecoder, []byte, error) { 172 pp, err := r.GetPackagePayload(pkIdx, payloadbuf) 173 if r.debug { 174 fmt.Fprintf(os.Stderr, "=-= pkidx=%d payload length is %d hash=%s\n", 175 pkIdx, len(pp), fmt.Sprintf("%x", md5.Sum(pp))) 176 } 177 if err != nil { 178 return nil, nil, err 179 } 180 mdd, err := NewCoverageMetaDataDecoder(pp, r.fileView != nil) 181 if err != nil { 182 return nil, nil, err 183 } 184 return mdd, pp, nil 185 } 186 187 // GetPackagePayload returns the raw (encoded) meta-data payload for the 188 // package with index 'pkIdx'. As with GetPackageDecoder, if the 189 // CoverageMetaFileReader was set up with a read-only file view, a 190 // pointer into that file view will be returned, otherwise the buffer 191 // 'payloadbuf' will be written to (or if it is not of sufficient 192 // size, a new buffer will be allocated). Return value is the decoder, 193 // a byte slice with the encoded meta-data, and an error. 194 func (r *CoverageMetaFileReader) GetPackagePayload(pkIdx uint32, payloadbuf []byte) ([]byte, error) { 195 196 // Determine correct offset/length. 197 if uint64(pkIdx) >= r.hdr.Entries { 198 return nil, fmt.Errorf("GetPackagePayload: illegal pkg index %d", pkIdx) 199 } 200 off := r.pkgOffsets[pkIdx] 201 len := r.pkgLengths[pkIdx] 202 203 if r.debug { 204 fmt.Fprintf(os.Stderr, "=-= for pk %d, off=%d len=%d\n", pkIdx, off, len) 205 } 206 207 if r.fileView != nil { 208 return r.fileView[off : off+len], nil 209 } 210 211 payload := payloadbuf[:0] 212 if cap(payload) < int(len) { 213 payload = make([]byte, 0, len) 214 } 215 payload = append(payload, make([]byte, len)...) 216 if _, err := r.f.Seek(int64(off), io.SeekStart); err != nil { 217 return nil, err 218 } 219 if _, err := io.ReadFull(r.f, payload); err != nil { 220 return nil, err 221 } 222 return payload, nil 223 }