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