Skip to content

Commit 4609d79

Browse files
committed
cmd/bisect: add -compile and -godebug shorthands
This makes it easier for people to invoke bisect for its usual use cases: bisect -compile=loopvar go test Change-Id: I60ae74f145a2aef7b8d028accc89264e80019d1e Reviewed-on: https://go-review.googlesource.com/c/tools/+/493856 Run-TryBot: Russ Cox <[email protected]> Reviewed-by: David Chase <[email protected]> TryBot-Result: Gopher Robot <[email protected]> gopls-CI: kokoro <[email protected]>
1 parent ddfa220 commit 4609d79

File tree

1 file changed

+90
-10
lines changed

1 file changed

+90
-10
lines changed

cmd/bisect/main.go

+90-10
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
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.
@@ -29,45 +29,84 @@
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
@@ -87,6 +126,8 @@
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
90131
package main
91132

92133
import (
@@ -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

Comments
 (0)