diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2023-08-22 12:51:02 +0200 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2023-08-22 12:20:24 +0000 |
| commit | 96546aceffcf9f774c2f704a3f8348930a66b4cb (patch) | |
| tree | 8eef9f333739687e9f75a84753861d7af5113089 | |
| parent | 91132985a7ff76db390949ac765113cfd3178fa7 (diff) | |
dashboard/app: calculate obsoletion periods differently
Rely on the Poisson distribution to determine the moment when we should
give on a bug.
We don't have to wait 100x the average time between the crashes.
| -rw-r--r-- | dashboard/app/reporting.go | 20 | ||||
| -rw-r--r-- | dashboard/app/reporting_test.go | 66 |
2 files changed, 82 insertions, 4 deletions
diff --git a/dashboard/app/reporting.go b/dashboard/app/reporting.go index 3917adb56..27b1fed0b 100644 --- a/dashboard/app/reporting.go +++ b/dashboard/app/reporting.go @@ -7,6 +7,7 @@ import ( "bytes" "encoding/json" "fmt" + "math" "reflect" "sort" "strings" @@ -346,13 +347,24 @@ func (bug *Bug) obsoletePeriod() time.Duration { if config.Obsoleting.MinPeriod == 0 { return period } + + // Let's assume that crashes follow the Possion distribution with rate r=crashes/days. + // Then, the chance of seeing a crash within t days is p=1-e^(-r*t). + // Solving it for t, we get t=log(1/(1-p))/r. + // Since our rate is also only an estimate, let's require p=0.99. + // Before we have at least 10 crashes, any estimation of frequency is too imprecise. // In such case we conservatively assume it still happens. if bug.NumCrashes >= 10 { - // This is linear extrapolation for when the next crash should happen. - period = bug.LastTime.Sub(bug.FirstTime) / time.Duration(bug.NumCrashes-1) - // Let's be conservative with obsoleting too early. - period *= 100 + bugDays := bug.LastTime.Sub(bug.FirstTime).Hours() / 24.0 + rate := float64(bug.NumCrashes-1) / bugDays + + const probability = 0.99 + // For 1 crash/day, this will be ~4.6 days. + days := math.Log(1.0/(1.0-probability)) / rate + // Let's be conservative and multiply it by 3. + days = days * 3 + period = time.Hour * time.Duration(24*days) } min, max := config.Obsoleting.MinPeriod, config.Obsoleting.MaxPeriod if config.Obsoleting.NonFinalMinPeriod != 0 && diff --git a/dashboard/app/reporting_test.go b/dashboard/app/reporting_test.go index a1d991cfa..7ef314df4 100644 --- a/dashboard/app/reporting_test.go +++ b/dashboard/app/reporting_test.go @@ -15,6 +15,7 @@ import ( "github.com/google/syzkaller/dashboard/dashapi" "github.com/google/syzkaller/pkg/email" "github.com/google/syzkaller/sys/targets" + "github.com/stretchr/testify/assert" ) func TestReportBug(t *testing.T) { @@ -1105,3 +1106,68 @@ func TestReportDecommissionedBugs(t *testing.T) { c.expectEQ(len(closed), 1) c.expectEQ(closed[0], rep.ID) } + +func TestObsoletePeriod(t *testing.T) { + base := time.Now() + tests := []struct { + name string + bug *Bug + period time.Duration + }{ + { + name: "frequent final bug", + bug: &Bug{ + // Once in a day. + NumCrashes: 30, + FirstTime: base, + LastTime: base.Add(time.Hour * 24 * 30), + Reporting: []BugReporting{{Reported: base}}, + }, + // 80 days are definitely enough. + period: config.Obsoleting.MinPeriod, + }, + { + name: "very short-living final bug", + bug: &Bug{ + NumCrashes: 5, + FirstTime: base, + LastTime: base.Add(time.Hour * 24), + Reporting: []BugReporting{{Reported: base}}, + }, + // Too few crashes, wait max time. + period: config.Obsoleting.MaxPeriod, + }, + { + name: "rare stable final bug", + bug: &Bug{ + // Once in 20 days. + NumCrashes: 20, + FirstTime: base, + LastTime: base.Add(time.Hour * 24 * 400), + Reporting: []BugReporting{{Reported: base}}, + }, + // Wait max time. + period: config.Obsoleting.MaxPeriod, + }, + { + name: "frequent non-final bug", + bug: &Bug{ + // Once in a day. + NumCrashes: 10, + FirstTime: base, + LastTime: base.Add(time.Hour * 24 * 10), + Reporting: []BugReporting{{}}, + }, + // 40 days are also enough. + period: config.Obsoleting.NonFinalMinPeriod, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + ret := test.bug.obsoletePeriod() + assert.Equal(t, test.period, ret) + }) + } +} |
