Source file src/cmd/covdata/covdata.go

     1  // Copyright 2022 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 main
     6  
     7  import (
     8  	"cmd/internal/cov"
     9  	"cmd/internal/pkgpattern"
    10  	"cmd/internal/telemetry/counter"
    11  	"flag"
    12  	"fmt"
    13  	"os"
    14  	"runtime"
    15  	"runtime/pprof"
    16  	"strings"
    17  )
    18  
    19  var verbflag = flag.Int("v", 0, "Verbose trace output level")
    20  var hflag = flag.Bool("h", false, "Panic on fatal errors (for stack trace)")
    21  var hwflag = flag.Bool("hw", false, "Panic on warnings (for stack trace)")
    22  var indirsflag = flag.String("i", "", "Input dirs to examine (comma separated)")
    23  var pkgpatflag = flag.String("pkg", "", "Restrict output to package(s) matching specified package pattern.")
    24  var cpuprofileflag = flag.String("cpuprofile", "", "Write CPU profile to specified file")
    25  var memprofileflag = flag.String("memprofile", "", "Write memory profile to specified file")
    26  var memprofilerateflag = flag.Int("memprofilerate", 0, "Set memprofile sampling rate to value")
    27  
    28  var matchpkg func(name string) bool
    29  
    30  var atExitFuncs []func()
    31  
    32  func atExit(f func()) {
    33  	atExitFuncs = append(atExitFuncs, f)
    34  }
    35  
    36  func Exit(code int) {
    37  	for i := len(atExitFuncs) - 1; i >= 0; i-- {
    38  		f := atExitFuncs[i]
    39  		atExitFuncs = atExitFuncs[:i]
    40  		f()
    41  	}
    42  	os.Exit(code)
    43  }
    44  
    45  func dbgtrace(vlevel int, s string, a ...interface{}) {
    46  	if *verbflag >= vlevel {
    47  		fmt.Printf(s, a...)
    48  		fmt.Printf("\n")
    49  	}
    50  }
    51  
    52  func warn(s string, a ...interface{}) {
    53  	fmt.Fprintf(os.Stderr, "warning: ")
    54  	fmt.Fprintf(os.Stderr, s, a...)
    55  	fmt.Fprintf(os.Stderr, "\n")
    56  	if *hwflag {
    57  		panic("unexpected warning")
    58  	}
    59  }
    60  
    61  func fatal(s string, a ...interface{}) {
    62  	fmt.Fprintf(os.Stderr, "error: ")
    63  	fmt.Fprintf(os.Stderr, s, a...)
    64  	fmt.Fprintf(os.Stderr, "\n")
    65  	if *hflag {
    66  		panic("fatal error")
    67  	}
    68  	Exit(1)
    69  }
    70  
    71  func usage(msg string) {
    72  	if len(msg) > 0 {
    73  		fmt.Fprintf(os.Stderr, "error: %s\n", msg)
    74  	}
    75  	fmt.Fprintf(os.Stderr, "usage: go tool covdata [command]\n")
    76  	fmt.Fprintf(os.Stderr, `
    77  Commands are:
    78  
    79  textfmt     convert coverage data to textual format
    80  percent     output total percentage of statements covered
    81  pkglist     output list of package import paths
    82  func        output coverage profile information for each function
    83  merge       merge data files together
    84  subtract    subtract one set of data files from another set
    85  intersect   generate intersection of two sets of data files
    86  debugdump   dump data in human-readable format for debugging purposes
    87  `)
    88  	fmt.Fprintf(os.Stderr, "\nFor help on a specific subcommand, try:\n")
    89  	fmt.Fprintf(os.Stderr, "\ngo tool covdata <cmd> -help\n")
    90  	Exit(2)
    91  }
    92  
    93  type covOperation interface {
    94  	cov.CovDataVisitor
    95  	Setup()
    96  	Usage(string)
    97  }
    98  
    99  // Modes of operation.
   100  const (
   101  	funcMode      = "func"
   102  	mergeMode     = "merge"
   103  	intersectMode = "intersect"
   104  	subtractMode  = "subtract"
   105  	percentMode   = "percent"
   106  	pkglistMode   = "pkglist"
   107  	textfmtMode   = "textfmt"
   108  	debugDumpMode = "debugdump"
   109  )
   110  
   111  func main() {
   112  	counter.Open()
   113  
   114  	// First argument should be mode/subcommand.
   115  	if len(os.Args) < 2 {
   116  		usage("missing command selector")
   117  	}
   118  
   119  	// Select mode
   120  	var op covOperation
   121  	cmd := os.Args[1]
   122  	switch cmd {
   123  	case mergeMode:
   124  		op = makeMergeOp()
   125  	case debugDumpMode:
   126  		op = makeDumpOp(debugDumpMode)
   127  	case textfmtMode:
   128  		op = makeDumpOp(textfmtMode)
   129  	case percentMode:
   130  		op = makeDumpOp(percentMode)
   131  	case funcMode:
   132  		op = makeDumpOp(funcMode)
   133  	case pkglistMode:
   134  		op = makeDumpOp(pkglistMode)
   135  	case subtractMode:
   136  		op = makeSubtractIntersectOp(subtractMode)
   137  	case intersectMode:
   138  		op = makeSubtractIntersectOp(intersectMode)
   139  	default:
   140  		usage(fmt.Sprintf("unknown command selector %q", cmd))
   141  	}
   142  
   143  	// Edit out command selector, then parse flags.
   144  	os.Args = append(os.Args[:1], os.Args[2:]...)
   145  	flag.Usage = func() {
   146  		op.Usage("")
   147  	}
   148  	flag.Parse()
   149  	counter.Inc("covdata/invocations")
   150  	counter.CountFlags("covdata/flag:", *flag.CommandLine)
   151  
   152  	// Mode-independent flag setup
   153  	dbgtrace(1, "starting mode-independent setup")
   154  	if flag.NArg() != 0 {
   155  		op.Usage("unknown extra arguments")
   156  	}
   157  	if *pkgpatflag != "" {
   158  		pats := strings.Split(*pkgpatflag, ",")
   159  		matchers := []func(name string) bool{}
   160  		for _, p := range pats {
   161  			if p == "" {
   162  				continue
   163  			}
   164  			f := pkgpattern.MatchSimplePattern(p)
   165  			matchers = append(matchers, f)
   166  		}
   167  		matchpkg = func(name string) bool {
   168  			for _, f := range matchers {
   169  				if f(name) {
   170  					return true
   171  				}
   172  			}
   173  			return false
   174  		}
   175  	}
   176  	if *cpuprofileflag != "" {
   177  		f, err := os.Create(*cpuprofileflag)
   178  		if err != nil {
   179  			fatal("%v", err)
   180  		}
   181  		if err := pprof.StartCPUProfile(f); err != nil {
   182  			fatal("%v", err)
   183  		}
   184  		atExit(func() {
   185  			pprof.StopCPUProfile()
   186  			if err = f.Close(); err != nil {
   187  				fatal("error closing cpu profile: %v", err)
   188  			}
   189  		})
   190  	}
   191  	if *memprofileflag != "" {
   192  		if *memprofilerateflag != 0 {
   193  			runtime.MemProfileRate = *memprofilerateflag
   194  		}
   195  		f, err := os.Create(*memprofileflag)
   196  		if err != nil {
   197  			fatal("%v", err)
   198  		}
   199  		atExit(func() {
   200  			runtime.GC()
   201  			const writeLegacyFormat = 1
   202  			if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil {
   203  				fatal("%v", err)
   204  			}
   205  			if err = f.Close(); err != nil {
   206  				fatal("error closing memory profile: %v", err)
   207  			}
   208  		})
   209  	} else {
   210  		// Not doing memory profiling; disable it entirely.
   211  		runtime.MemProfileRate = 0
   212  	}
   213  
   214  	// Mode-dependent setup.
   215  	op.Setup()
   216  
   217  	// ... off and running now.
   218  	dbgtrace(1, "starting perform")
   219  
   220  	indirs := strings.Split(*indirsflag, ",")
   221  	vis := cov.CovDataVisitor(op)
   222  	var flags cov.CovDataReaderFlags
   223  	if *hflag {
   224  		flags |= cov.PanicOnError
   225  	}
   226  	if *hwflag {
   227  		flags |= cov.PanicOnWarning
   228  	}
   229  	reader := cov.MakeCovDataReader(vis, indirs, *verbflag, flags, matchpkg)
   230  	st := 0
   231  	if err := reader.Visit(); err != nil {
   232  		fmt.Fprintf(os.Stderr, "error: %v\n", err)
   233  		st = 1
   234  	}
   235  	dbgtrace(1, "leaving main")
   236  	Exit(st)
   237  }
   238  

View as plain text