aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2025-04-09 14:30:54 +0200
committerAleksandr Nogikh <nogikh@google.com>2025-04-11 13:02:25 +0000
commit0bd6db418098e2d98a2edf948b41410d3d9f9e70 (patch)
tree65a87774f63eadb75036b75c5294db051943de42
parent27ba3dade815757a586b8f5a994ff675786ca212 (diff)
syz-cluster: share and display triage logs
For now, only share it for the skipped series.
-rw-r--r--syz-cluster/dashboard/handler.go12
-rw-r--r--syz-cluster/dashboard/templates/series.html7
-rw-r--r--syz-cluster/pkg/api/api.go3
-rw-r--r--syz-cluster/pkg/db/entities.go17
-rw-r--r--syz-cluster/pkg/db/migrations/1_initialize.up.sql1
-rw-r--r--syz-cluster/pkg/service/session.go14
-rw-r--r--syz-cluster/pkg/triage/tree.go1
-rw-r--r--syz-cluster/workflow/triage-step/main.go8
8 files changed, 50 insertions, 13 deletions
diff --git a/syz-cluster/dashboard/handler.go b/syz-cluster/dashboard/handler.go
index c18436978..c17921351 100644
--- a/syz-cluster/dashboard/handler.go
+++ b/syz-cluster/dashboard/handler.go
@@ -57,6 +57,7 @@ var staticFs embed.FS
func (h *dashboardHandler) Mux() *http.ServeMux {
mux := http.NewServeMux()
mux.HandleFunc("/sessions/{id}/log", errToStatus(h.sessionLog))
+ mux.HandleFunc("/sessions/{id}/triage_log", errToStatus(h.sessionTriageLog))
mux.HandleFunc("/sessions/{id}/test_logs", errToStatus(h.sessionTestLog))
mux.HandleFunc("/sessions/{id}/test_artifacts", errToStatus(h.sessionTestArtifacts))
mux.HandleFunc("/series/{id}", errToStatus(h.seriesInfo))
@@ -223,6 +224,17 @@ func (h *dashboardHandler) sessionLog(w http.ResponseWriter, r *http.Request) er
}
// nolint:dupl
+func (h *dashboardHandler) sessionTriageLog(w http.ResponseWriter, r *http.Request) error {
+ session, err := h.sessionRepo.GetByID(r.Context(), r.PathValue("id"))
+ if err != nil {
+ return err
+ } else if session == nil {
+ return fmt.Errorf("%w: session", errNotFound)
+ }
+ return h.streamBlob(w, session.TriageLogURI)
+}
+
+// nolint:dupl
func (h *dashboardHandler) patchContent(w http.ResponseWriter, r *http.Request) error {
patch, err := h.seriesRepo.PatchByID(r.Context(), r.PathValue("id"))
if err != nil {
diff --git a/syz-cluster/dashboard/templates/series.html b/syz-cluster/dashboard/templates/series.html
index 754bfe9e2..9a71e0053 100644
--- a/syz-cluster/dashboard/templates/series.html
+++ b/syz-cluster/dashboard/templates/series.html
@@ -99,7 +99,12 @@
{{if not .SkipReason.IsNull}}
<tr>
<th>Skipped</th>
- <td>{{.SkipReason.StringVal}}</td>
+ <td>
+ {{.SkipReason.StringVal}}
+ {{if .TriageLogURI}}
+ <a href="/sessions/{{.ID}}/triage_log">[Log]</a>
+ {{end}}
+ </td>
</tr>
{{end}}
{{if .LogURI}}
diff --git a/syz-cluster/pkg/api/api.go b/syz-cluster/pkg/api/api.go
index 1644b0ff0..33da0e5ff 100644
--- a/syz-cluster/pkg/api/api.go
+++ b/syz-cluster/pkg/api/api.go
@@ -13,7 +13,8 @@ type TriageResult struct {
}
type SkipRequest struct {
- Reason string `json:"reason"`
+ Reason string `json:"reason"`
+ TriageLog []byte `json:"log"`
}
// The data layout faclitates the simplicity of the workflow definition.
diff --git a/syz-cluster/pkg/db/entities.go b/syz-cluster/pkg/db/entities.go
index 688bb8971..203ab32c9 100644
--- a/syz-cluster/pkg/db/entities.go
+++ b/syz-cluster/pkg/db/entities.go
@@ -60,14 +60,15 @@ const (
)
type Session struct {
- ID string `spanner:"ID"`
- SeriesID string `spanner:"SeriesID"`
- CreatedAt time.Time `spanner:"CreatedAt"`
- StartedAt spanner.NullTime `spanner:"StartedAt"`
- FinishedAt spanner.NullTime `spanner:"FinishedAt"`
- SkipReason spanner.NullString `spanner:"SkipReason"`
- LogURI string `spanner:"LogURI"`
- Tags []string `spanner:"Tags"`
+ ID string `spanner:"ID"`
+ SeriesID string `spanner:"SeriesID"`
+ CreatedAt time.Time `spanner:"CreatedAt"`
+ StartedAt spanner.NullTime `spanner:"StartedAt"`
+ FinishedAt spanner.NullTime `spanner:"FinishedAt"`
+ SkipReason spanner.NullString `spanner:"SkipReason"`
+ LogURI string `spanner:"LogURI"`
+ TriageLogURI string `spanner:"TriageLogURI"`
+ Tags []string `spanner:"Tags"`
// TODO: to accept more specific fuzzing assignment,
// add Triager, BaseRepo, BaseCommit, Config fields.
}
diff --git a/syz-cluster/pkg/db/migrations/1_initialize.up.sql b/syz-cluster/pkg/db/migrations/1_initialize.up.sql
index c8938ca53..9935774c4 100644
--- a/syz-cluster/pkg/db/migrations/1_initialize.up.sql
+++ b/syz-cluster/pkg/db/migrations/1_initialize.up.sql
@@ -56,6 +56,7 @@ CREATE TABLE Sessions (
FinishedAt TIMESTAMP,
SkipReason STRING(1024),
LogURI STRING(512) NOT NULL,
+ TriageLogURI STRING(512) NOT NULL,
Tags ARRAY<STRING(256)>,
CONSTRAINT FK_SeriesSessions FOREIGN KEY (SeriesID) REFERENCES Series (ID),
) PRIMARY KEY(ID);
diff --git a/syz-cluster/pkg/service/session.go b/syz-cluster/pkg/service/session.go
index 23f1a8c17..9ad0688fd 100644
--- a/syz-cluster/pkg/service/session.go
+++ b/syz-cluster/pkg/service/session.go
@@ -4,31 +4,45 @@
package service
import (
+ "bytes"
"context"
"errors"
+ "fmt"
"time"
"github.com/google/syzkaller/syz-cluster/pkg/api"
"github.com/google/syzkaller/syz-cluster/pkg/app"
+ "github.com/google/syzkaller/syz-cluster/pkg/blob"
"github.com/google/syzkaller/syz-cluster/pkg/db"
)
type SessionService struct {
sessionRepo *db.SessionRepository
seriesRepo *db.SeriesRepository
+ blobStorage blob.Storage
}
func NewSessionService(env *app.AppEnvironment) *SessionService {
return &SessionService{
sessionRepo: db.NewSessionRepository(env.Spanner),
seriesRepo: db.NewSeriesRepository(env.Spanner),
+ blobStorage: env.BlobStorage,
}
}
var ErrSessionNotFound = errors.New("session not found")
func (s *SessionService) SkipSession(ctx context.Context, sessionID string, skip *api.SkipRequest) error {
+ var triageLogURI string
+ if len(skip.TriageLog) > 0 {
+ var err error
+ triageLogURI, err = s.blobStorage.Store(bytes.NewReader(skip.TriageLog))
+ if err != nil {
+ return fmt.Errorf("failed to save the log: %w", err)
+ }
+ }
err := s.sessionRepo.Update(ctx, sessionID, func(session *db.Session) error {
+ session.TriageLogURI = triageLogURI
session.SetSkipReason(skip.Reason)
return nil
})
diff --git a/syz-cluster/pkg/triage/tree.go b/syz-cluster/pkg/triage/tree.go
index 592bbce78..61e8935f4 100644
--- a/syz-cluster/pkg/triage/tree.go
+++ b/syz-cluster/pkg/triage/tree.go
@@ -9,7 +9,6 @@ import (
"github.com/google/syzkaller/syz-cluster/pkg/api"
)
-// TODO: add tests.
func SelectTree(series *api.Series, trees []*api.Tree) *api.Tree {
seriesCc := map[string]bool{}
for _, cc := range series.Cc {
diff --git a/syz-cluster/workflow/triage-step/main.go b/syz-cluster/workflow/triage-step/main.go
index 14bae39f5..d61fc4947 100644
--- a/syz-cluster/workflow/triage-step/main.go
+++ b/syz-cluster/workflow/triage-step/main.go
@@ -4,9 +4,11 @@
package main
import (
+ "bytes"
"context"
"flag"
"fmt"
+ "io"
"os"
"github.com/google/syzkaller/pkg/debugtracer"
@@ -82,8 +84,9 @@ func getVerdict(ctx context.Context, client *api.Client, ops triage.TreeOps) (*a
// TODO: the workflow step must be retried.
return nil, fmt.Errorf("failed to query the last build: %w", err)
}
+ var buf bytes.Buffer
selector := triage.NewCommitSelector(ops, &debugtracer.GenericTracer{
- TraceWriter: os.Stderr,
+ TraceWriter: io.MultiWriter(os.Stderr, &buf),
})
result, err := selector.Select(series, tree, lastBuild)
if err != nil {
@@ -92,7 +95,8 @@ func getVerdict(ctx context.Context, client *api.Client, ops triage.TreeOps) (*a
} else if result.Commit == "" {
return &api.TriageResult{
Skip: &api.SkipRequest{
- Reason: "failed to find the base commit: " + result.Reason,
+ Reason: "failed to find the base commit: " + result.Reason,
+ TriageLog: buf.Bytes(),
},
}, nil
}