aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2023-08-22 12:51:02 +0200
committerAleksandr Nogikh <nogikh@google.com>2023-08-22 12:20:24 +0000
commit96546aceffcf9f774c2f704a3f8348930a66b4cb (patch)
tree8eef9f333739687e9f75a84753861d7af5113089
parent91132985a7ff76db390949ac765113cfd3178fa7 (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.go20
-rw-r--r--dashboard/app/reporting_test.go66
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)
+ })
+ }
+}