aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2023-05-10 19:38:27 +0200
committerAleksandr Nogikh <wp32pw@gmail.com>2023-05-12 13:24:58 +0200
commitecca8a243762a781257ba0b65291bca940e13e9c (patch)
tree6a3ae8f113f81af4f43b9f9eae0ff21256496c97
parent76821f5baf47294314823eb4df0e05ceb8242d74 (diff)
dashboard: display bug origin tree testing results
Display them as a collapsible block on the bug info page. Use colors to distinguish results.
-rw-r--r--dashboard/app/bug.html3
-rw-r--r--dashboard/app/jobs.go9
-rw-r--r--dashboard/app/main.go14
-rw-r--r--dashboard/app/templates.html41
-rw-r--r--dashboard/app/tree.go70
-rw-r--r--dashboard/app/tree_test.go7
-rw-r--r--dashboard/dashapi/dashapi.go1
-rw-r--r--pkg/html/pages/style.css12
8 files changed, 148 insertions, 9 deletions
diff --git a/dashboard/app/bug.html b/dashboard/app/bug.html
index 3078eee42..4d499bebf 100644
--- a/dashboard/app/bug.html
+++ b/dashboard/app/bug.html
@@ -53,7 +53,8 @@ Page with details about a single bug.
{{if eq $item.Type "bug_list"}}{{template "bug_list" $item.Value}}{{end}}
{{if eq $item.Type "job_list"}}{{template "job_list" $item.Value}}{{end}}
{{if eq $item.Type "discussion_list"}}{{template "discussion_list" $item.Value}}{{end}}
- </div>
+ {{if eq $item.Type "test_results"}}{{template "test_results" $item.Value}}{{end}}
+ </div>
</div>
{{end}}
diff --git a/dashboard/app/jobs.go b/dashboard/app/jobs.go
index f65f224b7..57816930b 100644
--- a/dashboard/app/jobs.go
+++ b/dashboard/app/jobs.go
@@ -1380,16 +1380,16 @@ func jobID2Key(c context.Context, id string) (*db.Key, error) {
return jobKey, nil
}
-func fetchJob(c context.Context, key string) (*Job, error) {
+func fetchJob(c context.Context, key string) (*Job, *db.Key, error) {
jobKey, err := db.DecodeKey(key)
if err != nil {
- return nil, err
+ return nil, nil, err
}
job := new(Job)
if err := db.Get(c, jobKey, job); err != nil {
- return nil, fmt.Errorf("failed to get job: %v", err)
+ return nil, nil, fmt.Errorf("failed to get job: %v", err)
}
- return job, nil
+ return job, jobKey, nil
}
func makeJobInfo(c context.Context, job *Job, jobKey *db.Key, bug *Bug, build *Build,
@@ -1423,6 +1423,7 @@ func makeJobInfo(c context.Context, job *Job, jobKey *db.Key, bug *Bug, build *B
ErrorLink: textLink(textError, job.Error),
Reported: job.Reported,
TreeOrigin: job.TreeOrigin,
+ OnMergeBase: job.MergeBaseRepo != "",
}
if !job.Finished.IsZero() {
info.Duration = job.Finished.Sub(job.LastStarted)
diff --git a/dashboard/app/main.go b/dashboard/app/main.go
index df7fb3023..35864b4df 100644
--- a/dashboard/app/main.go
+++ b/dashboard/app/main.go
@@ -254,6 +254,7 @@ const (
sectionBugList = "bug_list"
sectionJobList = "job_list"
sectionDiscussionList = "discussion_list"
+ sectionTestResults = "test_results"
)
type uiCollapsible struct {
@@ -746,6 +747,18 @@ func handleBug(c context.Context, w http.ResponseWriter, r *http.Request) error
Value: discussions,
})
}
+ treeTestJobs, err := treeTestJobs(c, bug)
+ if err != nil {
+ return err
+ }
+ if len(treeTestJobs) > 0 {
+ sections = append(sections, &uiCollapsible{
+ Title: fmt.Sprintf("Bug presence on other trees (%d)", len(treeTestJobs)),
+ Show: true,
+ Type: sectionTestResults,
+ Value: treeTestJobs,
+ })
+ }
similar, err := loadSimilarBugsUI(c, r, bug, state)
if err != nil {
return err
@@ -758,7 +771,6 @@ func handleBug(c context.Context, w http.ResponseWriter, r *http.Request) error
Value: similar,
})
}
-
var bisectCause *uiJob
if bug.BisectCause > BisectPending {
bisectCause, err = getUIJob(c, bug, JobBisectCause)
diff --git a/dashboard/app/templates.html b/dashboard/app/templates.html
index 001eec25e..2c84878d7 100644
--- a/dashboard/app/templates.html
+++ b/dashboard/app/templates.html
@@ -544,3 +544,44 @@ Use of this source code is governed by Apache 2 LICENSE that can be found in the
</table>
{{end}}
{{end}}
+
+{{/* List of test results, invoked with []*dashapi.JobInfo */}}
+{{define "test_results"}}
+{{if .}}
+<table class="list_table">
+ <thead>
+ <tr>
+ <th>Date</th>
+ <th>Name</th>
+ <th>Commit</th>
+ <th>Repro</th>
+ <th>Result</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{range $item := .}}
+ <tr>
+ <td>{{formatDate $item.Finished}}</td>
+ <td>{{$item.KernelAlias}} {{if $item.OnMergeBase}}(merge base){{else}}(ToT){{end}}</td>
+ <td class="stat">{{link $item.KernelCommitLink (formatTagHash $item.KernelCommit)}}</td>
+ <td>
+ {{if $item.ReproCLink}}<a href="{{$item.ReproCLink}}">C</a>
+ {{else if $item.ReproSyzLink}}<a href="{{$item.ReproSyzLink}}">C</a>{{end}}
+ </td>
+ {{if ne $item.CrashTitle ""}}
+ <td class="status-crashed">
+ {{link $item.CrashReportLink "[report]"}} <i>{{$item.CrashTitle}}</i>
+ </td>
+ {{else if ne $item.ErrorLink ""}}
+ <td class="status-error">
+ Failed due to {{link $item.ErrorLink "an error"}}; will retry later
+ </td>
+ {{else}}
+ <td class="status-ok">Didn't crash</td>
+ {{end}}
+ </tr>
+ {{end}}
+ </tbody>
+</table>
+{{end}}
+{{end}}
diff --git a/dashboard/app/tree.go b/dashboard/app/tree.go
index 50b35186a..0ac434c9a 100644
--- a/dashboard/app/tree.go
+++ b/dashboard/app/tree.go
@@ -10,10 +10,13 @@ package main
import (
"fmt"
+ "sort"
+ "sync"
"time"
"github.com/google/syzkaller/dashboard/dashapi"
"golang.org/x/net/context"
+ "golang.org/x/sync/errgroup"
db "google.golang.org/appengine/v2/datastore"
"google.golang.org/appengine/v2/log"
)
@@ -447,7 +450,7 @@ func (ctx *bugTreeContext) doRunRepro(repo KernelRepo, result expectedResult, ru
}
func (ctx *bugTreeContext) ensureRepeatPeriod(jobKey string, period time.Duration) pollTreeJobResult {
- job, err := fetchJob(ctx.c, jobKey)
+ job, _, err := fetchJob(ctx.c, jobKey)
if err != nil {
return pollResultError(err)
}
@@ -479,7 +482,7 @@ func (ctx *bugTreeContext) findResult(repo KernelRepo, result expectedResult, ru
if key == "" {
continue
}
- job, err := fetchJob(ctx.c, key)
+ job, _, err := fetchJob(ctx.c, key)
if err != nil {
return pollResultError(err)
}
@@ -605,7 +608,7 @@ func (test *BugTreeTest) applyPending(c context.Context) error {
if test.Pending == "" {
return nil
}
- job, err := fetchJob(c, test.Pending)
+ job, _, err := fetchJob(c, test.Pending)
if err != nil {
return err
}
@@ -631,6 +634,67 @@ func (test *BugTreeTest) applyPending(c context.Context) error {
return nil
}
+// treeTestJobs fetches relevant tree testing results.
+func treeTestJobs(c context.Context, bug *Bug) ([]*dashapi.JobInfo, error) {
+ g, _ := errgroup.WithContext(context.Background())
+ jobIDs := make(chan string)
+
+ var ret []*dashapi.JobInfo
+ var mu sync.Mutex
+
+ // The underlying code makes a number of queries, so let's do it in parallel to speed up processing.
+ const threads = 3
+ for i := 0; i < threads; i++ {
+ g.Go(func() error {
+ for id := range jobIDs {
+ job, jobKey, err := fetchJob(c, id)
+ if err != nil {
+ return err
+ }
+ build, err := loadBuild(c, job.Namespace, job.BuildID)
+ if err != nil {
+ return err
+ }
+ crashKey := db.NewKey(c, "Crash", "", job.CrashID, bug.key(c))
+ crash := new(Crash)
+ if err := db.Get(c, crashKey, crash); err != nil {
+ return fmt.Errorf("failed to get crash: %v", err)
+ }
+ info := makeJobInfo(c, job, jobKey, bug, build, crash)
+ mu.Lock()
+ ret = append(ret, info)
+ mu.Unlock()
+ }
+ return nil
+ })
+ }
+ for _, info := range bug.TreeTests.List {
+ if info.FirstOK != "" {
+ jobIDs <- info.FirstOK
+ }
+ if info.FirstCrash != "" {
+ jobIDs <- info.FirstCrash
+ }
+ if info.Error != "" {
+ jobIDs <- info.Error
+ }
+ }
+ // Wait until we have all information.
+ close(jobIDs)
+ err := g.Wait()
+ if err != nil {
+ return nil, err
+ }
+ // Sort structures to keep output consistent.
+ sort.Slice(ret, func(i, j int) bool {
+ if ret[i].KernelAlias != ret[j].KernelAlias {
+ return ret[i].KernelAlias < ret[j].KernelAlias
+ }
+ return ret[i].Finished.Before(ret[j].Finished)
+ })
+ return ret, nil
+}
+
type repoNode struct {
repo KernelRepo
edges []repoEdge
diff --git a/dashboard/app/tree_test.go b/dashboard/app/tree_test.go
index 9ebcf64dd..1464ea5f4 100644
--- a/dashboard/app/tree_test.go
+++ b/dashboard/app/tree_test.go
@@ -43,6 +43,9 @@ func TestTreeOriginDownstream(t *testing.T) {
c.expectEQ(ctx.entries[0].jobsDone, 1)
c.expectEQ(ctx.entries[1].jobsDone, 1)
c.expectEQ(ctx.entries[2].jobsDone, 1)
+ // Test that we can render the bug page.
+ _, err := c.GET(ctx.bugLink())
+ c.expectEQ(err, nil)
}
func TestTreeOriginLts(t *testing.T) {
@@ -730,6 +733,10 @@ func (ctx *treeTestCtx) ensureLabels(labels ...string) {
ctx.ctx.expectEQ(labels, bugLabels)
}
+func (ctx *treeTestCtx) bugLink() string {
+ return fmt.Sprintf("/bug?id=%v", ctx.bug.key(ctx.ctx.ctx).StringID())
+}
+
type treeTestEntry struct {
alias string
mergeAlias string
diff --git a/dashboard/dashapi/dashapi.go b/dashboard/dashapi/dashapi.go
index 91b6a4a0d..9f90467ef 100644
--- a/dashboard/dashapi/dashapi.go
+++ b/dashboard/dashapi/dashapi.go
@@ -865,6 +865,7 @@ type JobInfo struct {
Commits []*Commit // for inconclusive bisection
Reported bool
TreeOrigin bool
+ OnMergeBase bool
}
func (dash *Dashboard) Query(method string, req, reply interface{}) error {
diff --git a/pkg/html/pages/style.css b/pkg/html/pages/style.css
index 3bff3a9ed..8ced887ba 100644
--- a/pkg/html/pages/style.css
+++ b/pkg/html/pages/style.css
@@ -207,6 +207,18 @@ table td, table th {
font-size: 75%;
}
+.list_table .status-crashed {
+ background-color: #FF8674;
+}
+
+.list_table .status-ok {
+ background-color: lightgreen;
+}
+
+.list_table .status-error {
+ background-color: lightgray;
+}
+
.bug-label {
background: white;
border: 1pt solid black;