diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-03-05 14:45:59 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-03-05 14:45:59 +0100 |
| commit | a091396fc1a15a1840e9cecbcc67e0e05dfaffda (patch) | |
| tree | 6dbd14a27ec320c86aed9eacc770b1d63b418b0d | |
| parent | 895e034a4df54478c0745940a25ecb7329f8bd81 (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.go | 5 | ||||
| -rw-r--r-- | syz-dash/app.yaml | 2 | ||||
| -rw-r--r-- | syz-dash/bug.html | 23 | ||||
| -rw-r--r-- | syz-dash/common.html | 40 | ||||
| -rw-r--r-- | syz-dash/dash.html | 4 | ||||
| -rw-r--r-- | syz-dash/handler.go | 124 | ||||
| -rw-r--r-- | syz-dash/index.yaml | 1 | ||||
| -rw-r--r-- | syz-dash/memcache.go | 4 | ||||
| -rw-r--r-- | syz-dash/patch.go | 19 | ||||
| -rw-r--r-- | syz-dash/search.html | 18 | ||||
| -rw-r--r-- | syz-dash/static/style.css | 20 |
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 { |
