diff options
author | Elliott Hughes <enh@google.com> | 2019-11-13 19:53:59 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2019-11-13 19:53:59 +0000 |
commit | 4be8363327b1283d9a06abbbae57be7f3a69ad89 (patch) | |
tree | 3abf13f01a970379dcd4f65d3674be4bb5832a2a /dashboard/app/reporting.go | |
parent | 3ca947b438679edac8de3e298789344194adb37b (diff) | |
parent | 7775171a9b00ea7c7ecaff772d35cc1cd87520e4 (diff) | |
download | syzkaller-4be8363327b1283d9a06abbbae57be7f3a69ad89.tar.gz |
Diffstat (limited to 'dashboard/app/reporting.go')
-rw-r--r-- | dashboard/app/reporting.go | 145 |
1 files changed, 121 insertions, 24 deletions
diff --git a/dashboard/app/reporting.go b/dashboard/app/reporting.go index ee36930c5..cb3f52cdd 100644 --- a/dashboard/app/reporting.go +++ b/dashboard/app/reporting.go @@ -31,7 +31,7 @@ const ( maxInlineError = 16 << 10 notifyResendPeriod = 14 * 24 * time.Hour notifyAboutBadCommitPeriod = 90 * 24 * time.Hour - autoObsoletePeriod = 180 * 24 * time.Hour + never = 100 * 365 * 24 * time.Hour internalError = "internal error" // This is embedded as first line of syzkaller reproducer files. syzReproPrefix = "# See https://goo.gl/kgGztJ for information about syzkaller reproducers.\n" @@ -44,10 +44,9 @@ func reportingPollBugs(c context.Context, typ string) []*dashapi.BugReport { log.Errorf(c, "%v", err) return nil } - var bugs []*Bug - _, err = db.NewQuery("Bug"). - Filter("Status<", BugStatusFixed). - GetAll(c, &bugs) + bugs, err := loadAllBugs(c, func(query *db.Query) *db.Query { + return query.Filter("Status<", BugStatusFixed) + }) if err != nil { log.Errorf(c, "%v", err) return nil @@ -152,10 +151,9 @@ func needReport(c context.Context, typ string, state *ReportingState, bug *Bug) } func reportingPollNotifications(c context.Context, typ string) []*dashapi.BugNotification { - var bugs []*Bug - _, err := db.NewQuery("Bug"). - Filter("Status<", BugStatusFixed). - GetAll(c, &bugs) + bugs, err := loadAllBugs(c, func(query *db.Query) *db.Query { + return query.Filter("Status<", BugStatusFixed) + }) if err != nil { log.Errorf(c, "%v", err) return nil @@ -209,7 +207,7 @@ func handleReportNotif(c context.Context, typ string, bug *Bug) (*dashapi.BugNot if len(bug.Commits) == 0 && bug.ReproLevel == ReproLevelNone && timeSince(c, bug.LastActivity) > notifyResendPeriod && - timeSince(c, bug.LastTime) > autoObsoletePeriod { + timeSince(c, bug.LastTime) > bug.obsoletePeriod() { log.Infof(c, "%v: obsoleting: %v", bug.Namespace, bug.Title) return createNotification(c, dashapi.BugNotifObsoleted, false, "", bug, reporting, bugReporting) } @@ -224,6 +222,39 @@ func handleReportNotif(c context.Context, typ string, bug *Bug) (*dashapi.BugNot return nil, nil } +func (bug *Bug) obsoletePeriod() time.Duration { + period := never + if config.Obsoleting.MinPeriod == 0 { + return period + } + // 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 + } + min, max := config.Obsoleting.MinPeriod, config.Obsoleting.MaxPeriod + if config.Obsoleting.NonFinalMinPeriod != 0 && + bug.Reporting[len(bug.Reporting)-1].Reported.IsZero() { + min, max = config.Obsoleting.NonFinalMinPeriod, config.Obsoleting.NonFinalMaxPeriod + } + if len(bug.HappenedOn) == 1 { + mgr := config.Namespaces[bug.Namespace].Managers[bug.HappenedOn[0]] + if mgr.ObsoletingMinPeriod != 0 { + min, max = mgr.ObsoletingMinPeriod, mgr.ObsoletingMaxPeriod + } + } + if period < min { + period = min + } + if period > max { + period = max + } + return period +} + func createNotification(c context.Context, typ dashapi.BugNotif, public bool, text string, bug *Bug, reporting *Reporting, bugReporting *BugReporting) (*dashapi.BugNotification, error) { reportingConfig, err := json.Marshal(reporting.Config) @@ -471,25 +502,51 @@ func managersToRepos(c context.Context, ns string, managers []string) []string { return repos } -func foreachBug(c context.Context, fn func(bug *Bug) error) error { +func loadAllBugs(c context.Context, filter func(*db.Query) *db.Query) ([]*Bug, error) { + var bugs []*Bug + err := foreachBug(c, filter, func(bug *Bug, _ *db.Key) error { + bugs = append(bugs, bug) + return nil + }) + if err != nil { + return nil, err + } + return bugs, nil +} + +func foreachBug(c context.Context, filter func(*db.Query) *db.Query, fn func(bug *Bug, key *db.Key) error) error { const batchSize = 1000 - for offset := 0; ; offset += batchSize { - var bugs []*Bug - _, err := db.NewQuery("Bug"). - Offset(offset). - Limit(batchSize). - GetAll(c, &bugs) - if err != nil { - return fmt.Errorf("foreachBug: failed to query bugs: %v", err) + var cursor *db.Cursor + for { + query := db.NewQuery("Bug").Limit(batchSize) + if filter != nil { + query = filter(query) } - for _, bug := range bugs { - if err := fn(bug); err != nil { + if cursor != nil { + query = query.Start(*cursor) + } + iter := query.Run(c) + for i := 0; ; i++ { + bug := new(Bug) + key, err := iter.Next(bug) + if err == db.Done { + if i < batchSize { + return nil + } + break + } + if err != nil { + return fmt.Errorf("failed to fetch bugs: %v", err) + } + if err := fn(bug, key); err != nil { return err } } - if len(bugs) < batchSize { - return nil + cur, err := iter.Cursor() + if err != nil { + return fmt.Errorf("cursor failed while fetching bugs: %v", err) } + cursor = &cur } } @@ -500,7 +557,7 @@ func reportingPollClosed(c context.Context, ids []string) ([]string, error) { idMap[id] = true } var closed []string - err := foreachBug(c, func(bug *Bug) error { + err := foreachBug(c, nil, func(bug *Bug, _ *db.Key) error { for i := range bug.Reporting { bugReporting := &bug.Reporting[i] if !idMap[bugReporting.ID] { @@ -900,6 +957,46 @@ func queryCrashesForBug(c context.Context, bugKey *db.Key, limit int) ( return crashes, keys, nil } +func queryJobsForBug(c context.Context, bugKey *db.Key, jobType JobType) ( + []*Job, []*db.Key, error) { + var jobs []*Job + keys, err := db.NewQuery("Job"). + Ancestor(bugKey). + Filter("Type=", jobType). + Filter("Finished>", time.Time{}). + Order("-Finished"). + GetAll(c, &jobs) + if err != nil { + return nil, nil, fmt.Errorf("failed to fetch fix bisections: %v", err) + } + return jobs, keys, nil +} + +func queryCrashForJob(c context.Context, job *Job, bugKey *db.Key) (*Crash, error) { + // If there was no crash corresponding to the Job, return. + if job.CrashTitle == "" { + return nil, nil + } + // First, fetch the crash who's repro was used to start the bisection + // job. + crash := new(Crash) + crashKey := db.NewKey(c, "Crash", "", job.CrashID, bugKey) + if err := db.Get(c, crashKey, crash); err != nil { + return nil, err + } + // Now, create a crash object with the crash details from the job. + ret := &Crash{ + Manager: crash.Manager, + Time: job.Finished, + Log: job.Log, + Report: job.CrashReport, + ReproOpts: crash.ReproOpts, + ReproSyz: crash.ReproSyz, + ReproC: crash.ReproC, + } + return ret, nil +} + func findCrashForBug(c context.Context, bug *Bug) (*Crash, *db.Key, error) { bugKey := bug.key(c) crashes, keys, err := queryCrashesForBug(c, bugKey, 1) |