Skip to content

Commit 6eb1d4d

Browse files
committed
syz-ci/manager.go: compress programs coverage data
Each fuzzing session costs 2G-13G now. It looks too much. The data is highly redundant (jsonl) thus compression should help.
1 parent 02d129f commit 6eb1d4d

File tree

4 files changed

+214
-53
lines changed

4 files changed

+214
-53
lines changed

pkg/gcs/gcs.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ type Client interface {
3232
FileExists(path string) (bool, error)
3333
ListObjects(path string) ([]*Object, error)
3434

35-
publish(path string) error
35+
Publish(path string) error
3636
}
3737

3838
type UploadOptions struct {
@@ -63,7 +63,7 @@ func UploadFile(ctx context.Context, srcFile io.Reader, destURL string, opts Upl
6363
return fmt.Errorf("gcsWriter.Close: %w", err)
6464
}
6565
if opts.Publish {
66-
return gcsClient.publish(destURL)
66+
return gcsClient.Publish(destURL)
6767
}
6868
return nil
6969
}
@@ -143,8 +143,8 @@ func (c *client) FileWriter(gcsFile, contentType, contentEncoding string) (io.Wr
143143
return w, nil
144144
}
145145

146-
// publish lets any user read gcsFile.
147-
func (c *client) publish(gcsFile string) error {
146+
// Publish lets any user read gcsFile.
147+
func (c *client) Publish(gcsFile string) error {
148148
bucket, filename, err := split(gcsFile)
149149
if err != nil {
150150
return err

pkg/gcs/mocks/Client.go

Lines changed: 18 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

syz-ci/manager.go

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package main
55

66
import (
7+
"compress/gzip"
78
"context"
89
"crypto/sha256"
910
"encoding/json"
@@ -36,6 +37,7 @@ import (
3637
"github.com/google/syzkaller/prog"
3738
_ "github.com/google/syzkaller/sys"
3839
"github.com/google/syzkaller/sys/targets"
40+
"golang.org/x/sync/errgroup"
3941
)
4042

4143
// This is especially slightly longer than syzkaller rebuild period.
@@ -831,15 +833,19 @@ func (mgr *Manager) uploadBuildAssets(buildInfo *dashapi.Build, assetFolder stri
831833
return ret, nil
832834
}
833835

834-
func (mgr *Manager) httpGET(path string) (resp *http.Response, err error) {
836+
func (mgr *Manager) httpGET(ctx context.Context, path string) (resp *http.Response, err error) {
835837
addr := mgr.managercfg.HTTP
836838
if addr != "" && addr[0] == ':' {
837839
addr = "127.0.0.1" + addr // in case addr is ":port"
838840
}
839841
client := &http.Client{
840842
Timeout: time.Hour,
841843
}
842-
return client.Get(fmt.Sprintf("http://%s%s", addr, path))
844+
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("http://%s%s", addr, path), nil)
845+
if err != nil {
846+
return nil, err
847+
}
848+
return client.Do(req)
843849
}
844850

845851
func (mgr *Manager) uploadCoverReport() error {
@@ -859,13 +865,13 @@ func (mgr *Manager) uploadCoverReport() error {
859865
}
860866
defer buildSem.Signal()
861867

862-
resp, err := mgr.httpGET("/cover")
868+
resp, err := mgr.httpGET(context.Background(), "/cover")
863869
if err != nil {
864870
return fmt.Errorf("failed to get report: %w", err)
865871
}
866872
defer resp.Body.Close()
867873
if directUpload {
868-
return uploadFile(mgr.cfg.CoverUploadPath, mgr.name+".html", resp.Body, mgr.cfg.PublishGCS)
874+
return uploadFile(context.Background(), nil, mgr.cfg.CoverUploadPath, mgr.name+".html", resp.Body, mgr.cfg.PublishGCS)
869875
}
870876
// Upload via the asset storage.
871877
newAsset, err := mgr.storage.UploadBuildAsset(resp.Body, mgr.name+".html",
@@ -880,8 +886,8 @@ func (mgr *Manager) uploadCoverReport() error {
880886
return nil
881887
}
882888

883-
func (mgr *Manager) uploadCoverJSONLToGCS(mgrSrc, gcsDest string, curTime time.Time, publish bool,
884-
f func(io.Writer, *json.Decoder) error) error {
889+
func (mgr *Manager) uploadCoverJSONLToGCS(gcsClient gcs.Client, mgrSrc, gcsDest string, curTime time.Time,
890+
publish, compress bool, f func(io.Writer, *json.Decoder) error) error {
885891
if !mgr.managercfg.Cover || gcsDest == "" {
886892
return nil
887893
}
@@ -895,7 +901,8 @@ func (mgr *Manager) uploadCoverJSONLToGCS(mgrSrc, gcsDest string, curTime time.T
895901
}
896902
defer buildSem.Signal()
897903

898-
resp, err := mgr.httpGET(mgrSrc)
904+
eg, egCtx := errgroup.WithContext(context.Background())
905+
resp, err := mgr.httpGET(egCtx, mgrSrc)
899906
if err != nil {
900907
return fmt.Errorf("failed to httpGet %s: %w", mgrSrc, err)
901908
}
@@ -908,36 +915,48 @@ func (mgr *Manager) uploadCoverJSONLToGCS(mgrSrc, gcsDest string, curTime time.T
908915
}
909916

910917
pr, pw := io.Pipe()
911-
defer pr.Close()
912-
go func() {
918+
eg.Go(func() error {
919+
defer pw.Close()
920+
var w io.Writer
921+
w = pw
922+
if compress {
923+
gzw := gzip.NewWriter(pw)
924+
defer gzw.Close()
925+
w = gzw
926+
}
913927
decoder := json.NewDecoder(resp.Body)
914928
for decoder.More() {
915-
if err := f(pw, decoder); err != nil {
916-
pw.CloseWithError(fmt.Errorf("callback: %w", err))
917-
return
929+
if err := f(w, decoder); err != nil {
930+
return fmt.Errorf("callback: %w", err)
918931
}
919932
}
920-
pw.Close()
921-
}()
922-
fileName := fmt.Sprintf("%s/%s-%s-%d-%d.jsonl",
923-
mgr.mgrcfg.DashboardClient,
924-
mgr.name, curTime.Format(time.DateOnly),
925-
curTime.Hour(), curTime.Minute())
926-
if err := uploadFile(gcsDest, fileName, pr, publish); err != nil {
927-
return fmt.Errorf("failed to uploadFileGCS(): %w", err)
928-
}
929-
return nil
933+
return nil
934+
})
935+
eg.Go(func() error {
936+
defer pr.Close()
937+
fileName := fmt.Sprintf("%s/%s-%s-%d-%d.jsonl",
938+
mgr.mgrcfg.DashboardClient,
939+
mgr.name, curTime.Format(time.DateOnly),
940+
curTime.Hour(), curTime.Minute())
941+
if err := uploadFile(egCtx, gcsClient, gcsDest, fileName, pr, publish); err != nil {
942+
return fmt.Errorf("uploadFile: %w", err)
943+
}
944+
return nil
945+
})
946+
return eg.Wait()
930947
}
931948

932949
func (mgr *Manager) uploadCoverStat(fuzzingMinutes int) error {
933950
// Coverage report generation consumes and caches lots of memory.
934951
// In the syz-ci context report generation won't be used after this point,
935952
// so tell manager to flush report generator.
936953
curTime := time.Now()
937-
if err := mgr.uploadCoverJSONLToGCS("/cover?jsonl=1&flush=1",
954+
if err := mgr.uploadCoverJSONLToGCS(nil,
955+
"/cover?jsonl=1&flush=1",
938956
mgr.cfg.CoverPipelinePath,
939957
curTime,
940958
false,
959+
false,
941960
func(w io.Writer, dec *json.Decoder) error {
942961
var covInfo cover.CoverageInfo
943962
if err := dec.Decode(&covInfo); err != nil {
@@ -964,10 +983,12 @@ func (mgr *Manager) uploadCoverStat(fuzzingMinutes int) error {
964983
}
965984

966985
func (mgr *Manager) uploadProgramsWithCoverage() error {
967-
if err := mgr.uploadCoverJSONLToGCS("/coverprogs?jsonl=1",
986+
if err := mgr.uploadCoverJSONLToGCS(nil,
987+
"/coverprogs?jsonl=1",
968988
mgr.cfg.CoverProgramsPath,
969989
time.Now(),
970990
mgr.cfg.PublishGCS,
991+
true,
971992
func(w io.Writer, dec *json.Decoder) error {
972993
var programCoverage cover.ProgramCoverage
973994
if err := dec.Decode(&programCoverage); err != nil {
@@ -994,7 +1015,7 @@ func (mgr *Manager) uploadCorpus() error {
9941015
return err
9951016
}
9961017
defer f.Close()
997-
return uploadFile(mgr.cfg.CorpusUploadPath, mgr.name+"-corpus.db", f, mgr.cfg.PublishGCS)
1018+
return uploadFile(context.Background(), nil, mgr.cfg.CorpusUploadPath, mgr.name+"-corpus.db", f, mgr.cfg.PublishGCS)
9981019
}
9991020

10001021
func (mgr *Manager) uploadBenchData() error {
@@ -1012,15 +1033,15 @@ func (mgr *Manager) uploadBenchData() error {
10121033
return fmt.Errorf("failed to open bench file: %w", err)
10131034
}
10141035
defer f.Close()
1015-
err = uploadFile(mgr.cfg.BenchUploadPath+"/"+mgr.name,
1036+
err = uploadFile(context.Background(), nil, mgr.cfg.BenchUploadPath+"/"+mgr.name,
10161037
mgr.lastRestarted.Format("2006-01-02_15h.json"), f, false)
10171038
if err != nil {
10181039
return fmt.Errorf("failed to upload the bench file: %w", err)
10191040
}
10201041
return nil
10211042
}
10221043

1023-
func uploadFile(dstPath, name string, file io.Reader, publish bool) error {
1044+
func uploadFile(ctx context.Context, gcsClient gcs.Client, dstPath, name string, file io.Reader, publish bool) error {
10241045
URL, err := url.Parse(dstPath)
10251046
if err != nil {
10261047
return fmt.Errorf("failed to parse upload path: %w", err)
@@ -1030,13 +1051,16 @@ func uploadFile(dstPath, name string, file io.Reader, publish bool) error {
10301051
log.Logf(0, "uploading %v to %v", name, URLStr)
10311052
if strings.HasPrefix(URLStr, "http://") ||
10321053
strings.HasPrefix(URLStr, "https://") {
1033-
return uploadFileHTTPPut(URLStr, file)
1054+
if gcsClient != nil {
1055+
return fmt.Errorf("gcsClient is expected to be nil for the http* requests")
1056+
}
1057+
return uploadFileHTTPPut(ctx, URLStr, file)
10341058
}
1035-
return gcs.UploadFile(context.Background(), file, URLStr, gcs.UploadOptions{Publish: publish})
1059+
return gcs.UploadFile(ctx, file, URLStr, gcs.UploadOptions{Publish: publish, GCSClientMock: gcsClient})
10361060
}
10371061

1038-
func uploadFileHTTPPut(URL string, file io.Reader) error {
1039-
req, err := http.NewRequest(http.MethodPut, URL, file)
1062+
func uploadFileHTTPPut(ctx context.Context, URL string, file io.Reader) error {
1063+
req, err := http.NewRequestWithContext(ctx, http.MethodPut, URL, file)
10401064
if err != nil {
10411065
return fmt.Errorf("failed to create HTTP PUT request: %w", err)
10421066
}

0 commit comments

Comments
 (0)