aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2018-06-11 10:02:49 +0200
committerDmitry Vyukov <dvyukov@google.com>2018-06-11 12:12:01 +0300
commitb30b1136b621a3b829bb4b80487e776fbfa0a2b0 (patch)
treeab25cbc40e70bbc47f47ebc1c63709a535b30d74
parenta7429096651d84e6a9cf6232f0166c23fbdee2ee (diff)
dashboard/app: try 1 repro per day until we have at least syz repro
Bugs without repros are not actionable sometimes. Fixes #627
-rw-r--r--dashboard/app/api.go19
-rw-r--r--dashboard/app/entities.go1
-rw-r--r--dashboard/app/repro_test.go37
3 files changed, 43 insertions, 14 deletions
diff --git a/dashboard/app/api.go b/dashboard/app/api.go
index 93ae5a275..8bf27cdc7 100644
--- a/dashboard/app/api.go
+++ b/dashboard/app/api.go
@@ -52,7 +52,10 @@ type JSONHandler func(c context.Context, r *http.Request) (interface{}, error)
type APIHandler func(c context.Context, r *http.Request, payload []byte) (interface{}, error)
type APINamespaceHandler func(c context.Context, ns string, r *http.Request, payload []byte) (interface{}, error)
-const maxReproPerBug = 10
+const (
+ maxReproPerBug = 10
+ reproRetryPeriod = 24 * time.Hour // try 1 repro per day until we have at least syz repro
+)
// Overridable for testing.
var timeNow = func(c context.Context) time.Time {
@@ -572,6 +575,7 @@ func reportCrash(c context.Context, ns string, req *dashapi.Crash) (*Bug, error)
}
if reproLevel != ReproLevelNone {
bug.NumRepro++
+ bug.LastReproTime = now
}
if bug.ReproLevel < reproLevel {
bug.ReproLevel = reproLevel
@@ -674,12 +678,14 @@ func apiReportFailedRepro(c context.Context, ns string, r *http.Request, payload
if bug == nil {
return nil, fmt.Errorf("%v: can't find bug for crash %q", ns, req.Title)
}
+ now := timeNow(c)
tx := func(c context.Context) error {
bug := new(Bug)
if err := datastore.Get(c, bugKey, bug); err != nil {
return fmt.Errorf("failed to get bug: %v", err)
}
bug.NumRepro++
+ bug.LastReproTime = now
if _, err := datastore.Put(c, bugKey, bug); err != nil {
return fmt.Errorf("failed to put bug: %v", err)
}
@@ -825,7 +831,7 @@ func isActiveBug(c context.Context, bug *Bug) (bool, error) {
}
func needRepro(c context.Context, bug *Bug) bool {
- if !needReproForBug(bug) {
+ if !needReproForBug(c, bug) {
return false
}
canon, err := canonicalBug(c, bug)
@@ -833,14 +839,15 @@ func needRepro(c context.Context, bug *Bug) bool {
log.Errorf(c, "failed to get canonical bug: %v", err)
return false
}
- return needReproForBug(canon)
+ return needReproForBug(c, canon)
}
-func needReproForBug(bug *Bug) bool {
+func needReproForBug(c context.Context, bug *Bug) bool {
return bug.ReproLevel < ReproLevelC &&
- bug.NumRepro < maxReproPerBug &&
len(bug.Commits) == 0 &&
- bug.Title != corruptedReportTitle
+ bug.Title != corruptedReportTitle &&
+ (bug.NumRepro < maxReproPerBug ||
+ bug.ReproLevel == ReproLevelNone && timeSince(c, bug.LastReproTime) > reproRetryPeriod)
}
func putText(c context.Context, ns, tag string, data []byte, dedup bool) (int64, error) {
diff --git a/dashboard/app/entities.go b/dashboard/app/entities.go
index 0f4b604e7..fd0325cf3 100644
--- a/dashboard/app/entities.go
+++ b/dashboard/app/entities.go
@@ -77,6 +77,7 @@ type Bug struct {
FirstTime time.Time
LastTime time.Time
LastSavedCrash time.Time
+ LastReproTime time.Time
Closed time.Time
Reporting []BugReporting
Commits []string
diff --git a/dashboard/app/repro_test.go b/dashboard/app/repro_test.go
index 3bb37a6d3..25f0ad938 100644
--- a/dashboard/app/repro_test.go
+++ b/dashboard/app/repro_test.go
@@ -7,6 +7,7 @@ package dash
import (
"testing"
+ "time"
"github.com/google/syzkaller/dashboard/dashapi"
)
@@ -87,17 +88,29 @@ func testNeedRepro3(t *testing.T, crashCtor func(c *Ctx) *dashapi.Crash) {
for i := 0; i < maxReproPerBug; i++ {
resp, _ := c.client.ReportCrash(crash1)
c.expectEQ(resp.NeedRepro, true)
-
needRepro, _ := c.client.NeedRepro(testCrashID(crash1))
c.expectEQ(needRepro, true)
c.client.ReportFailedRepro(testCrashID(crash1))
}
- resp, _ := c.client.ReportCrash(crash1)
- c.expectEQ(resp.NeedRepro, false)
-
- needRepro, _ := c.client.NeedRepro(testCrashID(crash1))
- c.expectEQ(needRepro, false)
+ for i := 0; i < 3; i++ {
+ // No more repros today.
+ c.advanceTime(time.Hour)
+ resp, _ := c.client.ReportCrash(crash1)
+ c.expectEQ(resp.NeedRepro, false)
+ needRepro, _ := c.client.NeedRepro(testCrashID(crash1))
+ c.expectEQ(needRepro, false)
+
+ // Then another repro after a day.
+ c.advanceTime(25 * time.Hour)
+ for j := 0; j < 2; j++ {
+ resp, _ := c.client.ReportCrash(crash1)
+ c.expectEQ(resp.NeedRepro, true)
+ needRepro, _ := c.client.NeedRepro(testCrashID(crash1))
+ c.expectEQ(needRepro, true)
+ }
+ c.client.ReportFailedRepro(testCrashID(crash1))
+ }
}
func TestNeedRepro3_normal(t *testing.T) { testNeedRepro3(t, normalCrash) }
@@ -116,16 +129,24 @@ func testNeedRepro4(t *testing.T, crashCtor func(c *Ctx) *dashapi.Crash) {
for i := 0; i < maxReproPerBug-1; i++ {
resp, _ := c.client.ReportCrash(crash1)
c.expectEQ(resp.NeedRepro, true)
-
needRepro, _ := c.client.NeedRepro(testCrashID(crash1))
c.expectEQ(needRepro, true)
}
resp, _ := c.client.ReportCrash(crash1)
c.expectEQ(resp.NeedRepro, false)
-
needRepro, _ := c.client.NeedRepro(testCrashID(crash1))
c.expectEQ(needRepro, false)
+
+ // No more repros even after a day.
+ c.advanceTime(25 * time.Hour)
+ crash1.ReproOpts = nil
+ crash1.ReproSyz = nil
+
+ resp, _ = c.client.ReportCrash(crash1)
+ c.expectEQ(resp.NeedRepro, false)
+ needRepro, _ = c.client.NeedRepro(testCrashID(crash1))
+ c.expectEQ(needRepro, false)
}
func TestNeedRepro4_normal(t *testing.T) { testNeedRepro4(t, normalCrash) }