diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2020-01-17 19:30:03 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2020-01-18 21:02:24 +0100 |
| commit | de577addbf322eaa76ab50478593df7b11f197ff (patch) | |
| tree | 77e5da928957cf405c8330d212f96e8f2f05d43c /prog | |
| parent | 22535fecd5b37c6a14b92b17e548c8061ef77924 (diff) | |
prog: tune flags generation/mutation
Tune flags to generate more sane values over insane values
based on examination of results for common cases.
Diffstat (limited to 'prog')
| -rw-r--r-- | prog/rand.go | 93 | ||||
| -rw-r--r-- | prog/rand_test.go | 56 | ||||
| -rw-r--r-- | prog/types.go | 2 |
3 files changed, 119 insertions, 32 deletions
diff --git a/prog/rand.go b/prog/rand.go index 70a936870..bb3c81789 100644 --- a/prog/rand.go +++ b/prog/rand.go @@ -173,39 +173,70 @@ func (r *randGen) randPageCount() (n uint64) { } // Change a flag value or generate a new one. -func (r *randGen) flags(vv []uint64, bitmask bool, oldVal uint64) (v uint64) { - v = oldVal - if r.oneOf(5) { - // Ignore the old value sometimes. - v = 0 - } - switch { - case (bitmask && r.nOutOf(7, 10)) || (!bitmask && r.nOutOf(1, 5)): - // Try flipping randomly chosen flags. - // Prioritized when bitmask == true. - for stop := false; !stop; stop = r.oneOf(3) { - flag := vv[r.rand(len(vv))] - if r.oneOf(5) { - // Try choosing adjacent bit values in case we forgot - // to add all relevant flags to the descriptions. - if r.bin() { - flag >>= 1 - } else { - flag <<= 1 - } +// If you are changing this function, run TestFlags and examine effect of results. +func (r *randGen) flags(vv []uint64, bitmask bool, oldVal uint64) uint64 { + // Get these simpler cases out of the way first. + // Once in a while we want to return completely random values, + // or 0 which is frequently special. + if r.oneOf(100) { + return r.rand64() + } + if r.oneOf(50) { + return 0 + } + if !bitmask && oldVal != 0 && r.oneOf(100) { + // Slightly increment/decrement the old value. + // This is especially important during mutation when len(vv) == 1, + // otherwise in that case we produce almost no randomness + // (the value is always mutated to 0). + inc := uint64(1) + if r.bin() { + inc = ^uint64(0) + } + v := oldVal + inc + for r.bin() { + v += inc + } + return v + } + if len(vv) == 1 { + // This usually means that value or 0, + // at least that's our best (and only) bet. + if r.bin() { + return 0 + } + return vv[0] + } + if !bitmask && !r.oneOf(10) { + // Enumeration, so just choose one of the values. + return vv[r.rand(len(vv))] + } + if r.oneOf(len(vv) + 4) { + return 0 + } + // Flip rand bits. Do this for non-bitmask sometimes + // because we may have detected bitmask incorrectly for complex cases + // (e.g. part of the vlaue is bitmask and another is not). + v := oldVal + if v != 0 && r.oneOf(10) { + v = 0 // Ignore the old value sometimes. + } + // We don't want to return 0 here, because we already given 0 + // fixed probability above (otherwise we get 0 too frequently). + for v == 0 || r.nOutOf(2, 3) { + flag := vv[r.rand(len(vv))] + if r.oneOf(20) { + // Try choosing adjacent bit values in case we forgot + // to add all relevant flags to the descriptions. + if r.bin() { + flag >>= 1 + } else { + flag <<= 1 } - v ^= flag - } - case (bitmask && r.nOutOf(2, 3)) || (!bitmask && r.nOutOf(7, 8)): - // Chose a random flag. - // Prioritized when bitmask == false. - v = vv[r.rand(len(vv))] - case r.bin(): - v = 0 - default: - v = r.rand64() + } + v ^= flag } - return + return v } func (r *randGen) filename(s *state, typ *BufferType) string { diff --git a/prog/rand_test.go b/prog/rand_test.go index a17d60853..d308bf890 100644 --- a/prog/rand_test.go +++ b/prog/rand_test.go @@ -4,7 +4,10 @@ package prog import ( + "bytes" + "fmt" "math/rand" + "sort" "testing" ) @@ -113,3 +116,56 @@ func TestSizeGenerateConstArg(t *testing.T) { }) } } + +func TestFlags(t *testing.T) { + // This test does not test anything, it just prints resulting + // distribution of values for different scenarios. + tests := []struct { + vv []uint64 + bitmask bool + old uint64 + }{ + {[]uint64{0, 1, 2, 3}, false, 0}, + {[]uint64{0, 1, 2, 3}, false, 2}, + {[]uint64{1, 2, 3, 4}, false, 0}, + {[]uint64{1, 2, 3, 4}, false, 2}, + {[]uint64{1, 2, 4, 8}, true, 0}, + {[]uint64{1, 2, 4, 8}, true, 2}, + {[]uint64{7}, false, 0}, + {[]uint64{7}, false, 7}, + {[]uint64{1, 2}, true, 0}, + {[]uint64{1, 2}, true, 2}, + } + target, rs, _ := initRandomTargetTest(t, "test", "64") + r := newRand(target, rs) + for _, test := range tests { + results := make(map[uint64]uint64) + const throws = 1e4 + for i := 0; i < throws; i++ { + var v uint64 + for { + v = r.flags(test.vv, test.bitmask, test.old) + if test.old == 0 || test.old != v { + break + } + } + if v > 100 { + v = 999 // to not print all possible random values we generated + } + results[v]++ + } + var sorted [][2]uint64 + for v, c := range results { + sorted = append(sorted, [2]uint64{v, c}) + } + sort.Slice(sorted, func(i, j int) bool { + return sorted[i][0] < sorted[j][0] + }) + buf := new(bytes.Buffer) + for _, p := range sorted { + fmt.Fprintf(buf, "%v\t%v\n", p[0], p[1]) + } + t.Logf("test: vv=%+v bitmask=%v old=%v\nvalue\ttimes (out of %v)\n%v", + test.vv, test.bitmask, test.old, throws, buf.String()) + } +} diff --git a/prog/types.go b/prog/types.go index 68a1330c0..bebf0aaea 100644 --- a/prog/types.go +++ b/prog/types.go @@ -304,7 +304,7 @@ func (t *IntType) isDefaultArg(arg Arg) bool { type FlagsType struct { IntTypeCommon - Vals []uint64 + Vals []uint64 // compiler ensures that it's not empty BitMask bool } |
