| // 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 |