aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2025-04-08 16:29:16 +0200
committerAleksandr Nogikh <nogikh@google.com>2025-04-10 10:37:46 +0000
commitf68248c3be1d60bb978ff7034234d9e32597ee1b (patch)
tree2d87ea956abd579dca52eae07bc325ac7c2445dd
parentc8f1c38aba5ba9a902386908635f6e69c85cfb01 (diff)
syz-cluster: display the number of findings per series
It will help identify the series to highlight.
-rw-r--r--syz-cluster/dashboard/templates/index.html2
-rw-r--r--syz-cluster/pkg/db/series_repo.go110
-rw-r--r--syz-cluster/pkg/db/series_repo_test.go24
3 files changed, 100 insertions, 36 deletions
diff --git a/syz-cluster/dashboard/templates/index.html b/syz-cluster/dashboard/templates/index.html
index f51e5d34f..c87285b58 100644
--- a/syz-cluster/dashboard/templates/index.html
+++ b/syz-cluster/dashboard/templates/index.html
@@ -54,7 +54,7 @@
<td>{{.Series.AuthorEmail}}</td>
<td>
{{if .Session}}
- {{.Session.Status}}
+ {{.Session.Status}} {{if gt .Findings 0}}<b>[{{.Findings}} findings]</b>{{end}}
{{else}}
-
{{end}}
diff --git a/syz-cluster/pkg/db/series_repo.go b/syz-cluster/pkg/db/series_repo.go
index 554a32b00..5f414b7cc 100644
--- a/syz-cluster/pkg/db/series_repo.go
+++ b/syz-cluster/pkg/db/series_repo.go
@@ -127,8 +127,9 @@ func (repo *SeriesRepository) Count(ctx context.Context) (int, error) {
}
type SeriesWithSession struct {
- Series *Series
- Session *Session
+ Series *Series
+ Session *Session
+ Findings int
}
type SeriesFilter struct {
@@ -191,37 +192,96 @@ func (repo *SeriesRepository) ListLatest(ctx context.Context, filter SeriesFilte
}
// Now query Sessions.
- var keys []string
var ret []*SeriesWithSession
- idToSeries := map[string]*SeriesWithSession{}
for _, series := range seriesList {
+ obj := &SeriesWithSession{Series: series}
+ ret = append(ret, obj)
+ }
+
+ // And the rest of the data.
+ err = repo.querySessions(ctx, ro, ret)
+ if err != nil {
+ return nil, fmt.Errorf("failed to query sessions: %w", err)
+ }
+ err = repo.queryFindingCounts(ctx, ro, ret)
+ if err != nil {
+ return nil, fmt.Errorf("failed to query finding counts: %w", err)
+ }
+ return ret, nil
+}
+
+func (repo *SeriesRepository) querySessions(ctx context.Context, ro *spanner.ReadOnlyTransaction,
+ seriesList []*SeriesWithSession) error {
+ idToSeries := map[string]*SeriesWithSession{}
+ var keys []string
+ for _, item := range seriesList {
+ series := item.Series
+ idToSeries[series.ID] = item
if !series.LatestSessionID.IsNull() {
keys = append(keys, series.LatestSessionID.String())
}
- obj := &SeriesWithSession{Series: series}
- ret = append(ret, obj)
- idToSeries[series.ID] = obj
- }
- if len(keys) > 0 {
- iter := ro.Query(ctx, spanner.Statement{
- SQL: "SELECT * FROM Sessions WHERE ID IN UNNEST(@ids)",
- Params: map[string]interface{}{
- "ids": keys,
- },
- })
- defer iter.Stop()
- sessions, err := readEntities[Session](iter)
- if err != nil {
- return nil, err
+ }
+ if len(keys) == 0 {
+ return nil
+ }
+ iter := ro.Query(ctx, spanner.Statement{
+ SQL: "SELECT * FROM Sessions WHERE ID IN UNNEST(@ids)",
+ Params: map[string]interface{}{
+ "ids": keys,
+ },
+ })
+ defer iter.Stop()
+ sessions, err := readEntities[Session](iter)
+ if err != nil {
+ return err
+ }
+ for _, session := range sessions {
+ obj := idToSeries[session.SeriesID]
+ if obj != nil {
+ obj.Session = session
}
- for _, session := range sessions {
- obj := idToSeries[session.SeriesID]
- if obj != nil {
- obj.Session = session
- }
+ }
+ return nil
+}
+
+func (repo *SeriesRepository) queryFindingCounts(ctx context.Context, ro *spanner.ReadOnlyTransaction,
+ seriesList []*SeriesWithSession) error {
+ var keys []string
+ sessionToSeries := map[string]*SeriesWithSession{}
+ for _, series := range seriesList {
+ if series.Session == nil || series.Session.Status() != SessionStatusFinished {
+ continue
}
+ keys = append(keys, series.Session.ID)
+ sessionToSeries[series.Session.ID] = series
}
- return ret, nil
+ if len(keys) == 0 {
+ return nil
+ }
+
+ type findingCount struct {
+ SessionID string `spanner:"SessionID"`
+ Count int64 `spanner:"Count"`
+ }
+
+ stmt := spanner.Statement{
+ SQL: "SELECT `SessionID`, COUNT(`ID`) as `Count` FROM `Findings` " +
+ "WHERE `SessionID` IN UNNEST(@ids) GROUP BY `SessionID`",
+ Params: map[string]interface{}{
+ "ids": keys,
+ },
+ }
+ iter := repo.client.Single().Query(ctx, stmt)
+ defer iter.Stop()
+
+ list, err := readEntities[findingCount](iter)
+ if err != nil {
+ return err
+ }
+ for _, item := range list {
+ sessionToSeries[item.SessionID].Findings = int(item.Count)
+ }
+ return nil
}
// golint sees too much similarity with SessionRepository's ListForSeries, but in reality there's not.
diff --git a/syz-cluster/pkg/db/series_repo_test.go b/syz-cluster/pkg/db/series_repo_test.go
index 694893514..ca84b70a8 100644
--- a/syz-cluster/pkg/db/series_repo_test.go
+++ b/syz-cluster/pkg/db/series_repo_test.go
@@ -125,30 +125,34 @@ func TestSeriesRepositoryList(t *testing.T) {
})
// Start one session to test filtering by status.
- sessionRepo := NewSessionRepository(client)
series2, err := repo.GetByExtID(ctx, "series-2")
assert.NoError(t, err)
- session := &Session{
- SeriesID: series2.ID,
- CreatedAt: time.Now(),
- }
- err = sessionRepo.Insert(ctx, session)
- assert.NoError(t, err)
+ dtd := &dummyTestData{t, ctx, client}
+ session := dtd.dummySession(series2)
+ dtd.addSessionTest(session, "test")
t.Run("filter_status_waiting", func(t *testing.T) {
list, err := repo.ListLatest(ctx, SeriesFilter{Status: SessionStatusWaiting}, time.Time{})
assert.NoError(t, err)
assert.Len(t, list, 1)
})
- err = sessionRepo.Start(ctx, session.ID)
- assert.NoError(t, err)
-
+ dtd.startSession(session)
t.Run("filter_status_in_progress", func(t *testing.T) {
list, err := repo.ListLatest(ctx, SeriesFilter{Status: SessionStatusInProgress}, time.Time{})
assert.NoError(t, err)
assert.Len(t, list, 1)
})
+
+ dtd.addSessionTest(session, "test")
+ dtd.addFinding(session, "title", "test")
+ dtd.finishSession(session)
+ t.Run("query_finding_count", func(t *testing.T) {
+ list, err := repo.ListLatest(ctx, SeriesFilter{Status: SessionStatusFinished}, time.Time{})
+ assert.NoError(t, err)
+ assert.Len(t, list, 1)
+ assert.Equal(t, 1, list[0].Findings, "there must be just one finding")
+ })
}
func TestSeriesRepositoryUpdate(t *testing.T) {