aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-03-05 14:45:59 +0100
committerDmitry Vyukov <dvyukov@google.com>2017-03-05 14:45:59 +0100
commita091396fc1a15a1840e9cecbcc67e0e05dfaffda (patch)
tree6dbd14a27ec320c86aed9eacc770b1d63b418b0d
parent895e034a4df54478c0745940a25ecb7329f8bd81 (diff)
syz-dash: add search over reports
- add claimed status for bugs - add search over all reports - some other minor improvements
-rw-r--r--syz-dash/api.go5
-rw-r--r--syz-dash/app.yaml2
-rw-r--r--syz-dash/bug.html23
-rw-r--r--syz-dash/common.html40
-rw-r--r--syz-dash/dash.html4
-rw-r--r--syz-dash/handler.go124
-rw-r--r--syz-dash/index.yaml1
-rw-r--r--syz-dash/memcache.go4
-rw-r--r--syz-dash/patch.go19
-rw-r--r--syz-dash/search.html18
-rw-r--r--syz-dash/static/style.css20
11 files changed, 208 insertions, 52 deletions
diff --git a/syz-dash/api.go b/syz-dash/api.go
index 789ebf123..ffdd65615 100644
--- a/syz-dash/api.go
+++ b/syz-dash/api.go
@@ -68,6 +68,7 @@ const (
BugStatusReported
BugStatusFixed
BugStatusUnclear
+ BugStatusClaimed
BugStatusClosed = 1000 + iota
BugStatusDeleted
)
@@ -82,6 +83,8 @@ func statusToString(status int) string {
return "fixed"
case BugStatusUnclear:
return "unclear"
+ case BugStatusClaimed:
+ return "claimed"
case BugStatusClosed:
return "closed"
case BugStatusDeleted:
@@ -101,6 +104,8 @@ func stringToStatus(status string) (int, error) {
return BugStatusFixed, nil
case "unclear":
return BugStatusUnclear, nil
+ case "claimed":
+ return BugStatusClaimed, nil
case "closed":
return BugStatusClosed, nil
case "deleted":
diff --git a/syz-dash/app.yaml b/syz-dash/app.yaml
index a5d59f2fb..5933862c6 100644
--- a/syz-dash/app.yaml
+++ b/syz-dash/app.yaml
@@ -7,7 +7,7 @@ handlers:
- url: /static
static_dir: static
secure: always
-- url: /(|bug|text)
+- url: /(|bug|search|text)
script: _go_app
login: required
secure: always
diff --git a/syz-dash/bug.html b/syz-dash/bug.html
index a42d886b3..73a2d41b8 100644
--- a/syz-dash/bug.html
+++ b/syz-dash/bug.html
@@ -20,6 +20,7 @@
{{else}}
<td><select name="status">
<option value="new" {{if eq .Status "new"}}selected{{end}}>New</option>
+ <option value="claimed" {{if eq .Status "claimed"}}selected{{end}}>Claimed</option>
<option value="reported" {{if eq .Status "reported"}}selected{{end}}>Reported</option>
<option value="fixed" {{if eq .Status "fixed"}}selected{{end}}>Fixed</option>
<option value="unclear" {{if eq .Status "unclear"}}selected{{end}}>Unclear</option>
@@ -149,27 +150,7 @@ new crashes will produce a new bug"/>
<br>
{{end}}
- <table class="list_table">
- <caption>Crashes:</caption>
- <tr>
- <th>Title</th>
- <th>Manager</th>
- <th>Time</th>
- <th>Tag</th>
- <th>Log</th>
- <th>Report</th>
- </tr>
- {{range $c := $.Crashes}}
- <tr>
- <td class="title">{{$c.Title}}</td>
- <td class="manager">{{$c.Manager}}</td>
- <td class="time">{{formatTime $c.Time}}</td>
- <td class="tag">{{$c.Tag}}</td>
- <td class="log">{{if $c.Log}}<a href="/text?id={{$c.Log}}">log</a>{{end}}</td>
- <td class="report">{{if $c.Report}}<a href="/text?id={{$c.Report}}">report</a>{{end}}</td>
- </tr>
- {{end}}
- </table>
+ {{template "crash_list" $.Crashes}}
<br>
</body>
</html>
diff --git a/syz-dash/common.html b/syz-dash/common.html
index ab9e8c66d..97595a75a 100644
--- a/syz-dash/common.html
+++ b/syz-dash/common.html
@@ -1,8 +1,42 @@
{{define "header"}}
<header id="topbar">
- <h1><a href="/">syzkaller</a></h1>
- {{.Found}} bugs found, {{.Fixed}} bugs fixed, {{.Crashed}} kernels crashed
+ <table class="position_table">
+ <tr>
+ <td>
+ <h1><a href="/">syzkaller</a></h1>
+ {{.Found}} bugs found, {{.Fixed}} bugs fixed, {{.Crashed}} kernels crashed
+ </td>
+ <td class="search">
+ <form action="/search">
+ <input name="query" type="text" size="30" maxlength="1000" value="{{.Query}}" required/>
+ <input type="submit" value="Search reports" class="button"/>
+ </form>
+ </td>
+ </tr>
+ </table>
</header>
{{end}}
-
+{{define "crash_list"}}
+ <table class="list_table">
+ <caption>{{if .Title}}<a href="{{.Link}}">{{.Title}}</a>{{else}}Crashes:{{end}}</caption>
+ <tr>
+ <th>Title</th>
+ <th>Manager</th>
+ <th>Time</th>
+ <th>Tag</th>
+ <th>Log</th>
+ <th>Report</th>
+ </tr>
+ {{range $c := .List}}
+ <tr>
+ <td class="title">{{$c.Title}}</td>
+ <td class="managers" title="{{$c.Manager}}">{{$c.Manager}}</td>
+ <td class="time">{{formatTime $c.Time}}</td>
+ <td class="tag">{{$c.Tag}}</td>
+ <td class="log">{{if $c.Log}}<a href="/text?id={{$c.Log}}">log</a>{{end}}</td>
+ <td class="report">{{if $c.Report}}<a href="/text?id={{$c.Report}}">report</a>{{end}}</td>
+ </tr>
+ {{end}}
+ </table>
+{{end}}
diff --git a/syz-dash/dash.html b/syz-dash/dash.html
index 4b14a418c..9dbe24b82 100644
--- a/syz-dash/dash.html
+++ b/syz-dash/dash.html
@@ -1,6 +1,6 @@
{{define "bug_table"}}
<table class="list_table">
- <caption>{{.Name}}:</caption>
+ <caption>{{.Name}} ({{len $.Bugs}}):</caption>
<tr>
<th>Title</th>
<th>Count</th>
@@ -10,7 +10,7 @@
</tr>
{{range $b := $.Bugs}}
<tr>
- <td class="title"><a href="/bug?id={{$b.ID}}">{{$b.Title}}</a></td>
+ <td class="title"><a href="/bug?id={{$b.ID}}" title="{{$b.Comment}}">{{$b.Title}}</a></td>
<td class="count">{{$b.NumCrashes}}</td>
<td class="repro">{{$b.Repro}}</td>
<td class="time">{{formatTime $b.LastTime}}</td>
diff --git a/syz-dash/handler.go b/syz-dash/handler.go
index 7861fee0d..2c3754b32 100644
--- a/syz-dash/handler.go
+++ b/syz-dash/handler.go
@@ -6,6 +6,7 @@
package dash
import (
+ "bytes"
"fmt"
"html/template"
"net/http"
@@ -24,6 +25,7 @@ func init() {
http.Handle("/", handlerWrapper(handleAuth(handleDash)))
http.Handle("/bug", handlerWrapper(handleAuth(handleBug)))
http.Handle("/text", handlerWrapper(handleAuth(handleText)))
+ http.Handle("/search", handlerWrapper(handleAuth(handleSearch)))
http.Handle("/client", handlerWrapper(handleAuth(handleClient)))
}
@@ -88,11 +90,18 @@ func handleDash(c appengine.Context, w http.ResponseWriter, r *http.Request) err
data := &dataDash{}
bugGroups := map[int]*uiBugGroup{
BugStatusNew: &uiBugGroup{Name: "New bugs"},
+ BugStatusClaimed: &uiBugGroup{Name: "Claimed bugs"},
BugStatusReported: &uiBugGroup{Name: "Reported bugs"},
BugStatusUnclear: &uiBugGroup{Name: "Unclear bugs"},
BugStatusFixed: &uiBugGroup{Name: "Fixed bugs"},
}
- data.BugGroups = append(data.BugGroups, bugGroups[BugStatusNew], bugGroups[BugStatusReported], bugGroups[BugStatusUnclear], bugGroups[BugStatusFixed])
+ data.BugGroups = append(data.BugGroups,
+ bugGroups[BugStatusNew],
+ bugGroups[BugStatusClaimed],
+ bugGroups[BugStatusReported],
+ bugGroups[BugStatusUnclear],
+ bugGroups[BugStatusFixed],
+ )
all := r.FormValue("all") != ""
if all {
@@ -104,7 +113,7 @@ func handleDash(c appengine.Context, w http.ResponseWriter, r *http.Request) err
var bugs []*Bug
var keys []*ds.Key
var err error
- query := ds.NewQuery("Bug").Project("Title", "Status")
+ query := ds.NewQuery("Bug").Project("Title", "Status", "Comment")
if !all {
query = query.Filter("Status <", BugStatusClosed)
}
@@ -116,9 +125,10 @@ func handleDash(c appengine.Context, w http.ResponseWriter, r *http.Request) err
for i, bug := range bugs {
id := keys[i].IntID()
ui := &uiBug{
- ID: id,
- Title: bug.Title,
- Status: statusToString(bug.Status),
+ ID: id,
+ Title: bug.Title,
+ Status: statusToString(bug.Status),
+ Comment: bug.Comment,
}
bugMap[id] = ui
managers[id] = make(map[string]bool)
@@ -211,9 +221,9 @@ func handleBug(c appengine.Context, w http.ResponseWriter, r *http.Request) erro
if reportLink == "" {
return fmt.Errorf("enter report link")
}
- case BugStatusUnclear:
+ case BugStatusClaimed, BugStatusUnclear:
if comment == "" {
- return fmt.Errorf("enter comment as to why it's unclear")
+ return fmt.Errorf("enter comment as to why it's unclear/who claimed it")
}
}
@@ -228,7 +238,7 @@ func handleBug(c appengine.Context, w http.ResponseWriter, r *http.Request) erro
if status == BugStatusFixed && len(bug.Patches) == 0 {
return fmt.Errorf("add a patch for fixed bugs")
}
- flushCached = bug.Status != status
+ flushCached = bug.Status != status || bug.Title != title
bug.Title = title
bug.Status = status
bug.ReportLink = reportLink
@@ -314,7 +324,7 @@ func handleBug(c appengine.Context, w http.ResponseWriter, r *http.Request) erro
dstBug.Version++
dstBug.ReportLink = mergeStrings(dstBug.ReportLink, srcBug.ReportLink)
dstBug.Comment = mergeStrings(dstBug.Comment, srcBug.Comment)
- dstBug.CVE = mergeStrings(dstBug.ReportLink, srcBug.CVE)
+ dstBug.CVE = mergeStrings(dstBug.CVE, srcBug.CVE)
var groupKeys []*ds.Key
var groups []*Group
for _, hash := range srcBug.Groups {
@@ -517,7 +527,7 @@ func handleBug(c appengine.Context, w http.ResponseWriter, r *http.Request) erro
return fmt.Errorf("failed to fetch crashes: %v", err)
}
for _, crash := range crashes {
- data.Crashes = append(data.Crashes, &uiCrash{
+ data.Crashes.List = append(data.Crashes.List, &uiCrash{
Title: group.DisplayTitle(),
Manager: crash.Manager,
Tag: crash.Tag,
@@ -545,7 +555,7 @@ func handleBug(c appengine.Context, w http.ResponseWriter, r *http.Request) erro
}
}
- sort.Sort(uiCrashArray(data.Crashes))
+ sort.Sort(uiCrashArray(data.Crashes.List))
sort.Sort(uiReproArray(data.Repros))
if len(data.Groups) == 1 {
@@ -576,10 +586,75 @@ func handleText(c appengine.Context, w http.ResponseWriter, r *http.Request) err
return nil
}
+func handleSearch(c appengine.Context, w http.ResponseWriter, r *http.Request) error {
+ cached, err := getCached(c)
+ if err != nil {
+ return err
+ }
+ data := &dataSearch{
+ Header: headerFromCached(cached),
+ }
+ data.Header.Query = r.FormValue("query")
+ query := []byte(data.Header.Query)
+
+ bugTitles := make(map[int64]string)
+ for _, b := range cached.Bugs {
+ bugTitles[b.ID] = b.Title
+ }
+ resMap := make(map[int64]*uiCrashGroup)
+
+ var groups []*Group
+ if _, err := ds.NewQuery("Group").GetAll(c, &groups); err != nil {
+ return fmt.Errorf("failed to fetch crash groups: %v", err)
+ }
+ for _, group := range groups {
+ var crashes []*Crash
+ if _, err := ds.NewQuery("Crash").Ancestor(group.Key(c)).GetAll(c, &crashes); err != nil {
+ return fmt.Errorf("failed to fetch crashes: %v", err)
+ }
+ for _, crash := range crashes {
+ if crash.Report == 0 {
+ continue
+ }
+ report, err := getText(c, crash.Report)
+ if err != nil {
+ return err
+ }
+ if !bytes.Contains(report, query) {
+ continue
+ }
+ cg := resMap[group.Bug]
+ if cg == nil {
+ cg = &uiCrashGroup{
+ Title: bugTitles[group.Bug],
+ Link: fmt.Sprintf("/bug?id=%v", group.Bug),
+ }
+ resMap[group.Bug] = cg
+ }
+ cg.List = append(cg.List, &uiCrash{
+ Title: group.DisplayTitle(),
+ Manager: crash.Manager,
+ Tag: crash.Tag,
+ Time: crash.Time,
+ Log: crash.Log,
+ Report: crash.Report,
+ })
+ }
+ }
+ for _, res := range resMap {
+ sort.Sort(uiCrashArray(res.List))
+ data.Results = append(data.Results, res)
+ }
+ sort.Sort(uiCrashGroupArray(data.Results))
+
+ return templates.ExecuteTemplate(w, "search.html", data)
+}
+
type dataHeader struct {
Found int64
Fixed int64
Crashed int64
+ Query string
}
func headerFromCached(cached *Cached) *dataHeader {
@@ -590,6 +665,11 @@ func headerFromCached(cached *Cached) *dataHeader {
}
}
+type dataSearch struct {
+ Header *dataHeader
+ Results []*uiCrashGroup
+}
+
type dataDash struct {
Header *dataHeader
BugGroups []*uiBugGroup
@@ -598,12 +678,18 @@ type dataDash struct {
type dataBug struct {
Header *dataHeader
uiBug
- Crashes []*uiCrash
+ Crashes uiCrashGroup
Repros []*uiRepro
Message string
AllBugs []*uiBug
}
+type uiCrashGroup struct {
+ Title string
+ Link string
+ List []*uiCrash
+}
+
type uiBugGroup struct {
Name string
Bugs []*uiBug
@@ -694,6 +780,20 @@ func (a uiCrashArray) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
+type uiCrashGroupArray []*uiCrashGroup
+
+func (a uiCrashGroupArray) Len() int {
+ return len(a)
+}
+
+func (a uiCrashGroupArray) Less(i, j int) bool {
+ return a[i].Title < a[j].Title
+}
+
+func (a uiCrashGroupArray) Swap(i, j int) {
+ a[i], a[j] = a[j], a[i]
+}
+
type uiReproArray []*uiRepro
func (a uiReproArray) Len() int {
diff --git a/syz-dash/index.yaml b/syz-dash/index.yaml
index d330080f1..8c3e0f9de 100644
--- a/syz-dash/index.yaml
+++ b/syz-dash/index.yaml
@@ -14,6 +14,7 @@ indexes:
properties:
- name: Status
- name: Title
+ - name: Comment
- kind: Crash
ancestor: yes
diff --git a/syz-dash/memcache.go b/syz-dash/memcache.go
index 9653eef90..d663e6598 100644
--- a/syz-dash/memcache.go
+++ b/syz-dash/memcache.go
@@ -75,7 +75,7 @@ func buildCached(c appengine.Context) (*Cached, error) {
})
}
switch bug.Status {
- case BugStatusNew, BugStatusReported, BugStatusUnclear:
+ case BugStatusNew, BugStatusReported, BugStatusUnclear, BugStatusClaimed:
cached.Found++
case BugStatusFixed, BugStatusClosed:
cached.Found++
@@ -95,7 +95,7 @@ func buildCached(c appengine.Context) (*Cached, error) {
return nil, fmt.Errorf("failed to find bug for crash %v (%v)", group.Title, group.Seq)
}
switch status {
- case BugStatusNew, BugStatusReported, BugStatusFixed, BugStatusUnclear, BugStatusClosed:
+ case BugStatusNew, BugStatusReported, BugStatusFixed, BugStatusUnclear, BugStatusClaimed, BugStatusClosed:
cached.Crashed += group.NumCrashes
case BugStatusDeleted:
default:
diff --git a/syz-dash/patch.go b/syz-dash/patch.go
index b7f76c902..fe713b063 100644
--- a/syz-dash/patch.go
+++ b/syz-dash/patch.go
@@ -34,16 +34,7 @@ func parsePatch(text string) (title string, diff string, err error) {
continue
}
if strings.HasPrefix(ln, "Subject: ") {
- ln = ln[len("Subject: "):]
- if strings.Contains(strings.ToLower(ln), "[patch") {
- pos := strings.IndexByte(ln, ']')
- if pos == -1 {
- err = fmt.Errorf("subject line does not contain ']'")
- return
- }
- ln = ln[pos+1:]
- }
- title = ln
+ title = ln[len("Subject: "):]
continue
}
if ln == "" || title != "" || diffStarted {
@@ -57,6 +48,14 @@ func parsePatch(text string) (title string, diff string, err error) {
if err = s.Err(); err != nil {
return
}
+ if strings.Contains(strings.ToLower(title), "[patch") {
+ pos := strings.IndexByte(title, ']')
+ if pos == -1 {
+ err = fmt.Errorf("title contains '[patch' but not ']'")
+ return
+ }
+ title = title[pos+1:]
+ }
title = strings.TrimSpace(title)
if title == "" {
err = fmt.Errorf("failed to extract title")
diff --git a/syz-dash/search.html b/syz-dash/search.html
new file mode 100644
index 000000000..abbafa299
--- /dev/null
+++ b/syz-dash/search.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html>
+<head>
+ <title>Syzkaller Dashboard</title>
+ <link rel="stylesheet" href="/static/style.css"/>
+</head>
+<body>
+ {{template "header" .Header}}
+
+ {{if .Results}}
+ {{range $r := .Results}}
+ {{template "crash_list" $r}}
+ {{end}}
+ {{else}}
+ <b>No matches found</b>
+ {{end}}
+</body>
+</html>
diff --git a/syz-dash/static/style.css b/syz-dash/static/style.css
index e2558bacf..46cfcd980 100644
--- a/syz-dash/static/style.css
+++ b/syz-dash/static/style.css
@@ -35,6 +35,22 @@ table td, table th {
overflow: hidden;
}
+.position_table {
+ border: 0px;
+ margin: 0px;
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.position_table td, .position_table tr {
+ vertical-align: center;
+ padding: 0px;
+}
+
+.position_table .search {
+ text-align: right;
+}
+
.list_table td, .list_table th {
border-left: 1px solid #ccc;
}
@@ -62,7 +78,9 @@ table td, table th {
.list_table .tag {
font-family: monospace;
- font-size: 9pt;
+ font-size: 8pt;
+ width: 200pt;
+ max-width: 200pt;
}
.list_table .opts {