| // 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 decodemeta |
| |
| // This package contains APIs and helpers for decoding a single package's |
| // meta data "blob" emitted by the compiler when coverage instrumentation |
| // is turned on. |
| |
| import ( |
| "encoding/binary" |
| "fmt" |
| "internal/coverage" |
| "internal/coverage/slicereader" |
| "internal/coverage/stringtab" |
| "os" |
| ) |
| |
| // See comments in the encodecovmeta package for details on the format. |
| |
| type CoverageMetaDataDecoder struct { |
| r *slicereader.Reader |
| hdr coverage.MetaSymbolHeader |
| strtab *stringtab.Reader |
| tmp []byte |
| debug bool |
| } |
| |
| func NewCoverageMetaDataDecoder(b []byte, readonly bool) (*CoverageMetaDataDecoder, error) { |
| slr := slicereader.NewReader(b, readonly) |
| x := &CoverageMetaDataDecoder{ |
| r: slr, |
| tmp: make([]byte, 0, 256), |
| } |
| if err := x.readHeader(); err != nil { |
| return nil, err |
| } |
| if err := x.readStringTable(); err != nil { |
| return nil, err |
| } |
| return x, nil |
| } |
| |
| func (d *CoverageMetaDataDecoder) readHeader() error { |
| if err := binary.Read(d.r, binary.LittleEndian, &d.hdr); err != nil { |
| return err |
| } |
| if d.debug { |
| fmt.Fprintf(os.Stderr, "=-= after readHeader: %+v\n", d.hdr) |
| } |
| return nil |
| } |
| |
| func (d *CoverageMetaDataDecoder) readStringTable() error { |
| // Seek to the correct location to read the string table. |
| stringTableLocation := int64(coverage.CovMetaHeaderSize + 4*d.hdr.NumFuncs) |
| d.r.SeekTo(stringTableLocation) |
| |
| // Read the table itself. |
| d.strtab = stringtab.NewReader(d.r) |
| d.strtab.Read() |
| return nil |
| } |
| |
| func (d *CoverageMetaDataDecoder) PackagePath() string { |
| return d.strtab.Get(d.hdr.PkgPath) |
| } |
| |
| func (d *CoverageMetaDataDecoder) PackageName() string { |
| return d.strtab.Get(d.hdr.PkgName) |
| } |
| |
| func (d *CoverageMetaDataDecoder) ModulePath() string { |
| return d.strtab.Get(d.hdr.ModulePath) |
| } |
| |
| func (d *CoverageMetaDataDecoder) NumFuncs() uint32 { |
| return d.hdr.NumFuncs |
| } |
| |
| // ReadFunc reads the coverage meta-data for the function with index |
| // 'findex', filling it into the FuncDesc pointed to by 'f'. |
| func (d *CoverageMetaDataDecoder) ReadFunc(fidx uint32, f *coverage.FuncDesc) error { |
| if fidx >= d.hdr.NumFuncs { |
| return fmt.Errorf("illegal function index") |
| } |
| |
| // Seek to the correct location to read the function offset and read it. |
| funcOffsetLocation := int64(coverage.CovMetaHeaderSize + 4*fidx) |
| d.r.SeekTo(funcOffsetLocation) |
| foff := d.r.ReadUint32() |
| |
| // Check assumptions |
| if foff < uint32(funcOffsetLocation) || foff > d.hdr.Length { |
| return fmt.Errorf("malformed func offset %d", foff) |
| } |
| |
| // Seek to the correct location to read the function. |
| d.r.SeekTo(int64(foff)) |
| |
| // Preamble containing number of units, file, and function. |
| numUnits := uint32(d.r.ReadULEB128()) |
| fnameidx := uint32(d.r.ReadULEB128()) |
| fileidx := uint32(d.r.ReadULEB128()) |
| |
| f.Srcfile = d.strtab.Get(fileidx) |
| f.Funcname = d.strtab.Get(fnameidx) |
| |
| // Now the units |
| f.Units = f.Units[:0] |
| if cap(f.Units) < int(numUnits) { |
| f.Units = make([]coverage.CoverableUnit, 0, numUnits) |
| } |
| for k := uint32(0); k < numUnits; k++ { |
| f.Units = append(f.Units, |
| coverage.CoverableUnit{ |
| StLine: uint32(d.r.ReadULEB128()), |
| StCol: uint32(d.r.ReadULEB128()), |
| EnLine: uint32(d.r.ReadULEB128()), |
| EnCol: uint32(d.r.ReadULEB128()), |
| NxStmts: uint32(d.r.ReadULEB128()), |
| }) |
| } |
| lit := d.r.ReadULEB128() |
| f.Lit = lit != 0 |
| return nil |
| } |