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

View as plain text