1
2
3
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
20
21
22 type CounterDataReader struct {
23 stab *stringtab.Reader
24 args map[string]string
25 osargs []string
26 goarch string
27 goos string
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 string, rs io.ReadSeeker) (*CounterDataReader, error) {
40 cdr := &CounterDataReader{
41 mr: rs,
42 u32b: make([]byte, 4),
43 u8b: make([]byte, 1),
44 }
45
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
60 if err := cdr.readFooter(); err != nil {
61 return nil, err
62 }
63
64 hsz := int64(unsafe.Sizeof(cdr.hdr))
65 if _, err := cdr.mr.Seek(hsz, io.SeekStart); err != nil {
66 return nil, err
67 }
68
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
98
99 func (cdr *CounterDataReader) readSegmentPreamble() error {
100
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
111 if err := cdr.readStringTable(); err != nil {
112 return err
113 }
114 if err := cdr.readArgs(); err != nil {
115 return err
116 }
117
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 := make([]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 )
142 cdr.stab = stringtab.NewReader(slr)
143 cdr.stab.Read()
144 return nil
145 }
146
147 func (cdr *CounterDataReader) readArgs() error {
148 b := make([]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 )
157 sget := func() (string, 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 = make(map[string]string, int(nents))
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 = make([]string, 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
201
202
203
204
205
206 func (cdr *CounterDataReader) OsArgs() []string {
207 return cdr.osargs
208 }
209
210
211
212
213
214 func (cdr *CounterDataReader) Goos() string {
215 return cdr.goos
216 }
217
218
219
220
221
222 func (cdr *CounterDataReader) Goarch() string {
223 return cdr.goarch
224 }
225
226
227
228 type FuncPayload struct {
229 PkgIdx uint32
230 FuncIdx uint32
231 Counters []uint32
232 }
233
234
235 func (cdr *CounterDataReader) NumSegments() uint32 {
236 return cdr.ftr.NumSegments
237 }
238
239
240
241
242
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
250 ftrSize := int64(unsafe.Sizeof(cdr.ftr))
251 if _, err := cdr.mr.Seek(ftrSize, io.SeekCurrent); err != nil {
252 return false, err
253 }
254
255 if err := cdr.readSegmentPreamble(); err != nil {
256 return false, err
257 }
258 return true, nil
259 }
260
261
262
263 func (cdr *CounterDataReader) NumFunctionsInSegment() uint32 {
264 return uint32(cdr.shdr.FcnEntries)
265 }
266
267 const supportDeadFunctionsInCounterData = false
268
269
270
271
272
273
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
327
328
329
330
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
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 = make([]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
View as plain text