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 | } |
169 |
Members