mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-23 19:39:40 +00:00
dashboard/app: show info about duplicates and similar bugs
Show info about duplicates and similar bugs in other kernels on the bug page.
This commit is contained in:
parent
48359b9777
commit
96ca35f4c7
@ -1,3 +1,10 @@
|
||||
{{/*
|
||||
Copyright 2017 syzkaller project authors. All rights reserved.
|
||||
Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
|
||||
|
||||
Page with details about a single bug.
|
||||
*/}}
|
||||
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
@ -7,18 +14,21 @@
|
||||
<body>
|
||||
{{template "header" .Header}}
|
||||
|
||||
Title: {{.Bug.Title}}<br>
|
||||
Namespace: {{.Bug.Namespace}}<br>
|
||||
Crashes: {{.Bug.NumCrashes}}<br>
|
||||
First: {{formatTime .Bug.FirstTime}}<br>
|
||||
Last: {{formatTime .Bug.LastTime}}<br>
|
||||
Reporting: {{if .Bug.Link}}<a href="{{.Bug.Link}}">{{.Bug.Status}}</a>{{else}}{{.Bug.Status}}{{end}}<br>
|
||||
Commits: {{.Bug.Commits}}<br>
|
||||
Patched on: {{.Bug.PatchedOn}}<br>
|
||||
Missing on: {{.Bug.MissingOn}}<br>
|
||||
<b>[{{.Bug.Namespace}}] {{.Bug.Title}}</b><br>
|
||||
Status: {{if .Bug.ExternalLink}}<a href="{{.Bug.ExternalLink}}">{{.Bug.Status}}</a>{{else}}{{.Bug.Status}}{{end}}<br>
|
||||
{{if .Bug.Commits}}
|
||||
Commits: {{.Bug.Commits}}<br>
|
||||
Patched on: {{.Bug.PatchedOn}}, missing on: {{.Bug.MissingOn}}<br>
|
||||
{{end}}
|
||||
First: {{formatLateness $.Now $.Bug.FirstTime}}, last: {{formatLateness $.Now $.Bug.LastTime}}<br>
|
||||
<br>
|
||||
|
||||
{{template "bug_list" .DupOf}}
|
||||
{{template "bug_list" .Dups}}
|
||||
{{template "bug_list" .Similar}}
|
||||
|
||||
<table class="list_table">
|
||||
<caption>Crashes:</caption>
|
||||
<caption>Crashes ({{.Bug.NumCrashes}}):</caption>
|
||||
<tr>
|
||||
<th>Manager</th>
|
||||
<th>Time</th>
|
||||
|
@ -61,13 +61,19 @@ type uiBuild struct {
|
||||
|
||||
type uiBugPage struct {
|
||||
Header *uiHeader
|
||||
Now time.Time
|
||||
Bug *uiBug
|
||||
DupOf *uiBugGroup
|
||||
Dups *uiBugGroup
|
||||
Similar *uiBugGroup
|
||||
Crashes []*uiCrash
|
||||
}
|
||||
|
||||
type uiBugGroup struct {
|
||||
Namespace string
|
||||
Bugs []*uiBug
|
||||
Now time.Time
|
||||
Caption string
|
||||
ShowNamespace bool
|
||||
Bugs []*uiBug
|
||||
}
|
||||
|
||||
type uiBug struct {
|
||||
@ -184,14 +190,38 @@ func handleBug(c context.Context, w http.ResponseWriter, r *http.Request) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var dupOf *uiBugGroup
|
||||
if bug.DupOf != "" {
|
||||
dup := new(Bug)
|
||||
if err := datastore.Get(c, datastore.NewKey(c, "Bug", bug.DupOf, 0, nil), dup); err != nil {
|
||||
return err
|
||||
}
|
||||
dupOf = &uiBugGroup{
|
||||
Now: timeNow(c),
|
||||
Caption: "Duplicate of",
|
||||
Bugs: []*uiBug{createUIBug(c, dup, state, managers)},
|
||||
}
|
||||
}
|
||||
uiBug := createUIBug(c, bug, state, managers)
|
||||
crashes, err := loadCrashesForBug(c, bug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dups, err := loadDupsForBug(c, bug, state, managers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
similar, err := loadSimilarBugs(c, bug, state)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data := &uiBugPage{
|
||||
Header: h,
|
||||
Now: timeNow(c),
|
||||
Bug: uiBug,
|
||||
DupOf: dupOf,
|
||||
Dups: dups,
|
||||
Similar: similar,
|
||||
Crashes: crashes,
|
||||
}
|
||||
return serveTemplate(w, "bug.html", data)
|
||||
@ -237,25 +267,111 @@ func fetchBugs(c context.Context) ([]*uiBugGroup, error) {
|
||||
uiBug := createUIBug(c, bug, state, managers[bug.Namespace])
|
||||
groups[bug.Namespace] = append(groups[bug.Namespace], uiBug)
|
||||
}
|
||||
now := timeNow(c)
|
||||
var res []*uiBugGroup
|
||||
for ns, bugs := range groups {
|
||||
sort.Sort(uiBugSorter(bugs))
|
||||
res = append(res, &uiBugGroup{
|
||||
Namespace: ns,
|
||||
Bugs: bugs,
|
||||
Now: now,
|
||||
Caption: fmt.Sprintf("%v (%v)", ns, len(bugs)),
|
||||
Bugs: bugs,
|
||||
})
|
||||
}
|
||||
sort.Sort(uiBugGroupSorter(res))
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func createUIBug(c context.Context, bug *Bug, state *ReportingState, managers []string) *uiBug {
|
||||
_, _, _, reportingIdx, status, link, err := needReport(c, "", state, bug)
|
||||
func loadDupsForBug(c context.Context, bug *Bug, state *ReportingState, managers []string) (
|
||||
*uiBugGroup, error) {
|
||||
bugHash := bugKeyHash(bug.Namespace, bug.Title, bug.Seq)
|
||||
var dups []*Bug
|
||||
_, err := datastore.NewQuery("Bug").
|
||||
Filter("Status=", BugStatusDup).
|
||||
Filter("DupOf=", bugHash).
|
||||
GetAll(c, &dups)
|
||||
if err != nil {
|
||||
status = err.Error()
|
||||
return nil, err
|
||||
}
|
||||
if status == "" {
|
||||
status = "???"
|
||||
var results []*uiBug
|
||||
for _, dup := range dups {
|
||||
results = append(results, createUIBug(c, dup, state, managers))
|
||||
}
|
||||
group := &uiBugGroup{
|
||||
Now: timeNow(c),
|
||||
Caption: "Duplicates",
|
||||
Bugs: results,
|
||||
}
|
||||
return group, nil
|
||||
}
|
||||
|
||||
func loadSimilarBugs(c context.Context, bug *Bug, state *ReportingState) (*uiBugGroup, error) {
|
||||
var similar []*Bug
|
||||
_, err := datastore.NewQuery("Bug").
|
||||
Filter("Title=", bug.Title).
|
||||
GetAll(c, &similar)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
managers := make(map[string][]string)
|
||||
var results []*uiBug
|
||||
for _, similar := range similar {
|
||||
if similar.Namespace == bug.Namespace && similar.Seq == bug.Seq {
|
||||
continue
|
||||
}
|
||||
if managers[similar.Namespace] == nil {
|
||||
mgrs, err := managerList(c, similar.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
managers[similar.Namespace] = mgrs
|
||||
}
|
||||
results = append(results, createUIBug(c, similar, state, managers[similar.Namespace]))
|
||||
}
|
||||
group := &uiBugGroup{
|
||||
Now: timeNow(c),
|
||||
Caption: "Similar Bugs",
|
||||
ShowNamespace: true,
|
||||
Bugs: results,
|
||||
}
|
||||
return group, nil
|
||||
}
|
||||
|
||||
func createUIBug(c context.Context, bug *Bug, state *ReportingState, managers []string) *uiBug {
|
||||
reportingIdx, status, link := 0, "", ""
|
||||
var err error
|
||||
if bug.Status == BugStatusOpen {
|
||||
_, _, _, reportingIdx, status, link, err = needReport(c, "", state, bug)
|
||||
if err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
if status == "" {
|
||||
status = "???"
|
||||
}
|
||||
} else {
|
||||
for i := range bug.Reporting {
|
||||
bugReporting := &bug.Reporting[i]
|
||||
if i == len(bug.Reporting)-1 ||
|
||||
bug.Status == BugStatusInvalid && !bug.Reporting[i].Closed.IsZero() &&
|
||||
bug.Reporting[i+1].Closed.IsZero() ||
|
||||
(bug.Status == BugStatusFixed || bug.Status == BugStatusDup) &&
|
||||
bug.Reporting[i].Closed.IsZero() {
|
||||
reportingIdx = i
|
||||
link = bugReporting.Link
|
||||
switch bug.Status {
|
||||
case BugStatusInvalid:
|
||||
status = "invalid"
|
||||
case BugStatusFixed:
|
||||
status = "fixed"
|
||||
case BugStatusDup:
|
||||
status = "dup"
|
||||
default:
|
||||
status = fmt.Sprintf("unknown (%v)", bug.Status)
|
||||
}
|
||||
status = fmt.Sprintf("%v: closed as %v on %v",
|
||||
bugReporting.Name, status, formatTime(bug.Closed))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
id := bugKeyHash(bug.Namespace, bug.Title, bug.Seq)
|
||||
uiBug := &uiBug{
|
||||
@ -499,6 +615,9 @@ type uiBugSorter []*uiBug
|
||||
func (a uiBugSorter) Len() int { return len(a) }
|
||||
func (a uiBugSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a uiBugSorter) Less(i, j int) bool {
|
||||
if a[i].Namespace != a[j].Namespace {
|
||||
return a[i].Namespace < a[j].Namespace
|
||||
}
|
||||
if a[i].ReportingIndex != a[j].ReportingIndex {
|
||||
return a[i].ReportingIndex > a[j].ReportingIndex
|
||||
}
|
||||
@ -515,4 +634,4 @@ type uiBugGroupSorter []*uiBugGroup
|
||||
|
||||
func (a uiBugGroupSorter) Len() int { return len(a) }
|
||||
func (a uiBugGroupSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a uiBugGroupSorter) Less(i, j int) bool { return a[i].Namespace < a[j].Namespace }
|
||||
func (a uiBugGroupSorter) Less(i, j int) bool { return a[i].Caption < a[j].Caption }
|
||||
|
@ -1,3 +1,10 @@
|
||||
{{/*
|
||||
Copyright 2017 syzkaller project authors. All rights reserved.
|
||||
Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
|
||||
|
||||
Main page.
|
||||
*/}}
|
||||
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
@ -101,28 +108,7 @@
|
||||
<br><br>
|
||||
|
||||
{{range $group := $.BugGroups}}
|
||||
<table class="list_table">
|
||||
<caption>{{.Namespace}} ({{len $group.Bugs}}):</caption>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Count</th>
|
||||
<th>Repro</th>
|
||||
<th>Last</th>
|
||||
<th>Status</th>
|
||||
<th>Patched</th>
|
||||
</tr>
|
||||
{{range $b := $group.Bugs}}
|
||||
<tr>
|
||||
<td class="title"><a href="{{$b.Link}}">{{$b.Title}}</a></td>
|
||||
<td class="stat {{if $b.NumCrashesBad}}bad{{end}}">{{$b.NumCrashes}}</td>
|
||||
<td class="stat">{{formatReproLevel $b.ReproLevel}}</td>
|
||||
<td class="stat">{{formatLateness $.Now $b.LastTime}}</td>
|
||||
<td class="status">{{if $b.Link}}<a href="{{$b.ExternalLink}}">{{$b.Status}}</a>{{else}}{{$b.Status}}{{end}}</td>
|
||||
<td class="patched" title="{{$b.Commits}}">{{if $b.Commits}}{{len $b.PatchedOn}}/{{len $b.MissingOn}}{{end}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
<br><br>
|
||||
{{template "bug_list" $group}}
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
||||
|
36
dashboard/app/templates.html
Normal file
36
dashboard/app/templates.html
Normal file
@ -0,0 +1,36 @@
|
||||
{{/*
|
||||
Copyright 2017 syzkaller project authors. All rights reserved.
|
||||
Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
|
||||
*/}}
|
||||
|
||||
{{/* List of bugs, invoked with *uiBugGroup */}}
|
||||
{{define "bug_list"}}
|
||||
{{if .}}
|
||||
{{if .Bugs}}
|
||||
<table class="list_table">
|
||||
<caption>{{$.Caption}}:</caption>
|
||||
<tr>
|
||||
{{if $.ShowNamespace}}<th>Kernel</th>{{end}}
|
||||
<th>Title</th>
|
||||
<th>Count</th>
|
||||
<th>Repro</th>
|
||||
<th>Last</th>
|
||||
<th>Patched</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
{{range $b := .Bugs}}
|
||||
<tr>
|
||||
{{if $.ShowNamespace}}<td>{{$b.Namespace}}</td>{{end}}
|
||||
<td class="title"><a href="{{$b.Link}}">{{$b.Title}}</a></td>
|
||||
<td class="stat {{if $b.NumCrashesBad}}bad{{end}}">{{$b.NumCrashes}}</td>
|
||||
<td class="stat">{{formatReproLevel $b.ReproLevel}}</td>
|
||||
<td class="stat">{{formatLateness $.Now $b.LastTime}}</td>
|
||||
<td class="patched" title="{{$b.Commits}}">{{if $b.Commits}}{{len $b.PatchedOn}}/{{len $b.MissingOn}}{{end}}</td>
|
||||
<td class="status">{{if $b.Link}}<a href="{{$b.ExternalLink}}">{{$b.Status}}</a>{{else}}{{$b.Status}}{{end}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
<br>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
@ -28,6 +28,7 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/appengine"
|
||||
"google.golang.org/appengine/aetest"
|
||||
"google.golang.org/appengine/datastore"
|
||||
aemail "google.golang.org/appengine/mail"
|
||||
"google.golang.org/appengine/user"
|
||||
)
|
||||
@ -93,8 +94,16 @@ func caller(skip int) string {
|
||||
|
||||
func (c *Ctx) Close() {
|
||||
if !c.t.Failed() {
|
||||
// Ensure that we can render bugs in the final test state.
|
||||
// Ensure that we can render main page and all bugs in the final test state.
|
||||
c.expectOK(c.GET("/"))
|
||||
var bugs []*Bug
|
||||
keys, err := datastore.NewQuery("Bug").GetAll(c.ctx, &bugs)
|
||||
if err != nil {
|
||||
c.t.Errorf("ERROR: failed to query bugs: %v", err)
|
||||
}
|
||||
for _, key := range keys {
|
||||
c.expectOK(c.GET(fmt.Sprintf("/bug?id=%v", key.StringID())))
|
||||
}
|
||||
c.expectOK(c.GET("/email_poll"))
|
||||
for len(c.emailSink) != 0 {
|
||||
c.t.Errorf("ERROR: leftover email: %v", (<-c.emailSink).Body)
|
||||
|
Loading…
Reference in New Issue
Block a user