1616// code zero). With all the changes enabled, the target is known to fail
1717// (exit any other way). Bisect repeats the target with different sets of
1818// changes enabled, using binary search to find (non-overlapping) minimal
19- // change sets that preserve the failure.
19+ // change sets that provoke the failure.
2020//
2121// The target must cooperate with bisect by accepting a change pattern
2222// and then enabling and reporting the changes that match that pattern.
2929// targets implement this protocol. We plan to publish that package
3030// in a non-internal location after finalizing its API.
3131//
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+ //
3243// # Command Line Flags
3344//
3445// Bisect supports the following command-line flags:
3546//
36- // -max M
47+ // -max= M
3748//
3849// Stop after finding M minimal change sets. The default is no maximum, meaning to run until
3950// all changes that provoke a failure have been identified.
4051//
41- // -maxset S
52+ // -maxset= S
4253//
4354// Disallow change sets larger than S elements. The default is no maximum.
4455//
45- // -timeout D
56+ // -timeout= D
4657//
4758// If the target runs for longer than duration D, stop the target and interpret that as a failure.
4859// The default is no timeout.
4960//
50- // -count N
61+ // -count= N
5162//
5263// Run each trial N times (default 2), checking for consistency.
5364//
5465// -v
5566//
5667// Print verbose output, showing each run and its match lines.
5768//
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+ //
5888// # Example
5989//
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:
6394//
6495// bisect go test -gcflags=all=-d=loopvarhash=PATTERN
6596//
6697// 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
6999// needed to reproduce the failure.
70100//
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+ //
71110// # Defeating Build Caches
72111//
73112// Build systems cache build results, to avoid repeating the same compilations
87126// previous example using Bazel, the invocation is:
88127//
89128// bazel test --define=gc_goopts=-d=loopvarhash=PATTERN,unused=RANDOM //path/to:test
129+ //
130+ // [GODEBUG setting]: https://tip.golang.org/doc/godebug
90131package main
91132
92133import (
@@ -127,6 +168,25 @@ func main() {
127168 flag .IntVar (& b .Count , "count" , 2 , "run target `n` times for each trial" )
128169 flag .BoolVar (& b .Verbose , "v" , false , "enable verbose output" )
129170
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+
130190 flag .Usage = usage
131191 flag .Parse ()
132192 args := flag .Args ()
@@ -140,6 +200,26 @@ func main() {
140200 usage ()
141201 }
142202 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+
143223 if ! b .Search () {
144224 os .Exit (1 )
145225 }
0 commit comments