aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2023-02-16 18:37:12 +0100
committerAleksandr Nogikh <wp32pw@gmail.com>2023-02-17 12:00:36 +0100
commitbc63aac79a4640f951b050c537ba040bb231449b (patch)
tree45c308692b866132b65cb219c16742657808b868
parent5fe5301b69de36dc64cf350d6924cb3e7b54b9ba (diff)
dashboard: display the subsystem list
Take the counts from the cache, include links to the filtered bug views.
-rw-r--r--dashboard/app/handler.go2
-rw-r--r--dashboard/app/main.go64
-rw-r--r--dashboard/app/main_test.go27
-rw-r--r--dashboard/app/subsystems.html41
-rw-r--r--dashboard/app/templates.html4
-rw-r--r--pkg/html/pages/style.css4
-rw-r--r--pkg/subsystem/service.go8
7 files changed, 148 insertions, 2 deletions
diff --git a/dashboard/app/handler.go b/dashboard/app/handler.go
index cb998a516..333d7847d 100644
--- a/dashboard/app/handler.go
+++ b/dashboard/app/handler.go
@@ -129,6 +129,7 @@ type uiHeader struct {
Namespace string
BugCounts *CachedBugStats
Namespaces []uiNamespace
+ ShowSubsystems bool
}
type uiNamespace struct {
@@ -203,6 +204,7 @@ func commonHeader(c context.Context, r *http.Request, w http.ResponseWriter, ns
}
if ns != adminPage {
h.Namespace = ns
+ h.ShowSubsystems = getSubsystemService(c, ns) != nil
cookie.Namespace = ns
encodeCookie(w, cookie)
cached, err := CacheGet(c, r, ns)
diff --git a/dashboard/app/main.go b/dashboard/app/main.go
index 661d7e992..fa31a7ab2 100644
--- a/dashboard/app/main.go
+++ b/dashboard/app/main.go
@@ -60,6 +60,7 @@ func initHTTPHandlers() {
http.Handle("/"+ns+"/graph/crashes", handlerWrapper(handleGraphCrashes))
http.Handle("/"+ns+"/repos", handlerWrapper(handleRepos))
http.Handle("/"+ns+"/bug-stats", handlerWrapper(handleBugStats))
+ http.Handle("/"+ns+"/subsystems", handlerWrapper(handleSubsystemsList))
}
http.HandleFunc("/cron/cache_update", cacheUpdate)
http.HandleFunc("/cron/deprecate_assets", handleDeprecateAssets)
@@ -144,6 +145,25 @@ type uiRepo struct {
Alias string
}
+type uiSubsystemsPage struct {
+ Header *uiHeader
+ List []*uiSubsystem
+}
+
+type uiSubsystem struct {
+ Name string
+ Lists string
+ Maintainers string
+ Open uiSubsystemStats
+ Fixed uiSubsystemStats
+ Invalid uiSubsystemStats
+}
+
+type uiSubsystemStats struct {
+ Count int
+ Link string
+}
+
type uiAdminPage struct {
Header *uiHeader
Log []byte
@@ -770,6 +790,50 @@ func getUIJob(c context.Context, bug *Bug, jobType JobType) (*uiJob, error) {
return makeUIJob(job, jobKey, bug, crash, build), nil
}
+func handleSubsystemsList(c context.Context, w http.ResponseWriter, r *http.Request) error {
+ hdr, err := commonHeader(c, r, w, "")
+ if err != nil {
+ return err
+ }
+ cached, err := CacheGet(c, r, hdr.Namespace)
+ if err != nil {
+ return err
+ }
+ service := getSubsystemService(c, hdr.Namespace)
+ if service == nil {
+ return fmt.Errorf("the namespace does not have subsystems")
+ }
+ list := []*uiSubsystem{}
+ for _, item := range service.List() {
+ stats := cached.Subsystems[item.Name]
+ list = append(list, &uiSubsystem{
+ Name: item.Name,
+ Lists: strings.Join(item.Lists, ", "),
+ Maintainers: strings.Join(item.Maintainers, ", "),
+ Open: uiSubsystemStats{
+ Count: stats.Open,
+ Link: html.AmendURL("/"+hdr.Namespace,
+ "subsystem", item.Name),
+ },
+ Fixed: uiSubsystemStats{
+ Count: stats.Fixed,
+ Link: html.AmendURL("/"+hdr.Namespace+"/fixed",
+ "subsystem", item.Name),
+ },
+ Invalid: uiSubsystemStats{
+ Count: stats.Invalid,
+ Link: html.AmendURL("/"+hdr.Namespace+"/invalid",
+ "subsystem", item.Name),
+ },
+ })
+ }
+ sort.Slice(list, func(i, j int) bool { return list[i].Name < list[j].Name })
+ return serveTemplate(w, "subsystems.html", &uiSubsystemsPage{
+ Header: hdr,
+ List: list,
+ })
+}
+
// handleText serves plain text blobs (crash logs, reports, reproducers, etc).
func handleTextImpl(c context.Context, w http.ResponseWriter, r *http.Request, tag string) error {
var id int64
diff --git a/dashboard/app/main_test.go b/dashboard/app/main_test.go
index 88cb0dcf5..e69dd0275 100644
--- a/dashboard/app/main_test.go
+++ b/dashboard/app/main_test.go
@@ -183,3 +183,30 @@ func TestMainBugFilters(t *testing.T) {
assert.NotContains(t, string(reply), build1.Manager) // managers are hidden
assert.Contains(t, string(reply), "Applied filters") // we're seeing a prompt to disable the filter
}
+
+func TestSubsystemsList(t *testing.T) {
+ c := NewCtx(t)
+ defer c.Close()
+
+ client := c.client
+ build := testBuild(1)
+ client.UploadBuild(build)
+
+ crash1 := testCrash(build, 1)
+ crash1.GuiltyFiles = []string{"a.c"}
+ client.ReportCrash(crash1)
+ client.pollBug()
+
+ crash2 := testCrash(build, 2)
+ crash2.GuiltyFiles = []string{"b.c"}
+ client.ReportCrash(crash2)
+ client.updateBug(client.pollBug().ID, dashapi.BugStatusInvalid, "")
+
+ _, err := c.AuthGET(AccessUser, "/cron/refresh_subsystems")
+ c.expectOK(err)
+
+ reply, err := c.AuthGET(AccessAdmin, "/test1/subsystems")
+ c.expectOK(err)
+ assert.Contains(t, string(reply), "subsystemA")
+ assert.Contains(t, string(reply), "subsystemB")
+}
diff --git a/dashboard/app/subsystems.html b/dashboard/app/subsystems.html
new file mode 100644
index 000000000..254a113fe
--- /dev/null
+++ b/dashboard/app/subsystems.html
@@ -0,0 +1,41 @@
+{{/*
+Copyright 2023 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.
+
+The list of polled trees.
+*/}}
+
+<!doctype html>
+<html>
+<head>
+ {{template "head" .Header}}
+ <title>syzbot</title>
+</head>
+<body>
+ {{template "header" .Header}}
+ <h2>The list of subsystems</h2><br>
+ <i>(*) Note that the numbers below do not represent the latest data. They are updated once an hour.</i><br>
+ <i>(**) If you have any ideas/suggestions on how to improve this list, feel free to contact us at <a href="mailto:syzkaller@googlegroups.com">syzkaller@googlegroups.com</a>.</i>
+ <table class="list_table">
+ <caption>Subsystems list</caption>
+ <tr>
+ <th>Name</th>
+ <th>Lists</th>
+ <th>Maintainers</th>
+ <th>Open bugs</th>
+ <th>Fixed</th>
+ <th>Invalid</th>
+ </tr>
+ {{range $item := .List}}
+ <tr>
+ <td>{{$item.Name}}</td>
+ <td>{{$item.Lists}}</td>
+ <td class="maintainers">{{$item.Maintainers}}</td>
+ <td>{{link $item.Open.Link (printf "%d" $item.Open.Count)}}</td>
+ <td>{{link $item.Fixed.Link (printf "%d" $item.Fixed.Count)}}</td>
+ <td>{{link $item.Invalid.Link (printf "%d" $item.Invalid.Count)}}</td>
+ </tr>
+ {{end}}
+ </table>
+</body>
+</html>
diff --git a/dashboard/app/templates.html b/dashboard/app/templates.html
index 2b7ef61c1..f40361f85 100644
--- a/dashboard/app/templates.html
+++ b/dashboard/app/templates.html
@@ -63,6 +63,10 @@ Use of this source code is governed by Apache 2 LICENSE that can be found in the
<span style="color:ForestGreen;">🐞</span> Fixed [{{$.BugCounts.Fixed}}]</a>
<a class="navigation_tab{{if eq .URLPath (printf "/%v/invalid" $.Namespace)}}_selected{{end}}" href='/{{$.Namespace}}/invalid'>
<span style="color:RoyalBlue;">🐞</span> Invalid [{{$.BugCounts.Invalid}}]</a>
+ {{if .ShowSubsystems}}
+ <a class="navigation_tab{{if eq .URLPath (printf "/%v/subsystems" $.Namespace)}}_selected{{end}}" href='/{{$.Namespace}}/subsystems'>
+ <span style="color:DeepPink;">≡</span> Subsystems</a>
+ {{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/lifetimes" $.Namespace)}}_selected{{end}}" href='/{{$.Namespace}}/graph/lifetimes'>
diff --git a/pkg/html/pages/style.css b/pkg/html/pages/style.css
index aecde7554..ebeb7d012 100644
--- a/pkg/html/pages/style.css
+++ b/pkg/html/pages/style.css
@@ -140,8 +140,8 @@ table td, table th {
}
.list_table .maintainers {
- width: 150pt;
- max-width: 150pt;
+ max-width: 300pt;
+ white-space: normal;
}
.list_table .result {
diff --git a/pkg/subsystem/service.go b/pkg/subsystem/service.go
index b27200c30..d8c144cc4 100644
--- a/pkg/subsystem/service.go
+++ b/pkg/subsystem/service.go
@@ -45,3 +45,11 @@ func MakeService(list []*Subsystem) (*Service, error) {
func (s *Service) ByName(name string) *Subsystem {
return s.perName[name]
}
+
+func (s *Service) List() []*Subsystem {
+ ret := []*Subsystem{}
+ for _, item := range s.perName {
+ ret = append(ret, item)
+ }
+ return ret
+}