File: driver.go

package info (click to toggle)
golang-golang-x-tools 1%3A0.25.0%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 22,724 kB
  • sloc: javascript: 2,027; asm: 1,645; sh: 166; yacc: 155; makefile: 49; ansic: 8
file content (168 lines) | stat: -rw-r--r-- 6,019 bytes parent folder | download | duplicates (3)
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")
}