Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Side by Side Diff: golden/go/analysis/analysis.go

Issue 650253003: Added HTTP endpoints for Correctness counts (Closed) Base URL: https://skia.googlesource.com/buildbot@master
Patch Set: Fixed comments Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | golden/go/db/db.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 package analysis 1 package analysis
2 2
3 import ( 3 import (
4 "sync" 4 "sync"
5 "time" 5 "time"
6 6
7 "github.com/golang/glog" 7 "github.com/golang/glog"
8 "github.com/rcrowley/go-metrics" 8 "github.com/rcrowley/go-metrics"
9 9
10 "skia.googlesource.com/buildbot.git/go/util" 10 "skia.googlesource.com/buildbot.git/go/util"
11 "skia.googlesource.com/buildbot.git/golden/go/diff" 11 "skia.googlesource.com/buildbot.git/golden/go/diff"
12 "skia.googlesource.com/buildbot.git/golden/go/expstorage" 12 "skia.googlesource.com/buildbot.git/golden/go/expstorage"
13 "skia.googlesource.com/buildbot.git/golden/go/types" 13 "skia.googlesource.com/buildbot.git/golden/go/types"
14 ptypes "skia.googlesource.com/buildbot.git/perf/go/types" 14 ptypes "skia.googlesource.com/buildbot.git/perf/go/types"
15 ) 15 )
16 16
17 // Stores a Trace with labels and digests in memory. CommitIds, Digests and 17 // LabeledTrace stores a Trace with labels and digests. CommitIds, Digests and
18 // Labels are of the same length, identical indices refer to the same digest. 18 // Labels are of the same length, identical indices refer to the same digest.
19 type LabeledTrace struct { 19 type LabeledTrace struct {
20 » Params map[string]string `json:"params"` 20 » Params map[string]string
21 » CommitIds []int `json:"commitIds"` 21 » CommitIds []int
22 » Digests []string `json:"digests"` 22 » Digests []string
23 » Labels []types.Label `json:"labels` 23 » Labels []types.Label
24 } 24 }
25 25
26 func NewLabeledTrace(params map[string]string, capacity int) *LabeledTrace { 26 func NewLabeledTrace(params map[string]string, capacity int) *LabeledTrace {
27 return &LabeledTrace{ 27 return &LabeledTrace{
28 Params: params, 28 Params: params,
29 CommitIds: make([]int, 0, capacity), 29 CommitIds: make([]int, 0, capacity),
30 Digests: make([]string, 0, capacity), 30 Digests: make([]string, 0, capacity),
31 Labels: make([]types.Label, 0, capacity), 31 Labels: make([]types.Label, 0, capacity),
32 } 32 }
33 } 33 }
34 34
35 // Add the given tripples of commitIds, digests and labels to this LabeledTrace. 35 // addLabledDigests adds the given tripples of commitIds, digests and labels to this LabeledTrace.
36 func (lt *LabeledTrace) addLabeledDigests(commitIds []int, digests []string, lab els []types.Label) { 36 func (lt *LabeledTrace) addLabeledDigests(commitIds []int, digests []string, lab els []types.Label) {
37 lt.CommitIds = append(lt.CommitIds, commitIds...) 37 lt.CommitIds = append(lt.CommitIds, commitIds...)
38 lt.Digests = append(lt.Digests, digests...) 38 lt.Digests = append(lt.Digests, digests...)
39 lt.Labels = append(lt.Labels, labels...) 39 lt.Labels = append(lt.Labels, labels...)
40 } 40 }
41 41
42 // Aggregates the Traces in tile and provides the commits that the 42 // LabeledTile aggregates the traces of a tile and provides a slice of commits
43 // CommitIds in LabeledTrace refer to. 43 // that the commitIds in LabeledTrace refer to.
44 // LabeledTile and LabeledTrace store the cannonical information
45 // extracted from the unterlying tile store. The (redundant) output data is
46 // derived from these.
44 type LabeledTile struct { 47 type LabeledTile struct {
45 » Commits []*ptypes.Commit `json:"commits"` 48 » Commits []*ptypes.Commit
46 49
47 // Traces are indexed by the primary key (test name). This is somewhat 50 // Traces are indexed by the primary key (test name). This is somewhat
48 // redundant, but this also output format. 51 // redundant, but this also output format.
49 » Traces map[string][]*LabeledTrace `json:"traces"` 52 » Traces map[string][]*LabeledTrace
50 } 53 }
51 54
52 func NewLabeledTile() *LabeledTile { 55 func NewLabeledTile() *LabeledTile {
53 return &LabeledTile{ 56 return &LabeledTile{
54 Commits: []*ptypes.Commit{}, 57 Commits: []*ptypes.Commit{},
55 Traces: map[string][]*LabeledTrace{}, 58 Traces: map[string][]*LabeledTrace{},
56 } 59 }
57 } 60 }
58 61
59 // Utility function that returns the testName and a labeled trace for the given 62 // getLabeledTrace is a utility function that returns the testName and a labeled
60 // Trace (read from a TileStore). If the LabeledTrace does not exist it will be 63 // trace for the given trace (read from a TileStore). If the LabeledTrace does
61 // added. 64 // not exist it will be added.
62 func (t *LabeledTile) getLabeledTrace(trace ptypes.Trace) (string, *LabeledTrace ) { 65 func (t *LabeledTile) getLabeledTrace(trace ptypes.Trace) (string, *LabeledTrace ) {
63 params := trace.Params() 66 params := trace.Params()
64 pKey := params[types.PRIMARY_KEY_FIELD] 67 pKey := params[types.PRIMARY_KEY_FIELD]
65 if _, ok := t.Traces[pKey]; !ok { 68 if _, ok := t.Traces[pKey]; !ok {
66 // Add the primary key with a single labled trace. 69 // Add the primary key with a single labled trace.
67 t.Traces[pKey] = []*LabeledTrace{} 70 t.Traces[pKey] = []*LabeledTrace{}
68 } 71 }
69 72
70 // Search through the traces associated witht this test. 73 // Search through the traces associated witht this test.
71 for _, v := range t.Traces[pKey] { 74 for _, v := range t.Traces[pKey] {
72 if util.MapsEqual(v.Params, params) { 75 if util.MapsEqual(v.Params, params) {
73 return pKey, v 76 return pKey, v
74 } 77 }
75 } 78 }
76 79
77 // If we cannot find the trace in our set of tests we are adding a new 80 // If we cannot find the trace in our set of tests we are adding a new
78 // labeled trace. 81 // labeled trace.
79 newLT := NewLabeledTrace(params, trace.Len()) 82 newLT := NewLabeledTrace(params, trace.Len())
80 t.Traces[pKey] = append(t.Traces[pKey], newLT) 83 t.Traces[pKey] = append(t.Traces[pKey], newLT)
81 return pKey, newLT 84 return pKey, newLT
82 } 85 }
83 86
84 // Analyzer continuously manages the tasks, like pollint for new traces 87 // LabelCounts is an output type to hold counts for classification labels.
85 // on disk, etc. 88 type LabelCounts struct {
89 » Unt int `json:"unt"` // Untriaged
90 » Pos int `json:"pos"` // Positive
91 » Neg int `json:"neg"` // Negative
92 }
93
94 // GUITileCounts is an output type for the aggregated label counts.
95 type GUITileCounts struct {
96 » Commits []*ptypes.Commit `json:"commits"`
97 » Counts map[string][]LabelCounts `json:"counts"`
98 }
99
100 // GUITestCounts is an output type for a single test that contains the
101 // aggregated counts over all traces and also the individual traces
102 // and their labels.
103 type GUITestCounts struct {
104 » Commits []*ptypes.Commit `json:"commits"`
105 » Aggregated []LabelCounts `json:"aggregated"`
106 » Traces []*GUILabeledTrace `json:"traces"`
107 }
108
109 // GUILabeledTrace is an output type for the labels of a trace.
110 type GUILabeledTrace struct {
111 » Params map[string]string `json:"params"`
112
113 » // List of commitId and Label pairs.
114 » Labels []IdLabel `json:"labels"`
115 }
116
117 // IdLabel stores the commitId and the label for one entry in a trace.
118 type IdLabel struct {
119 » Id int `json:"id"`
120 » Label int `json:"label"`
121 }
122
123 // Analyzer continuously manages tasks like polling for new traces
124 // on disk and generating diffs between images. It is the primary interface
125 // to be called by the HTTP frontend.
86 type Analyzer struct { 126 type Analyzer struct {
87 expStore expstorage.ExpectationsStore 127 expStore expstorage.ExpectationsStore
88 diffStore diff.DiffStore 128 diffStore diff.DiffStore
89 tileStore ptypes.TileStore 129 tileStore ptypes.TileStore
90 130
131 // Canonical data structure to hold our information about commits, diges ts
132 // and labels.
91 currentTile *LabeledTile 133 currentTile *LabeledTile
92 134
93 » // Lock to protect the expectations and the current labeled tile. 135 » // Output data structures that are derived from currentTile.
136 » currentTileCounts *GUITileCounts
137 » currentTestCounts map[string]*GUITestCounts
138
139 » // Lock to protect the expectations and current* variables.
94 mutex sync.Mutex 140 mutex sync.Mutex
95 } 141 }
96 142
97 func NewAnalyzer(expStore expstorage.ExpectationsStore, tileStore ptypes.TileSto re, diffStore diff.DiffStore, timeBetweenPolls time.Duration) *Analyzer { 143 func NewAnalyzer(expStore expstorage.ExpectationsStore, tileStore ptypes.TileSto re, diffStore diff.DiffStore, timeBetweenPolls time.Duration) *Analyzer {
98 result := &Analyzer{ 144 result := &Analyzer{
99 expStore: expStore, 145 expStore: expStore,
100 diffStore: diffStore, 146 diffStore: diffStore,
101 tileStore: tileStore, 147 tileStore: tileStore,
102 148
103 currentTile: NewLabeledTile(), 149 currentTile: NewLabeledTile(),
104 } 150 }
105 151
106 go result.loop(timeBetweenPolls) 152 go result.loop(timeBetweenPolls)
107 return result 153 return result
108 } 154 }
109 155
110 // Returns an entire Tile which is a collection of 'traces' over a series of 156 // GetTileCounts returns an entire Tile which is a collection of 'traces' over
111 // of commits. Each trace contains the digests and their labels based on 157 // a series of commits. Each trace contains the digests and their labels
112 // out knowledge base about digests (expectations). 158 // based on our knowledge about digests (expectations).
113 func (a *Analyzer) GetLabeledTile() *LabeledTile { 159 func (a *Analyzer) GetTileCounts() (*GUITileCounts, error) {
114 a.mutex.Lock() 160 a.mutex.Lock()
115 defer a.mutex.Unlock() 161 defer a.mutex.Unlock()
116 162
117 » return a.currentTile 163 » return a.currentTileCounts, nil
118 } 164 }
119 165
120 func (a *Analyzer) GetLabeledTraces(testName string) []*LabeledTrace { 166 // GetTestCounts returns the classification counts for a specific tests.
167 func (a *Analyzer) GetTestCounts(testName string) (*GUITestCounts, error) {
121 a.mutex.Lock() 168 a.mutex.Lock()
122 defer a.mutex.Unlock() 169 defer a.mutex.Unlock()
123 170
124 » return a.currentTile.Traces[testName] 171 » // TODO (stephana): This should return any error that occurs during read ing
172 » // of the tiles. We would rather get an error on the front-end than
173 » // look at outdated data.
174 » return a.currentTestCounts[testName], nil
125 } 175 }
126 176
177 // SetDigestLabels sets the labels for the given digest and records the user
178 // that made the classification.
127 func (a *Analyzer) SetDigestLabels(labeledTestDigests map[string]types.TestClass ification, userId string) (map[string][]*LabeledTrace, error) { 179 func (a *Analyzer) SetDigestLabels(labeledTestDigests map[string]types.TestClass ification, userId string) (map[string][]*LabeledTrace, error) {
128 a.mutex.Lock() 180 a.mutex.Lock()
129 defer a.mutex.Unlock() 181 defer a.mutex.Unlock()
130 182
131 expectations, err := a.expStore.Get(true) 183 expectations, err := a.expStore.Get(true)
132 if err != nil { 184 if err != nil {
133 return nil, err 185 return nil, err
134 } 186 }
135 expectations.AddDigests(labeledTestDigests) 187 expectations.AddDigests(labeledTestDigests)
136 if err = a.expStore.Put(expectations, userId); err != nil { 188 if err = a.expStore.Put(expectations, userId); err != nil {
137 return nil, err 189 return nil, err
138 } 190 }
139 191
140 // Let's update our knowledge of the labels. 192 // Let's update our knowledge of the labels.
141 updatedTraces := a.relabelTraces(labeledTestDigests) 193 updatedTraces := a.relabelTraces(labeledTestDigests)
194
142 return updatedTraces, nil 195 return updatedTraces, nil
143 } 196 }
144 197
145 // Main loop. 198 // loop is the main event loop.
146 func (a *Analyzer) loop(timeBetweenPolls time.Duration) { 199 func (a *Analyzer) loop(timeBetweenPolls time.Duration) {
147 // The number of times we've successfully loaded and processed a tile. 200 // The number of times we've successfully loaded and processed a tile.
148 runsCounter := metrics.NewRegisteredCounter("analysis.runs", metrics.Def aultRegistry) 201 runsCounter := metrics.NewRegisteredCounter("analysis.runs", metrics.Def aultRegistry)
149 202
150 // The number of times an error has ocurred when trying to load a tile. 203 // The number of times an error has ocurred when trying to load a tile.
151 errorTileLoadingCounter := metrics.NewRegisteredCounter("analysis.errors ", metrics.DefaultRegistry) 204 errorTileLoadingCounter := metrics.NewRegisteredCounter("analysis.errors ", metrics.DefaultRegistry)
152 205
153 » for { 206 » for _ = range time.Tick(timeBetweenPolls) {
154 glog.Info("Reading tiles ... ") 207 glog.Info("Reading tiles ... ")
155 208
156 // Load the tile and process it. 209 // Load the tile and process it.
157 tile, err := a.tileStore.Get(0, -1) 210 tile, err := a.tileStore.Get(0, -1)
158 if err != nil { 211 if err != nil {
159 glog.Errorf("Error reading tile store: %s\n", err.Error( )) 212 glog.Errorf("Error reading tile store: %s\n", err.Error( ))
160 errorTileLoadingCounter.Inc(1) 213 errorTileLoadingCounter.Inc(1)
161 } else { 214 } else {
162 newLabeledTile := a.processTile(tile) 215 newLabeledTile := a.processTile(tile)
216 newTileCounts, newTestCounts := a.getOutputCounts(newLab eledTile)
217
163 a.mutex.Lock() 218 a.mutex.Lock()
164 a.currentTile = newLabeledTile 219 a.currentTile = newLabeledTile
220 a.currentTileCounts = newTileCounts
221 a.currentTestCounts = newTestCounts
165 a.mutex.Unlock() 222 a.mutex.Unlock()
166 } 223 }
224 glog.Info("Done processing tiles.")
167 runsCounter.Inc(1) 225 runsCounter.Inc(1)
168
169 // Sleep for a while until the next poll.
170 time.Sleep(timeBetweenPolls)
171 } 226 }
172 } 227 }
173 228
174 // Process a tile segment and add it to the currentTile. 229 // processTile processes the last two tiles and updates the cannonical and
230 // output data structures.
175 func (a *Analyzer) processTile(tile *ptypes.Tile) *LabeledTile { 231 func (a *Analyzer) processTile(tile *ptypes.Tile) *LabeledTile {
176 result := NewLabeledTile() 232 result := NewLabeledTile()
177 233
178 tileLen := tile.LastCommitIndex() + 1 234 tileLen := tile.LastCommitIndex() + 1
179 result.Commits = tile.Commits[:tileLen] 235 result.Commits = tile.Commits[:tileLen]
180 236
181 // Note: We are assumming that the number and order of traces will chang e 237 // Note: We are assumming that the number and order of traces will chang e
182 // over time. 238 // over time.
183 for _, v := range tile.Traces { 239 for _, v := range tile.Traces {
184 tempCommitIds := make([]int, 0, tileLen) 240 tempCommitIds := make([]int, 0, tileLen)
(...skipping 16 matching lines...) Expand all
201 if err := a.labelDigests(testName, tempDigests, tempLabels); err != nil { 257 if err := a.labelDigests(testName, tempDigests, tempLabels); err != nil {
202 glog.Errorf("Error labeling digests: %s\n", err.Error()) 258 glog.Errorf("Error labeling digests: %s\n", err.Error())
203 continue 259 continue
204 } 260 }
205 targetLabeledTrace.addLabeledDigests(tempCommitIds, tempDigests, tempLabels) 261 targetLabeledTrace.addLabeledDigests(tempCommitIds, tempDigests, tempLabels)
206 } 262 }
207 263
208 return result 264 return result
209 } 265 }
210 266
211 // Run over the traces in of the tiles that have changed and label them 267 // relabelTraces iterates over the traces in of the tiles that have changed and
212 // according to our current expecatations. 268 // labels them according to our current expecatations.
213 func (a *Analyzer) relabelTraces(labeledTestDigests map[string]types.TestClassif ication) map[string][]*LabeledTrace { 269 func (a *Analyzer) relabelTraces(labeledTestDigests map[string]types.TestClassif ication) map[string][]*LabeledTrace {
214 result := map[string][]*LabeledTrace{} 270 result := map[string][]*LabeledTrace{}
215 271
216 for testName := range labeledTestDigests { 272 for testName := range labeledTestDigests {
217 if traces, ok := a.currentTile.Traces[testName]; ok { 273 if traces, ok := a.currentTile.Traces[testName]; ok {
218 for _, trace := range traces { 274 for _, trace := range traces {
219 // Note: This is potentially slower than using l abels in 275 // Note: This is potentially slower than using l abels in
220 // labeledTestDigests directly, but it keeps the code simpler. 276 // labeledTestDigests directly, but it keeps the code simpler.
221 a.labelDigests(testName, trace.Digests, trace.La bels) 277 a.labelDigests(testName, trace.Digests, trace.La bels)
222 } 278 }
(...skipping 18 matching lines...) Expand all
241 for idx, digest := range digests { 297 for idx, digest := range digests {
242 if test, ok := expectations.Tests[testName]; ok { 298 if test, ok := expectations.Tests[testName]; ok {
243 if foundLabel, ok := test[digest]; ok { 299 if foundLabel, ok := test[digest]; ok {
244 targetLabels[idx] = foundLabel 300 targetLabels[idx] = foundLabel
245 } 301 }
246 } 302 }
247 } 303 }
248 304
249 return nil 305 return nil
250 } 306 }
307
308 // getOutputCounts derives the output counts from the given labeled tile.
309 func (a *Analyzer) getOutputCounts(labeledTile *LabeledTile) (*GUITileCounts, ma p[string]*GUITestCounts) {
310 // Stores the aggregated counts of a tile for each test.
311 tileCountsMap := make(map[string][]LabelCounts, len(labeledTile.Traces))
312
313 // Stores the aggregated counts for each test and individual trace infor mation.
314 testCountsMap := make(map[string]*GUITestCounts, len(labeledTile.Traces) )
315
316 for testName, testTraces := range labeledTile.Traces {
317 acc := make([]LabelCounts, len(labeledTile.Commits))
318 tempTraces := make([]*GUILabeledTrace, 0, len(testTraces))
319
320 for _, oneTrace := range testTraces {
321 tempTrace := &GUILabeledTrace{
322 Params: oneTrace.Params,
323 Labels: make([]IdLabel, len(oneTrace.CommitIds)) ,
324 }
325
326 for i, ci := range oneTrace.CommitIds {
327 switch oneTrace.Labels[i] {
328 case types.UNTRIAGED:
329 acc[ci].Unt++
330 case types.POSITIVE:
331 acc[ci].Pos++
332 case types.NEGATIVE:
333 acc[ci].Neg++
334 }
335 tempTrace.Labels[i].Id = ci
336 tempTrace.Labels[i].Label = int(oneTrace.Labels[ i])
337 }
338
339 tempTraces = append(tempTraces, tempTrace)
340 }
341
342 tileCountsMap[testName] = acc
343 testCountsMap[testName] = &GUITestCounts{
344 Commits: labeledTile.Commits,
345 Aggregated: acc,
346 Traces: tempTraces,
347 }
348 }
349
350 tileCounts := &GUITileCounts{
351 Commits: labeledTile.Commits,
352 Counts: tileCountsMap,
353 }
354
355 return tileCounts, testCountsMap
356 }
OLDNEW
« no previous file with comments | « no previous file | golden/go/db/db.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698