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(func() {
   180  			pprof.StopCPUProfile()
   181  			if err = f.Close(); err != nil {
   182  				fatal("error closing cpu profile: %v", err)
   183  			}
   184  		})
   185  	}
   186  	if *memprofileflag != "" {
   187  		if *memprofilerateflag != 0 {
   188  			runtime.MemProfileRate = *memprofilerateflag
   189  		}
   190  		f, err := os.Create(*memprofileflag)
   191  		if err != nil {
   192  			fatal("%v", err)
   193  		}
   194  		atExit(func() {
   195  			runtime.GC()
   196  			const writeLegacyFormat = 1
   197  			if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil {
   198  				fatal("%v", err)
   199  			}
   200  			if err = f.Close(); err != nil {
   201  				fatal("error closing memory profile: %v", err)
   202  			}
   203  		})
   204  	} else {
   205  		// Not doing memory profiling; disable it entirely.
   206  		runtime.MemProfileRate = 0
   207  	}
   208  
   209  	// Mode-dependent setup.
   210  	op.Setup()
   211  
   212  	// ... off and running now.
   213  	dbgtrace(1, "starting perform")
   214  
   215  	indirs := strings.Split(*indirsflag, ",")
   216  	vis := cov.CovDataVisitor(op)
   217  	var flags cov.CovDataReaderFlags
   218  	if *hflag {
   219  		flags |= cov.PanicOnError
   220  	}
   221  	if *hwflag {
   222  		flags |= cov.PanicOnWarning
   223  	}
   224  	reader := cov.MakeCovDataReader(vis, indirs, *verbflag, flags, matchpkg)
   225  	st := 0
   226  	if err := reader.Visit(); err != nil {
   227  		fmt.Fprintf(os.Stderr, "error: %v\n", err)
   228  		st = 1
   229  	}
   230  	dbgtrace(1, "leaving main")
   231  	Exit(st)
   232  }
   233  

View as plain text