...

Source file src/internal/coverage/decodecounter/decodecounterfile.go

Documentation: internal/coverage/decodecounter

     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  	"internal/coverage"
    11  	"internal/coverage/slicereader"
    12  	"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  }
   374  

View as plain text