github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/cmd/signature-fuzzer/fuzz-driver/driver.go (about)

     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  // Stand-alone driver for emitting function-signature test code.  This
     6  // program is mainly just a wrapper around the code that lives in the
     7  // fuzz-generator package; it is useful for generating a specific bad
     8  // code scenario for a given seed, or for doing development on the
     9  // fuzzer, but for doing actual fuzz testing, better to use
    10  // fuzz-runner.
    11  
    12  package main
    13  
    14  import (
    15  	"flag"
    16  	"fmt"
    17  	"log"
    18  	"math/rand"
    19  	"os"
    20  	"time"
    21  
    22  	generator "golang.org/x/tools/cmd/signature-fuzzer/internal/fuzz-generator"
    23  )
    24  
    25  // Basic options
    26  var numfcnflag = flag.Int("numfcns", 10, "Number of test func pairs to emit in each package")
    27  var numpkgflag = flag.Int("numpkgs", 1, "Number of test packages to emit")
    28  var seedflag = flag.Int64("seed", -1, "Random seed")
    29  var tagflag = flag.String("tag", "gen", "Prefix name of go files/pkgs to generate")
    30  var outdirflag = flag.String("outdir", "", "Output directory for generated files")
    31  var pkgpathflag = flag.String("pkgpath", "gen", "Base package path for generated files")
    32  
    33  // Options used for test case minimization.
    34  var fcnmaskflag = flag.String("fcnmask", "", "Mask containing list of fcn numbers to emit")
    35  var pkmaskflag = flag.String("pkgmask", "", "Mask containing list of pkg numbers to emit")
    36  
    37  // Options used to control which features are used in the generated code.
    38  var reflectflag = flag.Bool("reflect", true, "Include testing of reflect.Call.")
    39  var deferflag = flag.Bool("defer", true, "Include testing of defer stmts.")
    40  var recurflag = flag.Bool("recur", true, "Include testing of recursive calls.")
    41  var takeaddrflag = flag.Bool("takeaddr", true, "Include functions that take the address of their parameters and results.")
    42  var methodflag = flag.Bool("method", true, "Include testing of method calls.")
    43  var inlimitflag = flag.Int("inmax", -1, "Max number of input params.")
    44  var outlimitflag = flag.Int("outmax", -1, "Max number of input params.")
    45  var pragmaflag = flag.String("pragma", "", "Tag generated test routines with pragma //go:<value>.")
    46  var maxfailflag = flag.Int("maxfail", 10, "Maximum runtime failures before test self-terminates")
    47  var stackforceflag = flag.Bool("forcestackgrowth", true, "Use hooks to force stack growth.")
    48  
    49  // Debugging options
    50  var verbflag = flag.Int("v", 0, "Verbose trace output level")
    51  
    52  // Debugging/testing options. These tell the generator to emit "bad" code so as to
    53  // test the logic for detecting errors and/or minimization (in the fuzz runner).
    54  var emitbadflag = flag.Int("emitbad", 0, "[Testing only] force generator to emit 'bad' code.")
    55  var selbadpkgflag = flag.Int("badpkgidx", 0, "[Testing only] select index of bad package (used with -emitbad)")
    56  var selbadfcnflag = flag.Int("badfcnidx", 0, "[Testing only] select index of bad function (used with -emitbad)")
    57  
    58  // Misc options
    59  var goimpflag = flag.Bool("goimports", false, "Run 'goimports' on generated code.")
    60  var randctlflag = flag.Int("randctl", generator.RandCtlChecks|generator.RandCtlPanic, "Wraprand control flag")
    61  
    62  func verb(vlevel int, s string, a ...interface{}) {
    63  	if *verbflag >= vlevel {
    64  		fmt.Printf(s, a...)
    65  		fmt.Printf("\n")
    66  	}
    67  }
    68  
    69  func usage(msg string) {
    70  	if len(msg) > 0 {
    71  		fmt.Fprintf(os.Stderr, "error: %s\n", msg)
    72  	}
    73  	fmt.Fprintf(os.Stderr, "usage: fuzz-driver [flags]\n\n")
    74  	flag.PrintDefaults()
    75  	fmt.Fprintf(os.Stderr, "Example:\n\n")
    76  	fmt.Fprintf(os.Stderr, "  fuzz-driver -numpkgs=23 -numfcns=19 -seed 10101 -outdir gendir\n\n")
    77  	fmt.Fprintf(os.Stderr, "  \tgenerates a Go program with 437 test cases (23 packages, each \n")
    78  	fmt.Fprintf(os.Stderr, "  \twith 19 functions, for a total of 437 funcs total) into a set of\n")
    79  	fmt.Fprintf(os.Stderr, "  \tsub-directories in 'gendir', using random see 10101\n")
    80  
    81  	os.Exit(2)
    82  }
    83  
    84  func setupTunables() {
    85  	tunables := generator.DefaultTunables()
    86  	if !*reflectflag {
    87  		tunables.DisableReflectionCalls()
    88  	}
    89  	if !*deferflag {
    90  		tunables.DisableDefer()
    91  	}
    92  	if !*recurflag {
    93  		tunables.DisableRecursiveCalls()
    94  	}
    95  	if !*takeaddrflag {
    96  		tunables.DisableTakeAddr()
    97  	}
    98  	if !*methodflag {
    99  		tunables.DisableMethodCalls()
   100  	}
   101  	if *inlimitflag != -1 {
   102  		tunables.LimitInputs(*inlimitflag)
   103  	}
   104  	if *outlimitflag != -1 {
   105  		tunables.LimitOutputs(*outlimitflag)
   106  	}
   107  	generator.SetTunables(tunables)
   108  }
   109  
   110  func main() {
   111  	log.SetFlags(0)
   112  	log.SetPrefix("fuzz-driver: ")
   113  	flag.Parse()
   114  	generator.Verbctl = *verbflag
   115  	if *outdirflag == "" {
   116  		usage("select an output directory with -o flag")
   117  	}
   118  	verb(1, "in main verblevel=%d", *verbflag)
   119  	if *seedflag == -1 {
   120  		// user has not selected a specific seed -- pick one.
   121  		now := time.Now()
   122  		*seedflag = now.UnixNano() % 123456789
   123  		verb(0, "selected seed: %d", *seedflag)
   124  	}
   125  	rand.Seed(*seedflag)
   126  	if flag.NArg() != 0 {
   127  		usage("unknown extra arguments")
   128  	}
   129  	verb(1, "tag is %s", *tagflag)
   130  
   131  	fcnmask, err := generator.ParseMaskString(*fcnmaskflag, "fcn")
   132  	if err != nil {
   133  		usage(fmt.Sprintf("mangled fcn mask arg: %v", err))
   134  	}
   135  	pkmask, err := generator.ParseMaskString(*pkmaskflag, "pkg")
   136  	if err != nil {
   137  		usage(fmt.Sprintf("mangled pkg mask arg: %v", err))
   138  	}
   139  	verb(2, "pkg mask is %v", pkmask)
   140  	verb(2, "fn mask is %v", fcnmask)
   141  
   142  	verb(1, "starting generation")
   143  	setupTunables()
   144  	config := generator.GenConfig{
   145  		PkgPath:          *pkgpathflag,
   146  		Tag:              *tagflag,
   147  		OutDir:           *outdirflag,
   148  		NumTestPackages:  *numpkgflag,
   149  		NumTestFunctions: *numfcnflag,
   150  		Seed:             *seedflag,
   151  		Pragma:           *pragmaflag,
   152  		FcnMask:          fcnmask,
   153  		PkgMask:          pkmask,
   154  		MaxFail:          *maxfailflag,
   155  		ForceStackGrowth: *stackforceflag,
   156  		RandCtl:          *randctlflag,
   157  		RunGoImports:     *goimpflag,
   158  		EmitBad:          *emitbadflag,
   159  		BadPackageIdx:    *selbadpkgflag,
   160  		BadFuncIdx:       *selbadfcnflag,
   161  	}
   162  	errs := generator.Generate(config)
   163  	if errs != 0 {
   164  		log.Fatal("errors during generation")
   165  	}
   166  	verb(1, "... files written to directory %s", *outdirflag)
   167  	verb(1, "leaving main")
   168  }