| OLD | NEW |
| 1 package syncer | 1 package syncer |
| 2 | 2 |
| 3 import ( | 3 import ( |
| 4 "fmt" | 4 "fmt" |
| 5 "sort" | 5 "sort" |
| 6 "strings" | 6 "strings" |
| 7 "sync" | 7 "sync" |
| 8 "time" | 8 "time" |
| 9 | 9 |
| 10 "github.com/skia-dev/glog" | 10 "github.com/skia-dev/glog" |
| 11 "go.skia.org/infra/fuzzer/go/common" | 11 "go.skia.org/infra/fuzzer/go/common" |
| 12 "go.skia.org/infra/fuzzer/go/config" | 12 "go.skia.org/infra/fuzzer/go/config" |
| 13 "go.skia.org/infra/fuzzer/go/frontend/gsloader" | 13 "go.skia.org/infra/fuzzer/go/frontend/gsloader" |
| 14 "go.skia.org/infra/go/gs" | 14 "go.skia.org/infra/go/gs" |
| 15 "go.skia.org/infra/go/util" |
| 15 "google.golang.org/cloud/storage" | 16 "google.golang.org/cloud/storage" |
| 16 ) | 17 ) |
| 17 | 18 |
| 18 // FuzzSyncer is a struct that will handle the syncing of bad/grey fuzzes. | 19 // FuzzSyncer is a struct that will handle the syncing of bad/grey fuzzes. |
| 19 // Once started, it will occasionally wake up and download any new fuzzes | 20 // Once started, it will occasionally wake up and download any new fuzzes |
| 20 // Clients should look at LastCount for the last count of fuzzes data. | 21 // Clients should look at LastCount for the last count of fuzzes data. |
| 21 type FuzzSyncer struct { | 22 type FuzzSyncer struct { |
| 22 countMutex sync.Mutex | 23 countMutex sync.Mutex |
| 23 storageClient *storage.Client | 24 storageClient *storage.Client |
| 24 gsLoader *gsloader.GSLoader | 25 gsLoader *gsloader.GSLoader |
| 25 » lastCount map[string]FuzzCount | 26 » lastCount map[string]FuzzCount // maps category->FuzzCount |
| 27 » fuzzNameCache map[string]util.StringSet // maps key->FuzzNames |
| 26 } | 28 } |
| 27 | 29 |
| 28 // FuzzCount is a struct that holds the counts of fuzzes. | 30 // FuzzCount is a struct that holds the counts of fuzzes. |
| 29 type FuzzCount struct { | 31 type FuzzCount struct { |
| 30 TotalBad int `json:"totalBadCount"` | 32 TotalBad int `json:"totalBadCount"` |
| 31 TotalGrey int `json:"totalGreyCount"` | 33 TotalGrey int `json:"totalGreyCount"` |
| 32 // "This" means "newly introduced/fixed in this revision" | 34 // "This" means "newly introduced/fixed in this revision" |
| 33 » ThisBad int `json:"thisBadCount"` | 35 » ThisBad int `json:"thisBadCount"` |
| 34 » ThisGrey int `json:"thisGreyCount"` | 36 » ThisRegression int `json:"thisRegressionCount"` |
| 35 } | 37 } |
| 36 | 38 |
| 37 // NewFuzzSyncer creates a FuzzSyncer and returns it. | 39 // NewFuzzSyncer creates a FuzzSyncer and returns it. |
| 38 func New(s *storage.Client) *FuzzSyncer { | 40 func New(s *storage.Client) *FuzzSyncer { |
| 39 return &FuzzSyncer{ | 41 return &FuzzSyncer{ |
| 40 storageClient: s, | 42 storageClient: s, |
| 41 lastCount: make(map[string]FuzzCount), | 43 lastCount: make(map[string]FuzzCount), |
| 44 fuzzNameCache: make(map[string]util.StringSet), |
| 42 } | 45 } |
| 43 } | 46 } |
| 44 | 47 |
| 45 // SetGSLoader sets this objects GSLoader, allowing it to fetch the new fuzzes i
t finds | 48 // SetGSLoader sets this objects GSLoader, allowing it to fetch the new fuzzes i
t finds |
| 46 // and update the fuzzes displayed to users. | 49 // and update the fuzzes displayed to users. |
| 47 func (f *FuzzSyncer) SetGSLoader(g *gsloader.GSLoader) { | 50 func (f *FuzzSyncer) SetGSLoader(g *gsloader.GSLoader) { |
| 48 f.gsLoader = g | 51 f.gsLoader = g |
| 49 } | 52 } |
| 50 | 53 |
| 51 // Start updates the LastCount and starts a timer with a period of config.FrontE
nd.FuzzSyncPeriod. | 54 // Start updates the LastCount and starts a timer with a period of config.FrontE
nd.FuzzSyncPeriod. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 65 func (f *FuzzSyncer) Refresh() { | 68 func (f *FuzzSyncer) Refresh() { |
| 66 glog.Info("Counting bad and grey fuzzes") | 69 glog.Info("Counting bad and grey fuzzes") |
| 67 currRevision := config.FrontEnd.SkiaVersion.Hash | 70 currRevision := config.FrontEnd.SkiaVersion.Hash |
| 68 prevRevision, err := f.getMostRecentOldRevision() | 71 prevRevision, err := f.getMostRecentOldRevision() |
| 69 if err != nil { | 72 if err != nil { |
| 70 glog.Infof("Problem getting most recent old version: %s", err) | 73 glog.Infof("Problem getting most recent old version: %s", err) |
| 71 return | 74 return |
| 72 } | 75 } |
| 73 f.countMutex.Lock() | 76 f.countMutex.Lock() |
| 74 defer f.countMutex.Unlock() | 77 defer f.countMutex.Unlock() |
| 75 » allCurrentNames := []string{} | 78 » allBadFuzzes := util.NewStringSet() |
| 76 | 79 |
| 77 for _, cat := range common.FUZZ_CATEGORIES { | 80 for _, cat := range common.FUZZ_CATEGORIES { |
| 78 lastCount := FuzzCount{} | 81 lastCount := FuzzCount{} |
| 79 currentBadNames, err := common.GetAllFuzzNamesInFolder(f.storage
Client, fmt.Sprintf("%s/%s/bad", cat, currRevision)) | |
| 80 if err != nil { | |
| 81 glog.Errorf("Problem getting total bad fuzz counts: %s",
err) | |
| 82 } else { | |
| 83 lastCount.TotalBad = len(currentBadNames) | |
| 84 allCurrentNames = append(allCurrentNames, currentBadName
s...) | |
| 85 } | |
| 86 | 82 |
| 87 » » if previousBadNames, err := common.GetAllFuzzNamesInFolder(f.sto
rageClient, fmt.Sprintf("%s/%s/bad", cat, prevRevision)); err != nil { | 83 » » // Previous fuzzes and current grey fuzzes can be drawn from the
cache, if they aren't there. |
| 88 » » » glog.Errorf("Problem getting this bad fuzz counts: %s",
err) | 84 » » previousGreyNames := f.getOrLookUpFuzzNames("grey", cat, prevRev
ision) |
| 89 » » } else { | 85 » » previousBadNames := f.getOrLookUpFuzzNames("bad", cat, prevRevis
ion) |
| 90 » » » lastCount.ThisBad = lastCount.TotalBad - len(previousBad
Names) | 86 » » currentGreyNames := f.getOrLookUpFuzzNames("grey", cat, currRevi
sion) |
| 91 » » } | 87 » » // always fetch current counts |
| 88 » » currentBadNames := f.getFuzzNames("bad", cat, currRevision) |
| 89 » » lastCount.TotalBad = len(currentBadNames) |
| 90 » » lastCount.TotalGrey = len(currentGreyNames) |
| 91 » » lastCount.ThisBad = len(currentBadNames.Complement(previousBadNa
mes).Complement(previousGreyNames)) |
| 92 » » lastCount.ThisRegression = len(previousGreyNames.Intersect(curre
ntBadNames)) |
| 93 » » allBadFuzzes = allBadFuzzes.Union(currentBadNames) |
| 92 | 94 |
| 93 if currentGreyNames, err := common.GetAllFuzzNamesInFolder(f.sto
rageClient, fmt.Sprintf("%s/%s/grey", cat, currRevision)); err != nil { | |
| 94 glog.Errorf("Problem getting total grey fuzz counts: %s"
, err) | |
| 95 } else { | |
| 96 lastCount.TotalGrey = len(currentGreyNames) | |
| 97 } | |
| 98 | |
| 99 if previousGreyNames, err := common.GetAllFuzzNamesInFolder(f.st
orageClient, fmt.Sprintf("%s/%s/grey", cat, prevRevision)); err != nil { | |
| 100 glog.Errorf("Problem getting this grey fuzz counts: %s",
err) | |
| 101 } else { | |
| 102 lastCount.ThisGrey = lastCount.TotalGrey - len(previousG
reyNames) | |
| 103 } | |
| 104 f.lastCount[cat] = lastCount | 95 f.lastCount[cat] = lastCount |
| 105 } | 96 } |
| 106 | 97 |
| 107 » if err = f.updateLoadedBinaryFuzzes(allCurrentNames); err != nil { | 98 » if err = f.updateLoadedBinaryFuzzes(allBadFuzzes.Keys()); err != nil { |
| 108 glog.Errorf("Problem updating loaded binary fuzzes: %s", err) | 99 glog.Errorf("Problem updating loaded binary fuzzes: %s", err) |
| 109 } | 100 } |
| 110 } | 101 } |
| 111 | 102 |
| 112 // getMostRecentOldRevision finds the most recently updated revision used. | 103 // getMostRecentOldRevision finds the most recently updated revision used. |
| 113 // It searches the GS bucket under skia_version/old/ An error is returned if th
ere is one. | 104 // It searches the GS bucket under skia_version/old/ An error is returned if th
ere is one. |
| 114 func (f *FuzzSyncer) getMostRecentOldRevision() (string, error) { | 105 func (f *FuzzSyncer) getMostRecentOldRevision() (string, error) { |
| 115 var newestTime time.Time | 106 var newestTime time.Time |
| 116 newestHash := "" | 107 newestHash := "" |
| 117 findNewest := func(item *storage.ObjectAttrs) { | 108 findNewest := func(item *storage.ObjectAttrs) { |
| 118 glog.Infof("%s: %s", item.Name, item.Updated) | 109 glog.Infof("%s: %s", item.Name, item.Updated) |
| 119 if newestTime.Before(item.Updated) { | 110 if newestTime.Before(item.Updated) { |
| 120 newestTime = item.Updated | 111 newestTime = item.Updated |
| 121 newestHash = item.Name[strings.LastIndex(item.Name, "/")
+1:] | 112 newestHash = item.Name[strings.LastIndex(item.Name, "/")
+1:] |
| 122 } | 113 } |
| 123 } | 114 } |
| 124 if err := gs.AllFilesInDir(f.storageClient, config.GS.Bucket, "skia_vers
ion/old/", findNewest); err != nil { | 115 if err := gs.AllFilesInDir(f.storageClient, config.GS.Bucket, "skia_vers
ion/old/", findNewest); err != nil { |
| 125 return "", err | 116 return "", err |
| 126 } | 117 } |
| 127 glog.Infof("Most recent old version found to be %s", newestHash) | 118 glog.Infof("Most recent old version found to be %s", newestHash) |
| 128 return newestHash, nil | 119 return newestHash, nil |
| 129 } | 120 } |
| 130 | 121 |
| 122 // getOrLookUpFuzzNames first checks the cache for a StringSet of fuzz names lin
ked to a given |
| 123 // tuple of fuzzType (bad or grey), category, revision. If such a thing is not
in the cache, it |
| 124 // fetches it via getFuzzNames() and caches it for next time. |
| 125 func (f *FuzzSyncer) getOrLookUpFuzzNames(fuzzType, category, revision string) u
til.StringSet { |
| 126 key := strings.Join([]string{fuzzType, category, revision}, "|") |
| 127 if s, has := f.fuzzNameCache[key]; has { |
| 128 return s |
| 129 } |
| 130 s := f.getFuzzNames(fuzzType, category, revision) |
| 131 // cache it |
| 132 f.fuzzNameCache[key] = s |
| 133 return s |
| 134 } |
| 135 |
| 136 // getFuzzNames gets all the fuzz names belonging to a fuzzType, category, revis
ion tuple from |
| 137 // Google Storage. It tries two different ways to do this, first by reading a |
| 138 // (bad|grey)_fuzz_names file, which exists in previous revisions. Second, it m
anually counts |
| 139 // all fuzzes in the given GCS folder. |
| 140 func (f *FuzzSyncer) getFuzzNames(fuzzType, category, revision string) util.Stri
ngSet { |
| 141 // The file stored, if it exists, is a pipe seperated list. |
| 142 if names, err := gs.FileContentsFromGS(f.storageClient, config.GS.Bucket
, fmt.Sprintf("%s/%s/%s_fuzz_names.txt", category, revision, fuzzType)); err ==
nil { |
| 143 return util.NewStringSet(strings.Split(string(names), "|")) |
| 144 } else { |
| 145 glog.Infof("Could not find cached names, downloading them the lo
ng way, instead: %s", err) |
| 146 } |
| 147 |
| 148 if names, err := common.GetAllFuzzNamesInFolder(f.storageClient, fmt.Spr
intf("%s/%s/%s", category, revision, fuzzType)); err != nil { |
| 149 glog.Errorf("Problem fetching %s %s fuzzes at revision %s: %s",
fuzzType, category, revision, err) |
| 150 return nil |
| 151 } else { |
| 152 return util.NewStringSet(names) |
| 153 } |
| 154 } |
| 155 |
| 131 // updateLoadedBinaryFuzzes uses gsLoader to download the fuzzes that are curren
tly not | 156 // updateLoadedBinaryFuzzes uses gsLoader to download the fuzzes that are curren
tly not |
| 132 // in the fuzz report tree / cache. | 157 // in the fuzz report tree / cache. |
| 133 func (f *FuzzSyncer) updateLoadedBinaryFuzzes(currentBadFuzzHashes []string) err
or { | 158 func (f *FuzzSyncer) updateLoadedBinaryFuzzes(currentBadFuzzHashes []string) err
or { |
| 134 if f.gsLoader == nil { | 159 if f.gsLoader == nil { |
| 135 glog.Info("Skipping update because the cache hasn't been set yet
") | 160 glog.Info("Skipping update because the cache hasn't been set yet
") |
| 136 return nil | 161 return nil |
| 137 } | 162 } |
| 138 prevBadFuzzNames, err := f.gsLoader.Cache.LoadFuzzNames(config.FrontEnd.
SkiaVersion.Hash) | 163 prevBadFuzzNames, err := f.gsLoader.Cache.LoadFuzzNames(config.FrontEnd.
SkiaVersion.Hash) |
| 139 if err != nil { | 164 if err != nil { |
| 140 return fmt.Errorf("Could not load previous fuzz hashes from cach
e at revision %s: %s", config.FrontEnd.SkiaVersion.Hash, err) | 165 return fmt.Errorf("Could not load previous fuzz hashes from cach
e at revision %s: %s", config.FrontEnd.SkiaVersion.Hash, err) |
| (...skipping 12 matching lines...) Expand all Loading... |
| 153 glog.Infof("%d newly found fuzzes from Google Storage. Going to load th
em.", len(newBinaryFuzzNames)) | 178 glog.Infof("%d newly found fuzzes from Google Storage. Going to load th
em.", len(newBinaryFuzzNames)) |
| 154 if len(newBinaryFuzzNames) > 0 { | 179 if len(newBinaryFuzzNames) > 0 { |
| 155 return f.gsLoader.LoadFuzzesFromGoogleStorage(newBinaryFuzzNames
) | 180 return f.gsLoader.LoadFuzzesFromGoogleStorage(newBinaryFuzzNames
) |
| 156 } | 181 } |
| 157 return nil | 182 return nil |
| 158 } | 183 } |
| 159 | 184 |
| 160 func (f *FuzzSyncer) LastCount(category string) FuzzCount { | 185 func (f *FuzzSyncer) LastCount(category string) FuzzCount { |
| 161 return f.lastCount[category] | 186 return f.lastCount[category] |
| 162 } | 187 } |
| OLD | NEW |