mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Add utilities for reading and writing of counter data files as part of the new code coverage implementation. Trivia note: currently the contents of counter data files are emitted in little-endian form, which for the counters is somewhat painful in that we need to visit each counter value and properly encode it. It might be better to instead emit counters in native endianity and then teach the tools to decode properly in the case of an endianity mismatch. Updates #51430. Change-Id: I124fdcb40fc339a48b64b35264bf24c3be50ddd4 Reviewed-on: https://go-review.googlesource.com/c/go/+/359403 TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: David Chase <drchase@google.com> Run-TryBot: Than McIntosh <thanm@google.com>
231 lines
6.1 KiB
Go
231 lines
6.1 KiB
Go
// Copyright 2021 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 test
|
|
|
|
import (
|
|
"fmt"
|
|
"internal/coverage"
|
|
"internal/coverage/decodecounter"
|
|
"internal/coverage/encodecounter"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
type ctrVis struct {
|
|
funcs []decodecounter.FuncPayload
|
|
}
|
|
|
|
func (v *ctrVis) NumFuncs() (int, error) {
|
|
return len(v.funcs), nil
|
|
}
|
|
|
|
func (v *ctrVis) VisitFuncs(f encodecounter.CounterVisitorFn) error {
|
|
for _, fn := range v.funcs {
|
|
if err := f(fn.PkgIdx, fn.FuncIdx, fn.Counters); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func mkfunc(p uint32, f uint32, c []uint32) decodecounter.FuncPayload {
|
|
return decodecounter.FuncPayload{
|
|
PkgIdx: p,
|
|
FuncIdx: f,
|
|
Counters: c,
|
|
}
|
|
}
|
|
|
|
func TestCounterDataWriterReader(t *testing.T) {
|
|
flavors := []coverage.CounterFlavor{
|
|
coverage.CtrRaw,
|
|
coverage.CtrULeb128,
|
|
}
|
|
|
|
isDead := func(fp decodecounter.FuncPayload) bool {
|
|
for _, v := range fp.Counters {
|
|
if v != 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
funcs := []decodecounter.FuncPayload{
|
|
mkfunc(0, 0, []uint32{13, 14, 15}),
|
|
mkfunc(0, 1, []uint32{16, 17}),
|
|
mkfunc(1, 0, []uint32{18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 976543, 7}),
|
|
}
|
|
writeVisitor := &ctrVis{funcs: funcs}
|
|
|
|
for kf, flav := range flavors {
|
|
|
|
t.Logf("testing flavor %d\n", flav)
|
|
|
|
// Open a counter data file in preparation for emitting data.
|
|
d := t.TempDir()
|
|
cfpath := filepath.Join(d, fmt.Sprintf("covcounters.hash.0.%d", kf))
|
|
of, err := os.OpenFile(cfpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
|
if err != nil {
|
|
t.Fatalf("opening covcounters: %v", err)
|
|
}
|
|
|
|
// Perform the encode and write.
|
|
cdfw := encodecounter.NewCoverageDataWriter(of, flav)
|
|
if cdfw == nil {
|
|
t.Fatalf("NewCoverageDataWriter failed")
|
|
}
|
|
finalHash := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0}
|
|
args := map[string]string{"argc": "3", "argv0": "arg0", "argv1": "arg1", "argv2": "arg_________2"}
|
|
if err := cdfw.Write(finalHash, args, writeVisitor); err != nil {
|
|
t.Fatalf("counter file Write failed: %v", err)
|
|
}
|
|
if err := of.Close(); err != nil {
|
|
t.Fatalf("closing covcounters: %v", err)
|
|
}
|
|
cdfw = nil
|
|
|
|
// Decode the same file.
|
|
var cdr *decodecounter.CounterDataReader
|
|
inf, err := os.Open(cfpath)
|
|
defer inf.Close()
|
|
if err != nil {
|
|
t.Fatalf("reopening covcounters file: %v", err)
|
|
}
|
|
if cdr, err = decodecounter.NewCounterDataReader(cfpath, inf); err != nil {
|
|
t.Fatalf("opening covcounters for read: %v", err)
|
|
}
|
|
decodedArgs := cdr.OsArgs()
|
|
aWant := "[arg0 arg1 arg_________2]"
|
|
aGot := fmt.Sprintf("%+v", decodedArgs)
|
|
if aWant != aGot {
|
|
t.Errorf("reading decoded args, got %s want %s", aGot, aWant)
|
|
}
|
|
for i := range funcs {
|
|
if isDead(funcs[i]) {
|
|
continue
|
|
}
|
|
var fp decodecounter.FuncPayload
|
|
if ok, err := cdr.NextFunc(&fp); err != nil {
|
|
t.Fatalf("reading func %d: %v", i, err)
|
|
} else if !ok {
|
|
t.Fatalf("reading func %d: bad return", i)
|
|
}
|
|
got := fmt.Sprintf("%+v", fp)
|
|
want := fmt.Sprintf("%+v", funcs[i])
|
|
if got != want {
|
|
t.Errorf("cdr.NextFunc iter %d\ngot %+v\nwant %+v", i, got, want)
|
|
}
|
|
}
|
|
var dummy decodecounter.FuncPayload
|
|
if ok, err := cdr.NextFunc(&dummy); err != nil {
|
|
t.Fatalf("reading func after loop: %v", err)
|
|
} else if ok {
|
|
t.Fatalf("reading func after loop: expected EOF")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCounterDataAppendSegment(t *testing.T) {
|
|
d := t.TempDir()
|
|
cfpath := filepath.Join(d, "covcounters.hash2.0")
|
|
of, err := os.OpenFile(cfpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
|
if err != nil {
|
|
t.Fatalf("opening covcounters: %v", err)
|
|
}
|
|
|
|
const numSegments = 2
|
|
|
|
// Write a counter with with multiple segments.
|
|
args := map[string]string{"argc": "1", "argv0": "prog.exe"}
|
|
allfuncs := [][]decodecounter.FuncPayload{}
|
|
ctrs := []uint32{}
|
|
q := uint32(0)
|
|
var cdfw *encodecounter.CoverageDataWriter
|
|
for idx := 0; idx < numSegments; idx++ {
|
|
args[fmt.Sprintf("seg%d", idx)] = "x"
|
|
q += 7
|
|
ctrs = append(ctrs, q)
|
|
funcs := []decodecounter.FuncPayload{}
|
|
for k := 0; k < idx+1; k++ {
|
|
c := make([]uint32, len(ctrs))
|
|
copy(c, ctrs)
|
|
funcs = append(funcs, mkfunc(uint32(idx), uint32(k), c))
|
|
}
|
|
allfuncs = append(allfuncs, funcs)
|
|
|
|
writeVisitor := &ctrVis{funcs: funcs}
|
|
|
|
if idx == 0 {
|
|
// Perform the encode and write.
|
|
cdfw = encodecounter.NewCoverageDataWriter(of, coverage.CtrRaw)
|
|
if cdfw == nil {
|
|
t.Fatalf("NewCoverageDataWriter failed")
|
|
}
|
|
finalHash := [16]byte{1, 2}
|
|
if err := cdfw.Write(finalHash, args, writeVisitor); err != nil {
|
|
t.Fatalf("counter file Write failed: %v", err)
|
|
}
|
|
} else {
|
|
if err := cdfw.AppendSegment(args, writeVisitor); err != nil {
|
|
t.Fatalf("counter file AppendSegment failed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
if err := of.Close(); err != nil {
|
|
t.Fatalf("closing covcounters: %v", err)
|
|
}
|
|
|
|
// Read the result file.
|
|
var cdr *decodecounter.CounterDataReader
|
|
inf, err := os.Open(cfpath)
|
|
defer inf.Close()
|
|
if err != nil {
|
|
t.Fatalf("reopening covcounters file: %v", err)
|
|
}
|
|
if cdr, err = decodecounter.NewCounterDataReader(cfpath, inf); err != nil {
|
|
t.Fatalf("opening covcounters for read: %v", err)
|
|
}
|
|
ns := cdr.NumSegments()
|
|
if ns != numSegments {
|
|
t.Fatalf("got %d segments want %d", ns, numSegments)
|
|
}
|
|
if len(allfuncs) != numSegments {
|
|
t.Fatalf("expected %d got %d", numSegments, len(allfuncs))
|
|
}
|
|
|
|
for sidx := 0; sidx < int(ns); sidx++ {
|
|
|
|
if off, err := inf.Seek(0, os.SEEK_CUR); err != nil {
|
|
t.Fatalf("Seek failed: %v", err)
|
|
} else {
|
|
t.Logf("sidx=%d off=%d\n", sidx, off)
|
|
}
|
|
|
|
if sidx != 0 {
|
|
if ok, err := cdr.BeginNextSegment(); err != nil {
|
|
t.Fatalf("BeginNextSegment failed: %v", err)
|
|
} else if !ok {
|
|
t.Fatalf("BeginNextSegment return %v on iter %d",
|
|
ok, sidx)
|
|
}
|
|
}
|
|
funcs := allfuncs[sidx]
|
|
for i := range funcs {
|
|
var fp decodecounter.FuncPayload
|
|
if ok, err := cdr.NextFunc(&fp); err != nil {
|
|
t.Fatalf("reading func %d: %v", i, err)
|
|
} else if !ok {
|
|
t.Fatalf("reading func %d: bad return", i)
|
|
}
|
|
got := fmt.Sprintf("%+v", fp)
|
|
want := fmt.Sprintf("%+v", funcs[i])
|
|
if got != want {
|
|
t.Errorf("cdr.NextFunc iter %d\ngot %+v\nwant %+v", i, got, want)
|
|
}
|
|
}
|
|
}
|
|
}
|