aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-05-27 16:55:58 +0200
committerDmitry Vyukov <dvyukov@google.com>2024-05-31 10:18:05 +0000
commit4a71ac623f5560d8fc83f0840f7dded5775976ac (patch)
tree58efe436722445c3dbb69e44bf79970a25e02db8
parent0c378259b1aa20c6bed6c2efd19198c0303bd18b (diff)
dashboard/app: add bugs found per month graph
Useful to estimate overall syzbot performance.
-rw-r--r--dashboard/app/graph_bugs.html2
-rw-r--r--dashboard/app/graph_crashes.html2
-rw-r--r--dashboard/app/graph_found_bugs.html44
-rw-r--r--dashboard/app/graph_fuzzing.html2
-rw-r--r--dashboard/app/graphs.go136
-rw-r--r--dashboard/app/graphs_test.go5
-rw-r--r--dashboard/app/main.go1
-rw-r--r--dashboard/app/templates.html2
8 files changed, 175 insertions, 19 deletions
diff --git a/dashboard/app/graph_bugs.html b/dashboard/app/graph_bugs.html
index 55ce0dbab..03342614d 100644
--- a/dashboard/app/graph_bugs.html
+++ b/dashboard/app/graph_bugs.html
@@ -18,7 +18,7 @@ Bugs statistics graph.
function drawCharts() {
new google.visualization.LineChart(document.getElementById('graph_div')).
draw(google.visualization.arrayToDataTable([
- ["-", {{range $.Graph.Headers}}"{{.}}", {{end}}],
+ ["-", {{range $.Graph.Headers}}"{{.Name}}", {{end}}],
{{range $.Graph.Columns}}["{{.Hint}}", {{range .Vals}}{{.Val}},{{end}}],{{end}}
]), {
width: "100%",
diff --git a/dashboard/app/graph_crashes.html b/dashboard/app/graph_crashes.html
index 3e949af15..e44968154 100644
--- a/dashboard/app/graph_crashes.html
+++ b/dashboard/app/graph_crashes.html
@@ -20,7 +20,7 @@ Manager statistics graphs.
var data = new google.visualization.DataTable();
data.addColumn({type: 'string'});
{{range $.Graph.Headers}}
- data.addColumn({type: 'number', label: '{{.}}'});
+ data.addColumn({type: 'number', label: '{{.Name}}'});
data.addColumn({type: 'string', role: 'tooltip'});
{{- end}}
data.addRows([ {{range $.Graph.Columns}}
diff --git a/dashboard/app/graph_found_bugs.html b/dashboard/app/graph_found_bugs.html
new file mode 100644
index 000000000..4bf29434b
--- /dev/null
+++ b/dashboard/app/graph_found_bugs.html
@@ -0,0 +1,44 @@
+{{/*
+Copyright 2024 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.
+
+Number of found bugs per month.
+*/}}
+
+<!doctype html>
+<html>
+<head>
+ <title>{{.Header.Namespace}} bugs found per month</title>
+ {{template "head" .Header}}
+
+ <script type="text/javascript" src="https://www.google.com/jsapi"></script>
+ <script type="text/javascript">
+ google.load("visualization", "1", {packages:["corechart"]});
+ google.setOnLoadCallback(function() {
+ new google.visualization.ColumnChart (
+ document.getElementById('graph_div')).
+ draw(google.visualization.arrayToDataTable([
+ ["-", {{range $.Graph.Headers}}"{{.Name}}", {{end}}],
+ {{range $.Graph.Columns}}["{{.Hint}}", {{range .Vals}}{{.Val}},{{end}}],{{end}}
+ ]), {
+ width: "100%",
+ chartArea: {width: '90%', height: '85%'},
+ legend: {position: 'in'},
+ focusTarget: "category",
+ isStacked: true,
+ vAxis: {minValue: 1, textPosition: 'out', gridlines: {multiple: 1}, minorGridlines: {multiple: 1}},
+ hAxis: {minValue: 1, textPosition: 'out', maxAlternation: 1, gridlines: {multiple: 1}, minorGridlines: {multiple: 1}},
+ series: {
+ {{range $idx, $hdr := $.Graph.Headers}}
+ {{$idx}}: {color: "{{$hdr.Color}}"},
+ {{end}}
+ },
+ })
+ });
+ </script>
+</head>
+<body>
+ {{template "header" .Header}}
+ <div id="graph_div"></div>
+</body>
+</html>
diff --git a/dashboard/app/graph_fuzzing.html b/dashboard/app/graph_fuzzing.html
index 5ed07ba63..d7a22e6a6 100644
--- a/dashboard/app/graph_fuzzing.html
+++ b/dashboard/app/graph_fuzzing.html
@@ -19,7 +19,7 @@ Manager statistics graphs.
var data = new google.visualization.DataTable();
data.addColumn({type: 'string'});
{{range $.Graph.Headers}}
- data.addColumn({type: 'number', label: '{{.}}'});
+ data.addColumn({type: 'number', label: '{{.Name}}'});
data.addColumn({type: 'string', role: 'tooltip'});
{{- end}}
data.addRows([ {{range $.Graph.Columns}}
diff --git a/dashboard/app/graphs.go b/dashboard/app/graphs.go
index f4789b682..a0f48f312 100644
--- a/dashboard/app/graphs.go
+++ b/dashboard/app/graphs.go
@@ -26,6 +26,11 @@ type uiBugLifetimesPage struct {
Lifetimes []uiBugLifetime
}
+type uiReportedBugsPage struct {
+ Header *uiHeader
+ Graph *uiGraph
+}
+
type uiBugLifetime struct {
Reported time.Time
Fixed float32
@@ -53,10 +58,15 @@ type uiCrashesPage struct {
}
type uiGraph struct {
- Headers []string
+ Headers []uiGraphHeader
Columns []uiGraphColumn
}
+type uiGraphHeader struct {
+ Name string
+ Color string
+}
+
type uiGraphColumn struct {
Hint string
Vals []uiGraphValue
@@ -114,7 +124,7 @@ func handleKernelHealthGraph(c context.Context, w http.ResponseWriter, r *http.R
if err != nil {
return err
}
- bugs, err := loadGraphBugs(c, hdr.Namespace)
+ bugs, err := loadGraphBugs(c, hdr.Namespace, true)
if err != nil {
return err
}
@@ -131,7 +141,7 @@ func handleGraphLifetimes(c context.Context, w http.ResponseWriter, r *http.Requ
if err != nil {
return err
}
- bugs, err := loadGraphBugs(c, hdr.Namespace)
+ bugs, err := loadGraphBugs(c, hdr.Namespace, true)
if err != nil {
return err
}
@@ -158,7 +168,24 @@ func handleGraphLifetimes(c context.Context, w http.ResponseWriter, r *http.Requ
return serveTemplate(w, "graph_lifetimes.html", data)
}
-func loadGraphBugs(c context.Context, ns string) ([]*Bug, error) {
+// nolint: dupl
+func handleFoundBugsGraph(c context.Context, w http.ResponseWriter, r *http.Request) error {
+ hdr, err := commonHeader(c, r, w, "")
+ if err != nil {
+ return err
+ }
+ bugs, err := loadGraphBugs(c, hdr.Namespace, false)
+ if err != nil {
+ return err
+ }
+ data := &uiReportedBugsPage{
+ Header: hdr,
+ Graph: createFoundBugs(c, bugs),
+ }
+ return serveTemplate(w, "graph_found_bugs.html", data)
+}
+
+func loadGraphBugs(c context.Context, ns string, removeDupInvalid bool) ([]*Bug, error) {
filter := func(query *db.Query) *db.Query {
return query.Filter("Namespace=", ns)
}
@@ -176,20 +203,23 @@ func loadGraphBugs(c context.Context, ns string) ([]*Bug, error) {
continue
}
bugReporting := lastReportedReporting(bug)
- if bugReporting == nil || bugReporting.Auto && bug.Status == BugStatusInvalid {
+ if removeDupInvalid &&
+ (bugReporting == nil || bugReporting.Auto && bug.Status == BugStatusInvalid) {
// These bugs were auto-obsoleted before getting released.
continue
}
}
- dup := false
- for _, com := range bug.Commits {
- if fixes[com] {
- dup = true
+ if removeDupInvalid {
+ dup := false
+ for _, com := range bug.Commits {
+ if fixes[com] {
+ dup = true
+ }
+ fixes[com] = true
+ }
+ if dup {
+ continue
}
- fixes[com] = true
- }
- if dup {
- continue
}
bugs[n] = bug
n++
@@ -263,7 +293,78 @@ func createBugsGraph(c context.Context, bugs []*Bug) *uiGraph {
columns = append(columns, col)
}
return &uiGraph{
- Headers: []string{"open bugs", "total reported", "total fixed"},
+ Headers: []uiGraphHeader{{Name: "open bugs"}, {Name: "total reported"}, {Name: "total fixed"}},
+ Columns: columns,
+ }
+}
+
+func createFoundBugs(c context.Context, bugs []*Bug) *uiGraph {
+ const projected = "projected"
+ // This is linux-specific at the moment, potentially can move to pkg/report/crash
+ // and extend to other OSes.
+ // nolint: lll
+ types := []struct {
+ name string
+ color string
+ re *regexp.Regexp
+ }{
+ {"KASAN", "Red", regexp.MustCompile(`^KASAN:`)},
+ {"KMSAN", "Gold", regexp.MustCompile(`^KMSAN:`)},
+ {"KCSAN", "Fuchsia", regexp.MustCompile(`^KCSAN:`)},
+ {"mem safety", "OrangeRed", regexp.MustCompile(`^(WARNING: refcount bug|UBSAN: array-index|BUG: corrupted list|BUG: unable to handle kernel paging request)`)},
+ {"mem leak", "MediumSeaGreen", regexp.MustCompile(`^memory leak`)},
+ {"locking", "DodgerBlue", regexp.MustCompile(`^(BUG: sleeping function|BUG: spinlock recursion|BUG: using ([a-z_]+)\\(\\) in preemptible|inconsistent lock state|WARNING: still has locks held|possible deadlock|WARNING: suspicious RCU usage)`)},
+ {"hangs/stalls", "LightSalmon", regexp.MustCompile(`^(BUG: soft lockup|INFO: rcu .* stall|INFO: task hung)`)},
+ // This must be at the end, otherwise "BUG:" will match other error types.
+ {"DoS", "Violet", regexp.MustCompile(`^(BUG:|kernel BUG|divide error|Internal error in|kernel panic:|general protection fault)`)},
+ {"other", "Gray", regexp.MustCompile(`.*`)},
+ {projected, "LightGray", nil},
+ }
+ var sorted []time.Time
+ months := make(map[time.Time]map[string]int)
+ for _, bug := range bugs {
+ for _, typ := range types {
+ if !typ.re.MatchString(bug.Title) {
+ continue
+ }
+ t := bug.FirstTime
+ m := time.Date(t.Year(), t.Month(), 0, 0, 0, 0, 0, time.UTC)
+ if months[m] == nil {
+ months[m] = make(map[string]int)
+ sorted = append(sorted, m)
+ }
+ months[m][typ.name]++
+ break
+ }
+ }
+ sort.Slice(sorted, func(i, j int) bool {
+ return sorted[i].Before(sorted[j])
+ })
+ now := timeNow(c)
+ thisMonth := time.Date(now.Year(), now.Month(), 0, 0, 0, 0, 0, time.UTC)
+ if m := months[thisMonth]; m != nil {
+ total := 0
+ for _, c := range m {
+ total += c
+ }
+ nextMonth := thisMonth.AddDate(0, 1, 0)
+ m[projected] = int(float64(total) / float64(now.Sub(thisMonth)) * float64(nextMonth.Sub(now)))
+ }
+ var headers []uiGraphHeader
+ for _, typ := range types {
+ headers = append(headers, uiGraphHeader{Name: typ.name, Color: typ.color})
+ }
+ var columns []uiGraphColumn
+ for _, month := range sorted {
+ col := uiGraphColumn{Hint: month.Format("Jan-06")}
+ stats := months[month]
+ for _, typ := range types {
+ col.Vals = append(col.Vals, uiGraphValue{Val: float32(stats[typ.name])})
+ }
+ columns = append(columns, col)
+ }
+ return &uiGraph{
+ Headers: headers,
Columns: columns,
}
}
@@ -361,7 +462,7 @@ func createManagersGraph(c context.Context, ns string, selManagers, selMetrics [
graph := &uiGraph{}
for _, mgr := range selManagers {
for _, metric := range selMetrics {
- graph.Headers = append(graph.Headers, mgr+"-"+metric)
+ graph.Headers = append(graph.Headers, uiGraphHeader{Name: mgr + "-" + metric})
}
}
now := timeNow(c)
@@ -603,7 +704,10 @@ func createCrashesTable(c context.Context, ns string, days int, bugs []*Bug) *ui
func createCrashesGraph(c context.Context, ns string, regexps []string, days int, bugs []*Bug) (*uiGraph, error) {
const dayDuration = 24 * time.Hour
- graph := &uiGraph{Headers: regexps}
+ graph := &uiGraph{}
+ for _, re := range regexps {
+ graph.Headers = append(graph.Headers, uiGraphHeader{Name: re})
+ }
startDay := timeNow(c).Add(time.Duration(-days) * dayDuration)
// Step 1: fill the whole table with empty values.
dateToIdx := make(map[int]int)
diff --git a/dashboard/app/graphs_test.go b/dashboard/app/graphs_test.go
index 57aba18de..2e1391ed0 100644
--- a/dashboard/app/graphs_test.go
+++ b/dashboard/app/graphs_test.go
@@ -110,6 +110,11 @@ func TestManagersGraphs(t *testing.T) {
c.expectOK(err)
// TODO: check reply
_ = reply
+
+ reply, err = c.AuthGET(AccessAdmin, "/test2/graph/found-bugs")
+ c.expectOK(err)
+ // TODO: check reply
+ _ = reply
}
func managersGraphFixture(t *testing.T) *Ctx {
diff --git a/dashboard/app/main.go b/dashboard/app/main.go
index 2ac74af27..4e26fb86b 100644
--- a/dashboard/app/main.go
+++ b/dashboard/app/main.go
@@ -63,6 +63,7 @@ func initHTTPHandlers() {
http.Handle("/"+ns+"/graph/lifetimes", handlerWrapper(handleGraphLifetimes))
http.Handle("/"+ns+"/graph/fuzzing", handlerWrapper(handleGraphFuzzing))
http.Handle("/"+ns+"/graph/crashes", handlerWrapper(handleGraphCrashes))
+ http.Handle("/"+ns+"/graph/found-bugs", handlerWrapper(handleFoundBugsGraph))
http.Handle("/"+ns+"/repos", handlerWrapper(handleRepos))
http.Handle("/"+ns+"/bug-summaries", handlerWrapper(handleBugSummaries))
http.Handle("/"+ns+"/subsystems", handlerWrapper(handleSubsystemsList))
diff --git a/dashboard/app/templates.html b/dashboard/app/templates.html
index 4933c8bf4..4cfd5fb37 100644
--- a/dashboard/app/templates.html
+++ b/dashboard/app/templates.html
@@ -76,6 +76,8 @@ Use of this source code is governed by Apache 2 LICENSE that can be found in the
{{end}}
<a class="navigation_tab{{if eq .URLPath (printf "/%v/graph/bugs" $.Namespace)}}_selected{{end}}" href='/{{$.Namespace}}/graph/bugs'>
<span style="color:DarkOrange;">📈</span> Kernel Health</a>
+ <a class="navigation_tab{{if eq .URLPath (printf "/%v/graph/found-bugs" $.Namespace)}}_selected{{end}}" href='/{{$.Namespace}}/graph/found-bugs'>
+ <span style="color:DarkOrange;">📈</span> Bugs/Month</a>
<a class="navigation_tab{{if eq .URLPath (printf "/%v/graph/lifetimes" $.Namespace)}}_selected{{end}}" href='/{{$.Namespace}}/graph/lifetimes'>
<span style="color:DarkOrange;">📈</span> Bug Lifetimes</a>
<a class="navigation_tab{{if eq .URLPath (printf "/%v/graph/fuzzing" $.Namespace)}}_selected{{end}}" href='/{{$.Namespace}}/graph/fuzzing'>