From a091396fc1a15a1840e9cecbcc67e0e05dfaffda Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sun, 5 Mar 2017 14:45:59 +0100 Subject: syz-dash: add search over reports - add claimed status for bugs - add search over all reports - some other minor improvements --- syz-dash/api.go | 5 ++ syz-dash/app.yaml | 2 +- syz-dash/bug.html | 23 +-------- syz-dash/common.html | 40 +++++++++++++-- syz-dash/dash.html | 4 +- syz-dash/handler.go | 124 +++++++++++++++++++++++++++++++++++++++++----- syz-dash/index.yaml | 1 + syz-dash/memcache.go | 4 +- syz-dash/patch.go | 19 ++++--- syz-dash/search.html | 18 +++++++ syz-dash/static/style.css | 20 +++++++- 11 files changed, 208 insertions(+), 52 deletions(-) create mode 100644 syz-dash/search.html 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}} + + + + + {{end}} - +{{define "crash_list"}} + + + + + + + + + + + {{range $c := .List}} + + + + + + + + + {{end}} +
{{if .Title}}{{.Title}}{{else}}Crashes:{{end}}
TitleManagerTimeTagLogReport
{{$c.Title}}{{$c.Manager}}{{formatTime $c.Time}}{{$c.Tag}}{{if $c.Log}}log{{end}}{{if $c.Report}}report{{end}}
+{{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"}} - + @@ -10,7 +10,7 @@ {{range $b := $.Bugs}} - + 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 @@ + + + + Syzkaller Dashboard + + + + {{template "header" .Header}} + + {{if .Results}} + {{range $r := .Results}} + {{template "crash_list" $r}} + {{end}} + {{else}} + No matches found + {{end}} + + 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 { -- cgit mrf-deployment
{{.Name}}:{{.Name}} ({{len $.Bugs}}):
Title Count
{{$b.Title}}{{$b.Title}} {{$b.NumCrashes}} {{$b.Repro}} {{formatTime $b.LastTime}}