diff options
Diffstat (limited to '')
-rw-r--r-- | src/internal/coverage/defs.go | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/src/internal/coverage/defs.go b/src/internal/coverage/defs.go new file mode 100644 index 0000000..340ac95 --- /dev/null +++ b/src/internal/coverage/defs.go @@ -0,0 +1,388 @@ +// 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>" +} + +// Name of file within the "go test -cover" temp coverdir directory +// containing a list of meta-data files for packages being tested +// in a "go test -coverpkg=... ..." run. This constant is shared +// by the Go command and by the coverage runtime. +const MetaFilesFileName = "metafiles.txt" + +// MetaFilePaths contains information generated by the Go command and +// the read in by coverage test support functions within an executing +// "go test -cover" binary. +type MetaFileCollection struct { + ImportPaths []string + MetaFileFragments []string +} + +//..................................................................... +// +// 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 |