1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
|
// Copyright 2022 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 coverage
// Types and constants related to the output files written
// by code coverage tooling. When a coverage-instrumented binary
// is run, it emits two output files: a meta-data output file, and
// a counter data output file.
//.....................................................................
//
// Meta-data definitions:
//
// The meta-data file is composed of a file header, a series of
// meta-data blobs/sections (one per instrumented package), and an offsets
// area storing the offsets of each section. Format of the meta-data
// file looks like:
//
// --header----------
// | magic: [4]byte magic string
// | version
// | total length of meta-data file in bytes
// | numPkgs: number of package entries in file
// | hash: [16]byte hash of entire meta-data payload
// | offset to string table section
// | length of string table
// | number of entries in string table
// | counter mode
// | counter granularity
// --package offsets table------
// <offset to pkg 0>
// <offset to pkg 1>
// ...
// --package lengths table------
// <length of pkg 0>
// <length of pkg 1>
// ...
// --string table------
// <uleb128 len> 8
// <data> "somestring"
// ...
// --package payloads------
// <meta-symbol for pkg 0>
// <meta-symbol for pkg 1>
// ...
//
// Each package payload is a stand-alone blob emitted by the compiler,
// and does not depend on anything else in the meta-data file. In
// particular, each blob has it's own string table. Note that the
// file-level string table is expected to be very short (most strings
// will be in the meta-data blobs themselves).
// CovMetaMagic holds the magic string for a meta-data file.
var CovMetaMagic = [4]byte{'\x00', '\x63', '\x76', '\x6d'}
// MetaFilePref is a prefix used when emitting meta-data files; these
// files are of the form "covmeta.<hash>", where hash is a hash
// computed from the hashes of all the package meta-data symbols in
// the program.
const MetaFilePref = "covmeta"
// MetaFileVersion contains the current (most recent) meta-data file version.
const MetaFileVersion = 1
// MetaFileHeader stores file header information for a meta-data file.
type MetaFileHeader struct {
Magic [4]byte
Version uint32
TotalLength uint64
Entries uint64
MetaFileHash [16]byte
StrTabOffset uint32
StrTabLength uint32
CMode CounterMode
CGranularity CounterGranularity
_ [6]byte // padding
}
// MetaSymbolHeader stores header information for a single
// meta-data blob, e.g. the coverage meta-data payload
// computed for a given Go package.
type MetaSymbolHeader struct {
Length uint32 // size of meta-symbol payload in bytes
PkgName uint32 // string table index
PkgPath uint32 // string table index
ModulePath uint32 // string table index
MetaHash [16]byte
_ byte // currently unused
_ [3]byte // padding
NumFiles uint32
NumFuncs uint32
}
const CovMetaHeaderSize = 16 + 4 + 4 + 4 + 4 + 4 + 4 + 4 // keep in sync with above
// As an example, consider the following Go package:
//
// 01: package p
// 02:
// 03: var v, w, z int
// 04:
// 05: func small(x, y int) int {
// 06: v++
// 07: // comment
// 08: if y == 0 {
// 09: return x
// 10: }
// 11: return (x << 1) ^ (9 / y)
// 12: }
// 13:
// 14: func Medium(q, r int) int {
// 15: s1 := small(q, r)
// 16: z += s1
// 17: s2 := small(r, q)
// 18: w -= s2
// 19: return w + z
// 20: }
//
// The meta-data blob for the single package above might look like the
// following:
//
// -- MetaSymbolHeader header----------
// | size: size of this blob in bytes
// | packagepath: <path to p>
// | modulepath: <modpath for p>
// | nfiles: 1
// | nfunctions: 2
// --func offsets table------
// <offset to func 0>
// <offset to func 1>
// --string table (contains all files and functions)------
// | <uleb128 len> 4
// | <data> "p.go"
// | <uleb128 len> 5
// | <data> "small"
// | <uleb128 len> 6
// | <data> "Medium"
// --func 0------
// | <uleb128> num units: 3
// | <uleb128> func name: S1 (index into string table)
// | <uleb128> file: S0 (index into string table)
// | <unit 0>: S0 L6 L8 2
// | <unit 1>: S0 L9 L9 1
// | <unit 2>: S0 L11 L11 1
// --func 1------
// | <uleb128> num units: 1
// | <uleb128> func name: S2 (index into string table)
// | <uleb128> file: S0 (index into string table)
// | <unit 0>: S0 L15 L19 5
// ---end-----------
// The following types and constants used by the meta-data encoder/decoder.
// FuncDesc encapsulates the meta-data definitions for a single Go function.
// This version assumes that we're looking at a function before inlining;
// if we want to capture a post-inlining view of the world, the
// representations of source positions would need to be a good deal more
// complicated.
type FuncDesc struct {
Funcname string
Srcfile string
Units []CoverableUnit
Lit bool // true if this is a function literal
}
// CoverableUnit describes the source characteristics of a single
// program unit for which we want to gather coverage info. Coverable
// units are either "simple" or "intraline"; a "simple" coverable unit
// corresponds to a basic block (region of straight-line code with no
// jumps or control transfers). An "intraline" unit corresponds to a
// logical clause nested within some other simple unit. A simple unit
// will have a zero Parent value; for an intraline unit NxStmts will
// be zero and Parent will be set to 1 plus the index of the
// containing simple statement. Example:
//
// L7: q := 1
// L8: x := (y == 101 || launch() == false)
// L9: r := x * 2
//
// For the code above we would have three simple units (one for each
// line), then an intraline unit describing the "launch() == false"
// clause in line 8, with Parent pointing to the index of the line 8
// unit in the units array.
//
// Note: in the initial version of the coverage revamp, only simple
// units will be in use.
type CoverableUnit struct {
StLine, StCol uint32
EnLine, EnCol uint32
NxStmts uint32
Parent uint32
}
// CounterMode tracks the "flavor" of the coverage counters being
// used in a given coverage-instrumented program.
type CounterMode uint8
const (
CtrModeInvalid CounterMode = iota
CtrModeSet // "set" mode
CtrModeCount // "count" mode
CtrModeAtomic // "atomic" mode
CtrModeRegOnly // registration-only pseudo-mode
CtrModeTestMain // testmain pseudo-mode
)
func (cm CounterMode) String() string {
switch cm {
case CtrModeSet:
return "set"
case CtrModeCount:
return "count"
case CtrModeAtomic:
return "atomic"
case CtrModeRegOnly:
return "regonly"
case CtrModeTestMain:
return "testmain"
}
return "<invalid>"
}
func ParseCounterMode(mode string) CounterMode {
var cm CounterMode
switch mode {
case "set":
cm = CtrModeSet
case "count":
cm = CtrModeCount
case "atomic":
cm = CtrModeAtomic
case "regonly":
cm = CtrModeRegOnly
case "testmain":
cm = CtrModeTestMain
default:
cm = CtrModeInvalid
}
return cm
}
// CounterGranularity tracks the granularity of the coverage counters being
// used in a given coverage-instrumented program.
type CounterGranularity uint8
const (
CtrGranularityInvalid CounterGranularity = iota
CtrGranularityPerBlock
CtrGranularityPerFunc
)
func (cm CounterGranularity) String() string {
switch cm {
case CtrGranularityPerBlock:
return "perblock"
case CtrGranularityPerFunc:
return "perfunc"
}
return "<invalid>"
}
//.....................................................................
//
// Counter data definitions:
//
// A counter data file is composed of a file header followed by one or
// more "segments" (each segment representing a given run or partial
// run of a give binary) followed by a footer.
// CovCounterMagic holds the magic string for a coverage counter-data file.
var CovCounterMagic = [4]byte{'\x00', '\x63', '\x77', '\x6d'}
// CounterFileVersion stores the most recent counter data file version.
const CounterFileVersion = 1
// CounterFileHeader stores files header information for a counter-data file.
type CounterFileHeader struct {
Magic [4]byte
Version uint32
MetaHash [16]byte
CFlavor CounterFlavor
BigEndian bool
_ [6]byte // padding
}
// CounterSegmentHeader encapsulates information about a specific
// segment in a counter data file, which at the moment contains
// counters data from a single execution of a coverage-instrumented
// program. Following the segment header will be the string table and
// args table, and then (possibly) padding bytes to bring the byte
// size of the preamble up to a multiple of 4. Immediately following
// that will be the counter payloads.
//
// The "args" section of a segment is used to store annotations
// describing where the counter data came from; this section is
// basically a series of key-value pairs (can be thought of as an
// encoded 'map[string]string'). At the moment we only write os.Args()
// data to this section, using pairs of the form "argc=<integer>",
// "argv0=<os.Args[0]>", "argv1=<os.Args[1]>", and so on. In the
// future the args table may also include things like GOOS/GOARCH
// values, and/or tags indicating which tests were run to generate the
// counter data.
type CounterSegmentHeader struct {
FcnEntries uint64
StrTabLen uint32
ArgsLen uint32
}
// CounterFileFooter appears at the tail end of a counter data file,
// and stores the number of segments it contains.
type CounterFileFooter struct {
Magic [4]byte
_ [4]byte // padding
NumSegments uint32
_ [4]byte // padding
}
// CounterFilePref is the file prefix used when emitting coverage data
// output files. CounterFileTemplate describes the format of the file
// name: prefix followed by meta-file hash followed by process ID
// followed by emit UnixNanoTime.
const CounterFilePref = "covcounters"
const CounterFileTempl = "%s.%x.%d.%d"
const CounterFileRegexp = `^%s\.(\S+)\.(\d+)\.(\d+)+$`
// CounterFlavor describes how function and counters are
// stored/represented in the counter section of the file.
type CounterFlavor uint8
const (
// "Raw" representation: all values (pkg ID, func ID, num counters,
// and counters themselves) are stored as uint32's.
CtrRaw CounterFlavor = iota + 1
// "ULeb" representation: all values (pkg ID, func ID, num counters,
// and counters themselves) are stored with ULEB128 encoding.
CtrULeb128
)
func Round4(x int) int {
return (x + 3) &^ 3
}
//.....................................................................
//
// Runtime counter data definitions.
//
// At runtime within a coverage-instrumented program, the "counters"
// object we associated with instrumented function can be thought of
// as a struct of the following form:
//
// struct {
// numCtrs uint32
// pkgid uint32
// funcid uint32
// counterArray [numBlocks]uint32
// }
//
// where "numCtrs" is the number of blocks / coverable units within the
// function, "pkgid" is the unique index assigned to this package by
// the runtime, "funcid" is the index of this function within its containing
// package, and "counterArray" stores the actual counters.
//
// The counter variable itself is created not as a struct but as a flat
// array of uint32's; we then use the offsets below to index into it.
const NumCtrsOffset = 0
const PkgIdOffset = 1
const FuncIdOffset = 2
const FirstCtrOffset = 3
|