From 7013cb0d7d7b78bb0160c45d13a8d7d472835513 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Wed, 7 Aug 2024 15:16:35 +0200 Subject: prog: replace MinimizeParams with MinimizeMode All callers shouldn't control lots of internal details of minimization (if we have more params, that's just more variations to test, and we don't have more, params is just a more convoluted way to say if we minimize for corpus or a crash). 2 bools also allow to express 4 options, but only 3 make sense. Also when I see MinimizeParams{} in the code, it's unclear what it means. Replace params with mode. And potentially "crash" minimization is not "light", it's just different. E.g. we can simplify int arguments for reproducers (esp in snapshot mode), but we don't need that for corpus. --- prog/encoding_test.go | 2 +- prog/expr_test.go | 2 +- prog/minimization.go | 41 ++++++++++++++++++++--------------------- prog/minimization_test.go | 12 ++++++++---- prog/prog_test.go | 2 +- prog/rand_test.go | 4 ++-- 6 files changed, 33 insertions(+), 30 deletions(-) (limited to 'prog') diff --git a/prog/encoding_test.go b/prog/encoding_test.go index 24c479948..a577c5be5 100644 --- a/prog/encoding_test.go +++ b/prog/encoding_test.go @@ -408,7 +408,7 @@ func TestSerializeDeserializeRandom(t *testing.T) { if _, _, ok := testSerializeDeserialize(t, p0); ok { continue } - p0, _ = Minimize(p0, -1, MinimizeParams{}, func(p1 *Prog, _ int) bool { + p0, _ = Minimize(p0, -1, MinimizeCorpus, func(p1 *Prog, _ int) bool { _, _, ok := testSerializeDeserialize(t, p1) return !ok }) diff --git a/prog/expr_test.go b/prog/expr_test.go index bdb4201dd..6b767cd11 100644 --- a/prog/expr_test.go +++ b/prog/expr_test.go @@ -186,7 +186,7 @@ func TestConditionalMinimize(t *testing.T) { assert.NoError(tt, err) p, err := target.Deserialize([]byte(test.input), Strict) assert.NoError(tt, err) - p1, _ := Minimize(p, 0, MinimizeParams{}, test.pred) + p1, _ := Minimize(p, 0, MinimizeCorpus, test.pred) res := p1.Serialize() assert.Equal(tt, test.output, strings.TrimSpace(string(res))) }) diff --git a/prog/minimization.go b/prog/minimization.go index 57999b397..935c18232 100644 --- a/prog/minimization.go +++ b/prog/minimization.go @@ -30,25 +30,24 @@ var ( "Total number of filename minimization attempts", stat.StackedGraph("minimize")) ) -type MinimizeParams struct { - // CallIndex was intentionally not included in this struct, since its - // default value should be -1, while the default value of 0 would introduce a bug. - - // If RemoveCallsOnly is set to true, Minimize() focuses only on removing whole calls. - RemoveCallsOnly bool - - // Light speeds up the minimization by - // 1. Not removing array elements one by one. - // 2. Not bisecting blobs too much. - // 3. Not minimizing integer values. - Light bool -} +type MinimizeMode int + +const ( + // Minimize for inclusion into corpus. + // This generally tries to reduce number of arguments for future mutation. + MinimizeCorpus MinimizeMode = iota + // Minimize crash reproducer. + // This mode assumes each test is expensive (need to reboot), so tries fewer things. + MinimizeCrash + // Only try to remove calls. + MinimizeCallsOnly +) // Minimize minimizes program p into an equivalent program using the equivalence // predicate pred. It iteratively generates simpler programs and asks pred // whether it is equal to the original program or not. If it is equivalent then // the simplification attempt is committed and the process continues. -func Minimize(p0 *Prog, callIndex0 int, params MinimizeParams, pred0 func(*Prog, int) bool) (*Prog, int) { +func Minimize(p0 *Prog, callIndex0 int, mode MinimizeMode, pred0 func(*Prog, int) bool) (*Prog, int) { pred := func(p *Prog, callIndex int, what *stat.Val) bool { what.Add(1) p.sanitizeFix() @@ -66,7 +65,7 @@ func Minimize(p0 *Prog, callIndex0 int, params MinimizeParams, pred0 func(*Prog, // Try to remove all calls except the last one one-by-one. p0, callIndex0 = removeCalls(p0, callIndex0, pred) - if !params.RemoveCallsOnly { + if mode != MinimizeCallsOnly { // Try to reset all call props to their default values. p0 = resetCallProps(p0, callIndex0, pred) @@ -79,7 +78,7 @@ func Minimize(p0 *Prog, callIndex0 int, params MinimizeParams, pred0 func(*Prog, target: p0.Target, p0: &p0, callIndex0: callIndex0, - params: params, + mode: mode, pred: pred, triedPaths: make(map[string]bool), } @@ -193,7 +192,7 @@ type minimizeArgsCtx struct { p *Prog call *Call callIndex0 int - params MinimizeParams + mode MinimizeMode pred minimizePred triedPaths map[string]bool } @@ -265,7 +264,7 @@ func (typ *ArrayType) minimize(ctx *minimizeArgsCtx, arg Arg, path string) bool elem := a.Inner[i] elemPath := fmt.Sprintf("%v-%v", path, i) // Try to remove individual elements one-by-one. - if !ctx.params.Light && !ctx.triedPaths[elemPath] && + if ctx.mode == MinimizeCorpus && !ctx.triedPaths[elemPath] && (typ.Kind == ArrayRandLen || typ.Kind == ArrayRangeLen && uint64(len(a.Inner)) > typ.RangeBegin) { ctx.triedPaths[elemPath] = true @@ -308,7 +307,7 @@ func (typ *ProcType) minimize(ctx *minimizeArgsCtx, arg Arg, path string) bool { func minimizeInt(ctx *minimizeArgsCtx, arg Arg, path string) bool { // TODO: try to reset bits in ints // TODO: try to set separate flags - if ctx.params.Light { + if ctx.mode == MinimizeCrash { return false } a := arg.(*ConstArg) @@ -336,7 +335,7 @@ func minimizeInt(ctx *minimizeArgsCtx, arg Arg, path string) bool { } func (typ *ResourceType) minimize(ctx *minimizeArgsCtx, arg Arg, path string) bool { - if ctx.params.Light { + if ctx.mode == MinimizeCrash { return false } a := arg.(*ResultArg) @@ -380,7 +379,7 @@ func (typ *BufferType) minimize(ctx *minimizeArgsCtx, arg Arg, path string) bool ctx.target.assignSizesCall(ctx.call) } step /= 2 - if ctx.params.Light { + if ctx.mode == MinimizeCrash { break } } diff --git a/prog/minimization_test.go b/prog/minimization_test.go index e0736206a..7862bcc01 100644 --- a/prog/minimization_test.go +++ b/prog/minimization_test.go @@ -251,7 +251,7 @@ func TestMinimize(t *testing.T) { if err != nil { t.Fatalf("failed to deserialize original program #%v: %v", ti, err) } - p1, ci := Minimize(p, test.callIndex, MinimizeParams{}, test.pred) + p1, ci := Minimize(p, test.callIndex, MinimizeCorpus, test.pred) res := p1.Serialize() if string(res) != test.result { t.Fatalf("minimization produced wrong result #%v\norig:\n%v\nexpect:\n%v\ngot:\n%v", @@ -270,10 +270,10 @@ func TestMinimizeRandom(t *testing.T) { ct := target.DefaultChoiceTable() r := rand.New(rs) for i := 0; i < iters; i++ { - for _, crash := range []bool{false, true} { + for _, mode := range []MinimizeMode{MinimizeCorpus, MinimizeCrash} { p := target.Generate(rs, 5, ct) copyP := p.Clone() - minP, _ := Minimize(p, len(p.Calls)-1, MinimizeParams{Light: crash}, func(p1 *Prog, callIndex int) bool { + minP, _ := Minimize(p, len(p.Calls)-1, mode, func(p1 *Prog, callIndex int) bool { if r.Intn(2) == 0 { return false } @@ -296,7 +296,11 @@ func TestMinimizeCallIndex(t *testing.T) { for i := 0; i < iters; i++ { p := target.Generate(rs, 5, ct) ci := r.Intn(len(p.Calls)) - p1, ci1 := Minimize(p, ci, MinimizeParams{Light: r.Intn(2) == 0}, func(p1 *Prog, callIndex int) bool { + mode := MinimizeCorpus + if r.Intn(2) == 0 { + mode = MinimizeCrash + } + p1, ci1 := Minimize(p, ci, mode, func(p1 *Prog, callIndex int) bool { return r.Intn(2) == 0 }) if ci1 < 0 || ci1 >= len(p1.Calls) || p.Calls[ci].Meta.Name != p1.Calls[ci1].Meta.Name { diff --git a/prog/prog_test.go b/prog/prog_test.go index e6c7f4a2b..29f2aee5b 100644 --- a/prog/prog_test.go +++ b/prog/prog_test.go @@ -195,7 +195,7 @@ func testCrossTarget(t *testing.T, target *Target, crossTargets []*Target) { testCrossArchProg(t, p, crossTargets) p.Mutate(rs, 20, ct, nil, nil) testCrossArchProg(t, p, crossTargets) - p, _ = Minimize(p, -1, MinimizeParams{}, func(*Prog, int) bool { + p, _ = Minimize(p, -1, MinimizeCorpus, func(*Prog, int) bool { return rs.Int63()%2 == 0 }) testCrossArchProg(t, p, crossTargets) diff --git a/prog/rand_test.go b/prog/rand_test.go index d1e963595..b165bd410 100644 --- a/prog/rand_test.go +++ b/prog/rand_test.go @@ -72,8 +72,8 @@ func generateProg(t *testing.T, target *Target, rs rand.Source, ct *ChoiceTable, return limit > 0 }) } - for _, crash := range []bool{false, true} { - p, _ = Minimize(p, -1, MinimizeParams{Light: crash}, func(*Prog, int) bool { + for _, mode := range []MinimizeMode{MinimizeCorpus, MinimizeCrash} { + p, _ = Minimize(p, -1, mode, func(*Prog, int) bool { return rs.Int63()%10 == 0 }) } -- cgit mrf-deployment