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