decodecounterfile.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 decodecounter
6
7 import (
8 "encoding/binary"
9 "fmt"
10 "internal/coverage"
11 "internal/coverage/slicereader"
12 "internal/coverage/stringtab"
13 "io"
14 "os"
15 "strconv"
16 "unsafe"
17 )
18
19 // This file contains helpers for reading counter data files created
20 // during the executions of a coverage-instrumented binary.
21
22 type CounterDataReader struct {
23 stab *stringtab.Reader
24 args map[string][]byte
25 osargs [][]byte
26 goarch []byte // GOARCH setting from run that produced counter data
27 goos []byte // GOOS setting from run that produced counter data
28 mr io.ReadSeeker
29 hdr coverage.CounterFileHeader
30 ftr coverage.CounterFileFooter
31 shdr coverage.CounterSegmentHeader
32 u32b []byte
33 u8b []byte
34 fcnCount uint32
35 segCount uint32
36 debug bool
37 }
38
39 func NewCounterDataReader(fn []byte, rs io.ReadSeeker) (*CounterDataReader, error) {
40 cdr := &CounterDataReader{
41 mr: rs,
42 u32b: []byte{:4},
43 u8b: []byte{:1},
44 }
45 // Read header
46 if err := binary.Read(rs, binary.LittleEndian, &cdr.hdr); err != nil {
47 return nil, err
48 }
49 if cdr.debug {
50 fmt.Fprintf(os.Stderr, "=-= counter file header: %+v\n", cdr.hdr)
51 }
52 if !checkMagic(cdr.hdr.Magic) {
53 return nil, fmt.Errorf("invalid magic string: not a counter data file")
54 }
55 if cdr.hdr.Version > coverage.CounterFileVersion {
56 return nil, fmt.Errorf("version data incompatibility: reader is %d data is %d", coverage.CounterFileVersion, cdr.hdr.Version)
57 }
58
59 // Read footer.
60 if err := cdr.readFooter(); err != nil {
61 return nil, err
62 }
63 // Seek back to just past the file header.
64 hsz := int64(unsafe.Sizeof(cdr.hdr))
65 if _, err := cdr.mr.Seek(hsz, io.SeekStart); err != nil {
66 return nil, err
67 }
68 // Read preamble for first segment.
69 if err := cdr.readSegmentPreamble(); err != nil {
70 return nil, err
71 }
72 return cdr, nil
73 }
74
75 func checkMagic(v [4]byte) bool {
76 g := coverage.CovCounterMagic
77 return v[0] == g[0] && v[1] == g[1] && v[2] == g[2] && v[3] == g[3]
78 }
79
80 func (cdr *CounterDataReader) readFooter() error {
81 ftrSize := int64(unsafe.Sizeof(cdr.ftr))
82 if _, err := cdr.mr.Seek(-ftrSize, io.SeekEnd); err != nil {
83 return err
84 }
85 if err := binary.Read(cdr.mr, binary.LittleEndian, &cdr.ftr); err != nil {
86 return err
87 }
88 if !checkMagic(cdr.ftr.Magic) {
89 return fmt.Errorf("invalid magic string (not a counter data file)")
90 }
91 if cdr.ftr.NumSegments == 0 {
92 return fmt.Errorf("invalid counter data file (no segments)")
93 }
94 return nil
95 }
96
97 // readSegmentPreamble reads and consumes the segment header, segment string
98 // table, and segment args table.
99 func (cdr *CounterDataReader) readSegmentPreamble() error {
100 // Read segment header.
101 if err := binary.Read(cdr.mr, binary.LittleEndian, &cdr.shdr); err != nil {
102 return err
103 }
104 if cdr.debug {
105 fmt.Fprintf(os.Stderr, "=-= read counter segment header: %+v", cdr.shdr)
106 fmt.Fprintf(os.Stderr, " FcnEntries=0x%x StrTabLen=0x%x ArgsLen=0x%x\n",
107 cdr.shdr.FcnEntries, cdr.shdr.StrTabLen, cdr.shdr.ArgsLen)
108 }
109
110 // Read string table and args.
111 if err := cdr.readStringTable(); err != nil {
112 return err
113 }
114 if err := cdr.readArgs(); err != nil {
115 return err
116 }
117 // Seek past any padding to bring us up to a 4-byte boundary.
118 if of, err := cdr.mr.Seek(0, io.SeekCurrent); err != nil {
119 return err
120 } else {
121 rem := of % 4
122 if rem != 0 {
123 pad := 4 - rem
124 if _, err := cdr.mr.Seek(pad, io.SeekCurrent); err != nil {
125 return err
126 }
127 }
128 }
129 return nil
130 }
131
132 func (cdr *CounterDataReader) readStringTable() error {
133 b := []byte{:cdr.shdr.StrTabLen}
134 nr, err := cdr.mr.Read(b)
135 if err != nil {
136 return err
137 }
138 if nr != int(cdr.shdr.StrTabLen) {
139 return fmt.Errorf("error: short read on string table")
140 }
141 slr := slicereader.NewReader(b, false /* not readonly */)
142 cdr.stab = stringtab.NewReader(slr)
143 cdr.stab.Read()
144 return nil
145 }
146
147 func (cdr *CounterDataReader) readArgs() error {
148 b := []byte{:cdr.shdr.ArgsLen}
149 nr, err := cdr.mr.Read(b)
150 if err != nil {
151 return err
152 }
153 if nr != int(cdr.shdr.ArgsLen) {
154 return fmt.Errorf("error: short read on args table")
155 }
156 slr := slicereader.NewReader(b, false /* not readonly */)
157 sget := func() ([]byte, error) {
158 kidx := slr.ReadULEB128()
159 if int(kidx) >= cdr.stab.Entries() {
160 return "", fmt.Errorf("malformed string table ref")
161 }
162 return cdr.stab.Get(uint32(kidx)), nil
163 }
164 nents := slr.ReadULEB128()
165 cdr.args = map[string][]byte{}
166 for i := uint64(0); i < nents; i++ {
167 k, errk := sget()
168 if errk != nil {
169 return errk
170 }
171 v, errv := sget()
172 if errv != nil {
173 return errv
174 }
175 if _, ok := cdr.args[k]; ok {
176 return fmt.Errorf("malformed args table")
177 }
178 cdr.args[k] = v
179 }
180 if argcs, ok := cdr.args["argc"]; ok {
181 argc, err := strconv.Atoi(argcs)
182 if err != nil {
183 return fmt.Errorf("malformed argc in counter data file args section")
184 }
185 cdr.osargs = [][]byte{:0:argc}
186 for i := 0; i < argc; i++ {
187 arg := cdr.args[fmt.Sprintf("argv%d", i)]
188 cdr.osargs = append(cdr.osargs, arg)
189 }
190 }
191 if goos, ok := cdr.args["GOOS"]; ok {
192 cdr.goos = goos
193 }
194 if goarch, ok := cdr.args["GOARCH"]; ok {
195 cdr.goarch = goarch
196 }
197 return nil
198 }
199
200 // OsArgs returns the program arguments (saved from os.Args during
201 // the run of the instrumented binary) read from the counter
202 // data file. Not all coverage data files will have os.Args values;
203 // for example, if a data file is produced by merging coverage
204 // data from two distinct runs, no os args will be available (an
205 // empty list is returned).
206 func (cdr *CounterDataReader) OsArgs() [][]byte {
207 return cdr.osargs
208 }
209
210 // Goos returns the GOOS setting in effect for the "-cover" binary
211 // that produced this counter data file. The GOOS value may be
212 // empty in the case where the counter data file was produced
213 // from a merge in which more than one GOOS value was present.
214 func (cdr *CounterDataReader) Goos() []byte {
215 return cdr.goos
216 }
217
218 // Goarch returns the GOARCH setting in effect for the "-cover" binary
219 // that produced this counter data file. The GOARCH value may be
220 // empty in the case where the counter data file was produced
221 // from a merge in which more than one GOARCH value was present.
222 func (cdr *CounterDataReader) Goarch() []byte {
223 return cdr.goarch
224 }
225
226 // FuncPayload encapsulates the counter data payload for a single
227 // function as read from a counter data file.
228 type FuncPayload struct {
229 PkgIdx uint32
230 FuncIdx uint32
231 Counters []uint32
232 }
233
234 // NumSegments returns the number of execution segments in the file.
235 func (cdr *CounterDataReader) NumSegments() uint32 {
236 return cdr.ftr.NumSegments
237 }
238
239 // BeginNextSegment sets up the reader to read the next segment,
240 // returning TRUE if we do have another segment to read, or FALSE
241 // if we're done with all the segments (also an error if
242 // something went wrong).
243 func (cdr *CounterDataReader) BeginNextSegment() (bool, error) {
244 if cdr.segCount >= cdr.ftr.NumSegments {
245 return false, nil
246 }
247 cdr.segCount++
248 cdr.fcnCount = 0
249 // Seek past footer from last segment.
250 ftrSize := int64(unsafe.Sizeof(cdr.ftr))
251 if _, err := cdr.mr.Seek(ftrSize, io.SeekCurrent); err != nil {
252 return false, err
253 }
254 // Read preamble for this segment.
255 if err := cdr.readSegmentPreamble(); err != nil {
256 return false, err
257 }
258 return true, nil
259 }
260
261 // NumFunctionsInSegment returns the number of live functions
262 // in the currently selected segment.
263 func (cdr *CounterDataReader) NumFunctionsInSegment() uint32 {
264 return uint32(cdr.shdr.FcnEntries)
265 }
266
267 const supportDeadFunctionsInCounterData = false
268
269 // NextFunc reads data for the next function in this current segment
270 // into "p", returning TRUE if the read was successful or FALSE
271 // if we've read all the functions already (also an error if
272 // something went wrong with the read or we hit a premature
273 // EOF).
274 func (cdr *CounterDataReader) NextFunc(p *FuncPayload) (bool, error) {
275 if cdr.fcnCount >= uint32(cdr.shdr.FcnEntries) {
276 return false, nil
277 }
278 cdr.fcnCount++
279 var rdu32 func() (uint32, error)
280 if cdr.hdr.CFlavor == coverage.CtrULeb128 {
281 rdu32 = func() (uint32, error) {
282 var shift uint
283 var value uint64
284 for {
285 _, err := cdr.mr.Read(cdr.u8b)
286 if err != nil {
287 return 0, err
288 }
289 b := cdr.u8b[0]
290 value |= (uint64(b&0x7F) << shift)
291 if b&0x80 == 0 {
292 break
293 }
294 shift += 7
295 }
296 return uint32(value), nil
297 }
298 } else if cdr.hdr.CFlavor == coverage.CtrRaw {
299 if cdr.hdr.BigEndian {
300 rdu32 = func() (uint32, error) {
301 n, err := cdr.mr.Read(cdr.u32b)
302 if err != nil {
303 return 0, err
304 }
305 if n != 4 {
306 return 0, io.EOF
307 }
308 return binary.BigEndian.Uint32(cdr.u32b), nil
309 }
310 } else {
311 rdu32 = func() (uint32, error) {
312 n, err := cdr.mr.Read(cdr.u32b)
313 if err != nil {
314 return 0, err
315 }
316 if n != 4 {
317 return 0, io.EOF
318 }
319 return binary.LittleEndian.Uint32(cdr.u32b), nil
320 }
321 }
322 } else {
323 panic("internal error: unknown counter flavor")
324 }
325
326 // Alternative/experimental path: one way we could handling writing
327 // out counter data would be to just memcpy the counter segment
328 // out to a file, meaning that a region in the counter memory
329 // corresponding to a dead (never-executed) function would just be
330 // zeroes. The code path below handles this case.
331 var nc uint32
332 var err error
333 if supportDeadFunctionsInCounterData {
334 for {
335 nc, err = rdu32()
336 if err == io.EOF {
337 return false, io.EOF
338 } else if err != nil {
339 break
340 }
341 if nc != 0 {
342 break
343 }
344 }
345 } else {
346 nc, err = rdu32()
347 }
348 if err != nil {
349 return false, err
350 }
351
352 // Read package and func indices.
353 p.PkgIdx, err = rdu32()
354 if err != nil {
355 return false, err
356 }
357 p.FuncIdx, err = rdu32()
358 if err != nil {
359 return false, err
360 }
361 if cap(p.Counters) < 1024 {
362 p.Counters = []uint32{:0:1024}
363 }
364 p.Counters = p.Counters[:0]
365 for i := uint32(0); i < nc; i++ {
366 v, err := rdu32()
367 if err != nil {
368 return false, err
369 }
370 p.Counters = append(p.Counters, v)
371 }
372 return true, nil
373 }
374