github.com/bir3/gocompiler@v0.9.2202/src/internal/coverage/decodecounter/decodecounterfile.go (about) 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 "github.com/bir3/gocompiler/src/internal/coverage" 11 "github.com/bir3/gocompiler/src/internal/coverage/slicereader" 12 "github.com/bir3/gocompiler/src/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]string 25 osargs []string 26 goarch string // GOARCH setting from run that produced counter data 27 goos string // 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 string, rs io.ReadSeeker) (*CounterDataReader, error) { 40 cdr := &CounterDataReader{ 41 mr: rs, 42 u32b: make([]byte, 4), 43 u8b: make([]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 := 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 /* 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 := 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 /* not readonly */) 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 // 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() []string { 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() string { 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() string { 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 = 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 }