github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/coverage/decodemeta/decode.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 decoding a single package's 8 // meta data "blob" emitted by the compiler when coverage instrumentation 9 // is turned on. 10 11 import ( 12 "encoding/binary" 13 "fmt" 14 "io" 15 "os" 16 17 "github.com/go-asm/go/coverage" 18 "github.com/go-asm/go/coverage/slicereader" 19 "github.com/go-asm/go/coverage/stringtab" 20 ) 21 22 // See comments in the encodecovmeta package for details on the format. 23 24 type CoverageMetaDataDecoder struct { 25 r *slicereader.Reader 26 hdr coverage.MetaSymbolHeader 27 strtab *stringtab.Reader 28 tmp []byte 29 debug bool 30 } 31 32 func NewCoverageMetaDataDecoder(b []byte, readonly bool) (*CoverageMetaDataDecoder, error) { 33 slr := slicereader.NewReader(b, readonly) 34 x := &CoverageMetaDataDecoder{ 35 r: slr, 36 tmp: make([]byte, 0, 256), 37 } 38 if err := x.readHeader(); err != nil { 39 return nil, err 40 } 41 if err := x.readStringTable(); err != nil { 42 return nil, err 43 } 44 return x, nil 45 } 46 47 func (d *CoverageMetaDataDecoder) readHeader() error { 48 if err := binary.Read(d.r, binary.LittleEndian, &d.hdr); err != nil { 49 return err 50 } 51 if d.debug { 52 fmt.Fprintf(os.Stderr, "=-= after readHeader: %+v\n", d.hdr) 53 } 54 return nil 55 } 56 57 func (d *CoverageMetaDataDecoder) readStringTable() error { 58 // Seek to the correct location to read the string table. 59 stringTableLocation := int64(coverage.CovMetaHeaderSize + 4*d.hdr.NumFuncs) 60 if _, err := d.r.Seek(stringTableLocation, io.SeekStart); err != nil { 61 return err 62 } 63 64 // Read the table itself. 65 d.strtab = stringtab.NewReader(d.r) 66 d.strtab.Read() 67 return nil 68 } 69 70 func (d *CoverageMetaDataDecoder) PackagePath() string { 71 return d.strtab.Get(d.hdr.PkgPath) 72 } 73 74 func (d *CoverageMetaDataDecoder) PackageName() string { 75 return d.strtab.Get(d.hdr.PkgName) 76 } 77 78 func (d *CoverageMetaDataDecoder) ModulePath() string { 79 return d.strtab.Get(d.hdr.ModulePath) 80 } 81 82 func (d *CoverageMetaDataDecoder) NumFuncs() uint32 { 83 return d.hdr.NumFuncs 84 } 85 86 // ReadFunc reads the coverage meta-data for the function with index 87 // 'findex', filling it into the FuncDesc pointed to by 'f'. 88 func (d *CoverageMetaDataDecoder) ReadFunc(fidx uint32, f *coverage.FuncDesc) error { 89 if fidx >= d.hdr.NumFuncs { 90 return fmt.Errorf("illegal function index") 91 } 92 93 // Seek to the correct location to read the function offset and read it. 94 funcOffsetLocation := int64(coverage.CovMetaHeaderSize + 4*fidx) 95 if _, err := d.r.Seek(funcOffsetLocation, io.SeekStart); err != nil { 96 return err 97 } 98 foff := d.r.ReadUint32() 99 100 // Check assumptions 101 if foff < uint32(funcOffsetLocation) || foff > d.hdr.Length { 102 return fmt.Errorf("malformed func offset %d", foff) 103 } 104 105 // Seek to the correct location to read the function. 106 floc := int64(foff) 107 if _, err := d.r.Seek(floc, io.SeekStart); err != nil { 108 return err 109 } 110 111 // Preamble containing number of units, file, and function. 112 numUnits := uint32(d.r.ReadULEB128()) 113 fnameidx := uint32(d.r.ReadULEB128()) 114 fileidx := uint32(d.r.ReadULEB128()) 115 116 f.Srcfile = d.strtab.Get(fileidx) 117 f.Funcname = d.strtab.Get(fnameidx) 118 119 // Now the units 120 f.Units = f.Units[:0] 121 if cap(f.Units) < int(numUnits) { 122 f.Units = make([]coverage.CoverableUnit, 0, numUnits) 123 } 124 for k := uint32(0); k < numUnits; k++ { 125 f.Units = append(f.Units, 126 coverage.CoverableUnit{ 127 StLine: uint32(d.r.ReadULEB128()), 128 StCol: uint32(d.r.ReadULEB128()), 129 EnLine: uint32(d.r.ReadULEB128()), 130 EnCol: uint32(d.r.ReadULEB128()), 131 NxStmts: uint32(d.r.ReadULEB128()), 132 }) 133 } 134 lit := d.r.ReadULEB128() 135 f.Lit = lit != 0 136 return nil 137 }