16
16
// code zero). With all the changes enabled, the target is known to fail
17
17
// (exit any other way). Bisect repeats the target with different sets of
18
18
// changes enabled, using binary search to find (non-overlapping) minimal
19
- // change sets that preserve the failure.
19
+ // change sets that provoke the failure.
20
20
//
21
21
// The target must cooperate with bisect by accepting a change pattern
22
22
// and then enabling and reporting the changes that match that pattern.
29
29
// targets implement this protocol. We plan to publish that package
30
30
// in a non-internal location after finalizing its API.
31
31
//
32
+ // Bisect starts by running the target with no changes enabled and then
33
+ // with all changes enabled. It expects the former to succeed and the latter to fail,
34
+ // and then it will search for the minimal set of changes that must be enabled
35
+ // to provoke the failure. If the situation is reversed – the target fails with no
36
+ // changes enabled and succeeds with all changes enabled – then bisect
37
+ // automatically runs in reverse as well, searching for the minimal set of changes
38
+ // that must be disabled to provoke the failure.
39
+ //
40
+ // Bisect prints tracing logs to standard error and the minimal change sets
41
+ // to standard output.
42
+ //
32
43
// # Command Line Flags
33
44
//
34
45
// Bisect supports the following command-line flags:
35
46
//
36
- // -max M
47
+ // -max= M
37
48
//
38
49
// Stop after finding M minimal change sets. The default is no maximum, meaning to run until
39
50
// all changes that provoke a failure have been identified.
40
51
//
41
- // -maxset S
52
+ // -maxset= S
42
53
//
43
54
// Disallow change sets larger than S elements. The default is no maximum.
44
55
//
45
- // -timeout D
56
+ // -timeout= D
46
57
//
47
58
// If the target runs for longer than duration D, stop the target and interpret that as a failure.
48
59
// The default is no timeout.
49
60
//
50
- // -count N
61
+ // -count= N
51
62
//
52
63
// Run each trial N times (default 2), checking for consistency.
53
64
//
54
65
// -v
55
66
//
56
67
// Print verbose output, showing each run and its match lines.
57
68
//
69
+ // In addition to these general flags,
70
+ // bisect supports a few “shortcut” flags that make it more convenient
71
+ // to use with specific targets.
72
+ //
73
+ // -compile=<rewrite>
74
+ //
75
+ // This flag is equivalent to adding an environment variable
76
+ // “GOCOMPILEDEBUG=<rewrite>hash=PATTERN”,
77
+ // which, as discussed in more detail in the example below,
78
+ // allows bisect to identify the specific source locations where the
79
+ // compiler rewrite causes the target to fail.
80
+ //
81
+ // -godebug=<name>=<value>
82
+ //
83
+ // This flag is equivalent to adding an environment variable
84
+ // “GODEBUG=<name>=<value>#PATTERN”,
85
+ // which allows bisect to identify the specific call stacks where
86
+ // the changed [GODEBUG setting] value causes the target to fail.
87
+ //
58
88
// # Example
59
89
//
60
- // For example, the Go compiler can be used as a bisect target to
61
- // determine the source locations that cause a test failure when compiled with
62
- // a new optimization:
90
+ // The Go compiler provides support for enabling or disabling certain rewrites
91
+ // and optimizations to allow bisect to identify specific source locations where
92
+ // the rewrite causes the program to fail. For example, to bisect a failure caused
93
+ // by the new loop variable semantics:
63
94
//
64
95
// bisect go test -gcflags=all=-d=loopvarhash=PATTERN
65
96
//
66
97
// The -gcflags=all= instructs the go command to pass the -d=... to the Go compiler
67
- // when compiling all packages. Bisect replaces the literal text “PATTERN” with a specific pattern
68
- // on each invocation, varying the patterns to determine the minimal set of changes
98
+ // when compiling all packages. Bisect varies PATTERN to determine the minimal set of changes
69
99
// needed to reproduce the failure.
70
100
//
101
+ // The go command also checks the GOCOMPILEDEBUG environment variable for flags
102
+ // to pass to the compiler, so the above command is equivalent to:
103
+ //
104
+ // bisect GOCOMPILEDEBUG=loopvarhash=PATTERN go test
105
+ //
106
+ // Finally, as mentioned earlier, the -compile flag allows shortening this command further:
107
+ //
108
+ // bisect -compile=loopvar go test
109
+ //
71
110
// # Defeating Build Caches
72
111
//
73
112
// Build systems cache build results, to avoid repeating the same compilations
87
126
// previous example using Bazel, the invocation is:
88
127
//
89
128
// bazel test --define=gc_goopts=-d=loopvarhash=PATTERN,unused=RANDOM //path/to:test
129
+ //
130
+ // [GODEBUG setting]: https://tip.golang.org/doc/godebug
90
131
package main
91
132
92
133
import (
@@ -127,6 +168,25 @@ func main() {
127
168
flag .IntVar (& b .Count , "count" , 2 , "run target `n` times for each trial" )
128
169
flag .BoolVar (& b .Verbose , "v" , false , "enable verbose output" )
129
170
171
+ env := ""
172
+ envFlag := ""
173
+ flag .Func ("compile" , "bisect source locations affected by Go compiler `rewrite` (fma, loopvar, ...)" , func (value string ) error {
174
+ if envFlag != "" {
175
+ return fmt .Errorf ("cannot use -%s and -compile" , envFlag )
176
+ }
177
+ envFlag = "compile"
178
+ env = "GOCOMPILEDEBUG=" + value + "hash=PATTERN"
179
+ return nil
180
+ })
181
+ flag .Func ("godebug" , "bisect call stacks affected by GODEBUG setting `name=value`" , func (value string ) error {
182
+ if envFlag != "" {
183
+ return fmt .Errorf ("cannot use -%s and -godebug" , envFlag )
184
+ }
185
+ envFlag = "godebug"
186
+ env = "GODEBUG=" + value + "#PATTERN"
187
+ return nil
188
+ })
189
+
130
190
flag .Usage = usage
131
191
flag .Parse ()
132
192
args := flag .Args ()
@@ -140,6 +200,26 @@ func main() {
140
200
usage ()
141
201
}
142
202
b .Env , b .Cmd , b .Args = args [:i ], args [i ], args [i + 1 :]
203
+ if env != "" {
204
+ b .Env = append ([]string {env }, b .Env ... )
205
+ }
206
+
207
+ // Check that PATTERN is available for us to vary.
208
+ found := false
209
+ for _ , e := range b .Env {
210
+ if _ , v , _ := strings .Cut (e , "=" ); strings .Contains (v , "PATTERN" ) {
211
+ found = true
212
+ }
213
+ }
214
+ for _ , a := range b .Args {
215
+ if strings .Contains (a , "PATTERN" ) {
216
+ found = true
217
+ }
218
+ }
219
+ if ! found {
220
+ log .Fatalf ("no PATTERN in target environment or args" )
221
+ }
222
+
143
223
if ! b .Search () {
144
224
os .Exit (1 )
145
225
}
0 commit comments