OLD | NEW |
(Empty) | |
| 1 package storage |
| 2 |
| 3 import ( |
| 4 "fmt" |
| 5 "sort" |
| 6 "sync" |
| 7 "sync/atomic" |
| 8 |
| 9 "github.com/skia-dev/glog" |
| 10 "go.skia.org/infra/fuzzer/go/common" |
| 11 "go.skia.org/infra/fuzzer/go/config" |
| 12 "go.skia.org/infra/fuzzer/go/data" |
| 13 "go.skia.org/infra/go/gs" |
| 14 "google.golang.org/cloud/storage" |
| 15 ) |
| 16 |
| 17 // GetReportsFromGS fetches all fuzz reports in the baseFolder from Google Stora
ge. It returns a |
| 18 // channel through which all reports will be sent. The channel will be closed wh
en finished. An |
| 19 // optional whitelist can be included, in which case only the fuzzes whose names
are on the list |
| 20 // will be downloaded. The category is needed to properly parse the downloaded
files to make |
| 21 // the FuzzReports. The downloading will use as many processes as specified, to
speed things up. |
| 22 func GetReportsFromGS(s *storage.Client, baseFolder, category string, whitelist
[]string, processes int) (<-chan data.FuzzReport, error) { |
| 23 reports := make(chan data.FuzzReport, 10000) |
| 24 |
| 25 fuzzPackages, err := fetchFuzzPackages(s, baseFolder, category) |
| 26 if err != nil { |
| 27 close(reports) |
| 28 return reports, err |
| 29 } |
| 30 |
| 31 toDownload := make(chan fuzzPackage, len(fuzzPackages)) |
| 32 completedCounter := int32(0) |
| 33 |
| 34 var wg sync.WaitGroup |
| 35 for i := 0; i < processes; i++ { |
| 36 wg.Add(1) |
| 37 go download(s, toDownload, reports, &completedCounter, &wg) |
| 38 } |
| 39 |
| 40 for _, d := range fuzzPackages { |
| 41 if whitelist != nil { |
| 42 name := d.FuzzName |
| 43 if i := sort.SearchStrings(whitelist, name); i < len(whi
telist) && whitelist[i] == name { |
| 44 // is on the whitelist |
| 45 toDownload <- d |
| 46 } |
| 47 } else { |
| 48 // no white list |
| 49 toDownload <- d |
| 50 } |
| 51 } |
| 52 close(toDownload) |
| 53 |
| 54 // Wait until all are done downloading to close the reports channel, but
don't block |
| 55 go func() { |
| 56 wg.Wait() |
| 57 close(reports) |
| 58 }() |
| 59 |
| 60 return reports, nil |
| 61 } |
| 62 |
| 63 // A fuzzPackage contains all the information about a fuzz, mostly the paths to
the files that |
| 64 // need to be downloaded. The use of this struct decouples the names of the fil
es that need to be |
| 65 // downloaded with the download logic. |
| 66 type fuzzPackage struct { |
| 67 FuzzName string |
| 68 FuzzCategory string |
| 69 DebugASANName string |
| 70 DebugDumpName string |
| 71 DebugErrName string |
| 72 ReleaseASANName string |
| 73 ReleaseDumpName string |
| 74 ReleaseErrName string |
| 75 } |
| 76 |
| 77 // fetchFuzzPackages scans for all fuzzes in the given folder and returns a slic
e of all of the |
| 78 // metadata for each fuzz, as a fuzz package. It returns error if it cannot acc
ess Google Storage. |
| 79 func fetchFuzzPackages(s *storage.Client, baseFolder, category string) (fuzzPack
ages []fuzzPackage, err error) { |
| 80 fuzzNames, err := common.GetAllFuzzNamesInFolder(s, baseFolder) |
| 81 if err != nil { |
| 82 return nil, fmt.Errorf("Problem getting fuzz packages from %s: %
s", baseFolder, err) |
| 83 } |
| 84 for _, fuzzName := range fuzzNames { |
| 85 prefix := fmt.Sprintf("%s/%s/%s", baseFolder, fuzzName, fuzzName
) |
| 86 fuzzPackages = append(fuzzPackages, fuzzPackage{ |
| 87 FuzzName: fuzzName, |
| 88 FuzzCategory: category, |
| 89 DebugASANName: fmt.Sprintf("%s_debug.asan", prefix), |
| 90 DebugDumpName: fmt.Sprintf("%s_debug.dump", prefix), |
| 91 DebugErrName: fmt.Sprintf("%s_debug.err", prefix), |
| 92 ReleaseASANName: fmt.Sprintf("%s_release.asan", prefix), |
| 93 ReleaseDumpName: fmt.Sprintf("%s_release.dump", prefix), |
| 94 ReleaseErrName: fmt.Sprintf("%s_release.err", prefix), |
| 95 }) |
| 96 } |
| 97 return fuzzPackages, nil |
| 98 } |
| 99 |
| 100 // emptyStringOnError returns a string of the passed in bytes or empty string if
err is nil. |
| 101 func emptyStringOnError(b []byte, err error) string { |
| 102 if err != nil { |
| 103 glog.Warningf("Ignoring error when fetching file contents: %v",
err) |
| 104 return "" |
| 105 } |
| 106 return string(b) |
| 107 } |
| 108 |
| 109 // download waits for fuzzPackages to appear on the toDownload channel and then
downloads |
| 110 // the four pieces of the package. It then parses them into a BinaryFuzzReport
and sends |
| 111 // the binary to the passed in channel. When there is no more work to be done,
this function. |
| 112 // returns and writes out true to the done channel. |
| 113 func download(s *storage.Client, toDownload <-chan fuzzPackage, reports chan<- d
ata.FuzzReport, completedCounter *int32, wg *sync.WaitGroup) { |
| 114 defer wg.Done() |
| 115 for job := range toDownload { |
| 116 p := data.GCSPackage{ |
| 117 Name: job.FuzzName, |
| 118 FuzzCategory: job.FuzzCategory, |
| 119 Debug: data.OutputFiles{ |
| 120 Asan: emptyStringOnError(gs.FileContentsFromGS
(s, config.GS.Bucket, job.DebugASANName)), |
| 121 Dump: emptyStringOnError(gs.FileContentsFromGS
(s, config.GS.Bucket, job.DebugDumpName)), |
| 122 StdErr: emptyStringOnError(gs.FileContentsFromGS
(s, config.GS.Bucket, job.DebugErrName)), |
| 123 }, |
| 124 Release: data.OutputFiles{ |
| 125 Asan: emptyStringOnError(gs.FileContentsFromGS
(s, config.GS.Bucket, job.ReleaseASANName)), |
| 126 Dump: emptyStringOnError(gs.FileContentsFromGS
(s, config.GS.Bucket, job.ReleaseDumpName)), |
| 127 StdErr: emptyStringOnError(gs.FileContentsFromGS
(s, config.GS.Bucket, job.ReleaseErrName)), |
| 128 }, |
| 129 } |
| 130 |
| 131 reports <- data.ParseReport(p) |
| 132 atomic.AddInt32(completedCounter, 1) |
| 133 if *completedCounter%100 == 0 { |
| 134 glog.Infof("%d fuzzes downloaded", *completedCounter) |
| 135 } |
| 136 } |
| 137 } |
OLD | NEW |