diff options
| author | Taras Madan <tarasmadan@google.com> | 2024-06-17 11:18:56 +0200 |
|---|---|---|
| committer | Taras Madan <tarasmadan@google.com> | 2024-06-20 09:22:00 +0000 |
| commit | 82874357f78ea9aacf31d1da403372f573f45e2d (patch) | |
| tree | 4939a8d6ce28a6a21e1bba1452e05d9e617a1e35 | |
| parent | 99e56cfbc20592aa8bc1bd010d73eba241351b8d (diff) | |
dashboard/app: add coverage histogram
| -rw-r--r-- | dashboard/app/entities_spanner.go | 74 | ||||
| -rw-r--r-- | dashboard/app/graphs.go | 40 | ||||
| -rw-r--r-- | dashboard/app/main.go | 1 | ||||
| -rw-r--r-- | dashboard/app/templates.html | 4 | ||||
| -rw-r--r-- | go.mod | 2 |
5 files changed, 119 insertions, 2 deletions
diff --git a/dashboard/app/entities_spanner.go b/dashboard/app/entities_spanner.go new file mode 100644 index 000000000..7f2c47b97 --- /dev/null +++ b/dashboard/app/entities_spanner.go @@ -0,0 +1,74 @@ +// 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. + +package main + +import ( + "context" + "fmt" + "os" + "time" + + "cloud.google.com/go/civil" + "cloud.google.com/go/spanner" + "google.golang.org/api/iterator" +) + +// This file contains definitions of entities stored in spanner. + +type CoverageHistory struct { + instrumented map[string]int64 + covered map[string]int64 +} + +// MergedCoverage uses dates, not time. +func MergedCoverage(ctx context.Context, ns string, fromDate, toDate civil.Date) (*CoverageHistory, error) { + projectID := os.Getenv("GOOGLE_CLOUD_PROJECT") + client, err := spanner.NewClient(ctx, "projects/"+projectID+"/instances/syzbot/databases/coverage") + if err != nil { + panic(fmt.Sprintf("spanner.NewClient() failed: %s", err.Error())) + } + defer client.Close() + + stmt := spanner.Statement{ + SQL: `select + dateto targetdate, + cast(sum(instrumented) as INTEGER) as instrumented, + cast(sum(covered) as INTEGER) as covered + from "files" + where namespace=$1 and dateto>=$2 and dateto<=$3 + group by targetdate`, + Params: map[string]interface{}{ + "p1": ns, + "p2": fromDate, + "p3": toDate, + }, + } + + iter := client.Single().Query(ctx, stmt) + defer iter.Stop() + res := &CoverageHistory{ + instrumented: map[string]int64{}, + covered: map[string]int64{}, + } + for { + row, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { + return nil, fmt.Errorf("failed to iter.Next() spanner DB: %w", err) + } + var r struct { + Targetdate time.Time + Instrumented int64 + Covered int64 + } + if err = row.ToStruct(&r); err != nil { + return nil, fmt.Errorf("failed to row.ToStruct() spanner DB: %w", err) + } + res.instrumented[r.Targetdate.Format(time.DateOnly)] = r.Instrumented + res.covered[r.Targetdate.Format(time.DateOnly)] = r.Covered + } + return res, nil +} diff --git a/dashboard/app/graphs.go b/dashboard/app/graphs.go index f2c991db7..c363417d0 100644 --- a/dashboard/app/graphs.go +++ b/dashboard/app/graphs.go @@ -13,6 +13,7 @@ import ( "strconv" "time" + "cloud.google.com/go/civil" db "google.golang.org/appengine/v2/datastore" ) @@ -187,6 +188,45 @@ func handleFoundBugsGraph(c context.Context, w http.ResponseWriter, r *http.Requ return serveTemplate(w, "graph_histogram.html", data) } +func handleCoverageGraph(c context.Context, w http.ResponseWriter, r *http.Request) error { + hdr, err := commonHeader(c, r, w, "") + if err != nil { + return err + } + yesterday := civil.DateOf(time.Now().Add(-1 * 24 * time.Hour)) + monthAgo := yesterday.AddDays(-31) + hist, err := MergedCoverage(c, hdr.Namespace, monthAgo, yesterday) + if err != nil { + return err + } + dates := []string{} + for i := 31; i >= 0; i-- { + dates = append(dates, yesterday.AddDays(-i).String()) + } + cols := []uiGraphColumn{} + for _, date := range dates { + if _, ok := hist.covered[date]; !ok || hist.instrumented[date] == 0 { + cols = append(cols, uiGraphColumn{Hint: date, Vals: []uiGraphValue{{IsNull: true}}}) + } else { + cols = append(cols, uiGraphColumn{ + Vals: []uiGraphValue{{Val: float32(hist.covered[date]) / float32(hist.instrumented[date])}}, + Hint: date, + }) + } + } + data := &uiHistogramPage{ + Title: hdr.Namespace + " coverage", + Header: hdr, + Graph: &uiGraph{ + Headers: []uiGraphHeader{ + {Name: "Total", Color: "Red"}, + }, + Columns: cols, + }, + } + return serveTemplate(w, "graph_histogram.html", data) +} + func loadGraphBugs(c context.Context, ns string) ([]*Bug, error) { filter := func(query *db.Query) *db.Query { return query.Filter("Namespace=", ns) diff --git a/dashboard/app/main.go b/dashboard/app/main.go index 4e26fb86b..42a0a9da3 100644 --- a/dashboard/app/main.go +++ b/dashboard/app/main.go @@ -64,6 +64,7 @@ func initHTTPHandlers() { 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+"/graph/coverage", handlerWrapper(handleCoverageGraph)) 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 4cfd5fb37..ffd7566ec 100644 --- a/dashboard/app/templates.html +++ b/dashboard/app/templates.html @@ -83,7 +83,9 @@ Use of this source code is governed by Apache 2 LICENSE that can be found in the <a class="navigation_tab{{if eq .URLPath (printf "/%v/graph/fuzzing" $.Namespace)}}_selected{{end}}" href='/{{$.Namespace}}/graph/fuzzing'> <span style="color:DarkOrange;">๐</span> Fuzzing</a> <a class="navigation_tab{{if eq .URLPath (printf "/%v/graph/crashes" $.Namespace)}}_selected{{end}}" href='/{{$.Namespace}}/graph/crashes'> - <span style="color:DarkOrange;">๐</span> Crashes</a> + <span style="color:DarkOrange;">๐</span> Crashes</a> + <a class="navigation_tab{{if eq .URLPath (printf "/%v/graph/crashes" $.Namespace)}}_selected{{end}}" href='/{{$.Namespace}}/graph/coverage'> + <span style="color:DarkOrange;">๐</span> Coverage</a> </td> {{if .ContactEmail}} <td class="navigation-right"> @@ -3,6 +3,7 @@ module github.com/google/syzkaller go 1.21 require ( + cloud.google.com/go v0.114.0 cloud.google.com/go/logging v1.10.0 cloud.google.com/go/profiler v0.4.0 cloud.google.com/go/pubsub v1.38.0 @@ -37,7 +38,6 @@ require ( require ( 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect 4d63.com/gochecknoglobals v0.2.1 // indirect - cloud.google.com/go v0.114.0 // indirect cloud.google.com/go/ai v0.5.0 // indirect cloud.google.com/go/auth v0.4.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect |
