diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2025-08-13 17:32:02 +0200 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2025-08-14 11:29:47 +0000 |
| commit | 83b914a1837f0f4de012f4dc6bdc84c9d5bab713 (patch) | |
| tree | 4014acd61a94833bd66b17d8066979d2dee33807 /syz-cluster/pkg | |
| parent | 028e3578dbbde98fdb5a4d170f0e1dce7a8a2ab4 (diff) | |
syz-cluster: display statistics
Add a web dashboard page with the main statistics concerning patch
series fuzzing.
Improve the navigation bar on top of the page.
Diffstat (limited to 'syz-cluster/pkg')
| -rw-r--r-- | syz-cluster/pkg/db/stats_repo.go | 92 | ||||
| -rw-r--r-- | syz-cluster/pkg/db/stats_repo_test.go | 33 |
2 files changed, 125 insertions, 0 deletions
diff --git a/syz-cluster/pkg/db/stats_repo.go b/syz-cluster/pkg/db/stats_repo.go new file mode 100644 index 000000000..fa6c1d901 --- /dev/null +++ b/syz-cluster/pkg/db/stats_repo.go @@ -0,0 +1,92 @@ +// Copyright 2025 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 db + +import ( + "context" + "time" + + "cloud.google.com/go/spanner" +) + +type StatsRepository struct { + client *spanner.Client +} + +func NewStatsRepository(client *spanner.Client) *StatsRepository { + return &StatsRepository{ + client: client, + } +} + +type CountPerWeek struct { + Date time.Time `spanner:"Date"` + Count int64 `spanner:"Count"` +} + +func (repo *StatsRepository) ProcessedSeriesPerWeek(ctx context.Context) ( + []*CountPerWeek, error) { + return readEntities[CountPerWeek](ctx, repo.client.Single(), spanner.Statement{ + SQL: `SELECT + TIMESTAMP_TRUNC(Sessions.FinishedAt, WEEK) as Date, + COUNT(*) as Count +FROM Series +JOIN Sessions ON Sessions.ID = Series.LatestSessionID +WHERE FinishedAt IS NOT NULL +GROUP BY Date +ORDER BY Date`, + }) +} + +func (repo *StatsRepository) FindingsPerWeek(ctx context.Context) ( + []*CountPerWeek, error) { + return readEntities[CountPerWeek](ctx, repo.client.Single(), spanner.Statement{ + SQL: `SELECT + TIMESTAMP_TRUNC(Sessions.FinishedAt, WEEK) as Date, + COUNT(*) as Count +FROM Findings +JOIN Sessions ON Sessions.ID = Findings.SessionID +GROUP BY Date +ORDER BY Date`, + }) +} + +type StatusPerWeek struct { + Date time.Time `spanner:"Date"` + Finished int64 `spanner:"Finished"` + Skipped int64 `spanner:"Skipped"` +} + +func (repo *StatsRepository) SessionStatusPerWeek(ctx context.Context) ( + []*StatusPerWeek, error) { + return readEntities[StatusPerWeek](ctx, repo.client.Single(), spanner.Statement{ + SQL: `SELECT + TIMESTAMP_TRUNC(Sessions.FinishedAt, WEEK) as Date, + COUNTIF(Sessions.SkipReason IS NULL) as Finished, + COUNTIF(Sessions.SkipReason IS NOT NULL) as Skipped +FROM Series +JOIN Sessions ON Sessions.ID = Series.LatestSessionID +WHERE FinishedAt IS NOT NULL +GROUP BY Date +ORDER BY Date`, + }) +} + +type DelayPerWeek struct { + Date time.Time `spanner:"Date"` + DelayHours float64 `spanner:"AvgDelayHours"` +} + +func (repo *StatsRepository) DelayPerWeek(ctx context.Context) ( + []*DelayPerWeek, error) { + return readEntities[DelayPerWeek](ctx, repo.client.Single(), spanner.Statement{ + SQL: `SELECT + TIMESTAMP_TRUNC(Sessions.StartedAt, WEEK) as Date, + AVG(TIMESTAMP_DIFF(Sessions.StartedAt,Sessions.CreatedAt, HOUR)) as AvgDelayHours +FROM Sessions +WHERE StartedAt IS NOT NULL +GROUP BY Date +ORDER BY Date`, + }) +} diff --git a/syz-cluster/pkg/db/stats_repo_test.go b/syz-cluster/pkg/db/stats_repo_test.go new file mode 100644 index 000000000..637894edf --- /dev/null +++ b/syz-cluster/pkg/db/stats_repo_test.go @@ -0,0 +1,33 @@ +// Copyright 2025 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 db + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStatsSQLs(t *testing.T) { + // Ideally, there should be some proper tests, but for now let's at least + // check that the SQL queries themselves have no errors. + // That already brings a lot of value. + client, ctx := NewTransientDB(t) + + // Add some data to test field decoding as well. + dtd := &dummyTestData{t, ctx, client} + session := dtd.dummySession(dtd.dummySeries()) + dtd.startSession(session) + dtd.finishSession(session) + + statsRepo := NewStatsRepository(client) + _, err := statsRepo.ProcessedSeriesPerWeek(ctx) + assert.NoError(t, err) + _, err = statsRepo.FindingsPerWeek(ctx) + assert.NoError(t, err) + _, err = statsRepo.SessionStatusPerWeek(ctx) + assert.NoError(t, err) + _, err = statsRepo.DelayPerWeek(ctx) + assert.NoError(t, err) +} |
