| // Copyright 2022 syzkaller project authors. All rights reserved. |
| // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. |
| |
| package prog |
| |
| import ( |
| "math/rand" |
| "sort" |
| "testing" |
| |
| "github.com/google/syzkaller/pkg/image" |
| "github.com/google/syzkaller/pkg/testutil" |
| ) |
| |
| func TestGenericHeatmap(t *testing.T) { |
| t.Parallel() |
| // A test case is some data with the regions the heatmap is permitted to choose. |
| testData := []struct { |
| data []byte |
| regions []region |
| }{ |
| { |
| // Normal usage test. |
| []byte( |
| "4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh" + |
| "4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh" + |
| "4eHh4eHh4eHh4eHh4Q5GTbHh4eHh4eHh4eHh4eHhcOHh4eHh4eHh4eHh4eHh4eHh4eHh4eEfNuHh4XPh" + |
| "4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHjd+GRzcLh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh" + |
| "4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4dpiSwpoReHh4eHh4eHh4eHh4eHh4eHh4eHh" + |
| "4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4bGfM+Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh" + |
| "4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh" + |
| "4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh" + |
| "4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh" + |
| "mpNKOZnS4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh" + |
| "4eHh4eHh4eHh4eHh4eHh4Q=="), |
| []region{{128, 384}, {512, 576}}, |
| }, |
| { |
| // Test all constant bytes, i.e. falling back to uniform selection. |
| []byte( |
| "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + |
| "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + |
| "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + |
| "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + |
| "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + |
| "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), |
| []region{{0, 324}}, // Anywhere in the data. |
| }, |
| } |
| |
| const tries = 10 |
| iters := testutil.IterCount() / tries |
| |
| r := rand.New(testutil.RandSource(t)) |
| for _, test := range testData { |
| data, err := image.DecodeB64(test.data) |
| if err != nil { |
| t.Fatalf("bad decode: %v", err) |
| } |
| for i := 0; i < iters; i++ { |
| hm := MakeGenericHeatmap(data, r).(*GenericHeatmap) |
| |
| for j := 0; j < tries; j++ { |
| index := hm.ChooseLocation() |
| if !checkIndex(index, len(data), test.regions) { |
| hm.debugPrint(t, data, test.regions) |
| t.Fatalf("selected index %d does not fall in a region", index) |
| } |
| } |
| } |
| } |
| } |
| |
| // Check an index is within some regions. |
| func checkIndex(index, maxIndex int, regions []region) bool { |
| if index < 0 || index >= maxIndex { |
| return false |
| } |
| |
| for _, region := range regions { |
| if region.start <= index && index < region.end { |
| return true |
| } |
| } |
| return false |
| } |
| |
| type region struct { |
| start int |
| end int |
| } |
| |
| func (hm *GenericHeatmap) debugPrint(t *testing.T, data []byte, regions []region) { |
| // Print data. |
| t.Logf("data: len = %d", len(data)) |
| for j := 0; j < len(data); j += granularity { |
| end := j + granularity |
| if end > len(data) { |
| end = len(data) |
| } |
| t.Logf("%8d: %x", j*granularity, data[j:end]) |
| } |
| t.Log("\n") |
| |
| // Print selected regions in data. |
| sort.Slice(regions, func(i, j int) bool { |
| return regions[i].start < regions[j].start |
| }) |
| for j, region := range regions { |
| t.Logf("region %4d: %8v - %8v", j, region.start, region.end) |
| } |
| t.Log("\n") |
| |
| // Print heatmap. |
| t.Logf("generic heatmap (total segment length %d)", hm.length) |
| for j, seg := range hm.segments { |
| t.Logf("segment %4d: %8v - %8v", j, seg.offset, seg.offset+seg.length) |
| } |
| t.Log("\n\n\n") |
| } |