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

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

Issue 884943003: Ignore traces (Closed) Base URL: https://skia.googlesource.com/buildbot@master
Patch Set: Created 5 years, 10 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
OLDNEW
1 package analysis 1 package analysis
2 2
3 import ( 3 import (
4 "fmt" 4 "fmt"
5 "net/url" 5 "net/url"
6 "sort" 6 "sort"
7 "sync" 7 "sync"
8 "time" 8 "time"
9 9
10 » metrics "github.com/rcrowley/go-metrics" 10 » "github.com/rcrowley/go-metrics"
11 "github.com/skia-dev/glog" 11 "github.com/skia-dev/glog"
12 12
13 "skia.googlesource.com/buildbot.git/go/util" 13 "skia.googlesource.com/buildbot.git/go/util"
14 "skia.googlesource.com/buildbot.git/golden/go/diff" 14 "skia.googlesource.com/buildbot.git/golden/go/diff"
15 "skia.googlesource.com/buildbot.git/golden/go/expstorage" 15 "skia.googlesource.com/buildbot.git/golden/go/expstorage"
16 "skia.googlesource.com/buildbot.git/golden/go/types" 16 "skia.googlesource.com/buildbot.git/golden/go/types"
17 "skia.googlesource.com/buildbot.git/perf/go/human" 17 "skia.googlesource.com/buildbot.git/perf/go/human"
18 ptypes "skia.googlesource.com/buildbot.git/perf/go/types" 18 ptypes "skia.googlesource.com/buildbot.git/perf/go/types"
19 ) 19 )
20 20
21 var (
22 // The number of times we've successfully loaded and processed a tile.
23 runsCounter metrics.Counter
24
25 // The number of times an error has ocurred when trying to load a tile.
26 errorTileLoadingCounter metrics.Counter
27 )
28
29 func init() {
30 runsCounter = metrics.NewRegisteredCounter("analysis.runs", metrics.Defa ultRegistry)
31 errorTileLoadingCounter = metrics.NewRegisteredCounter("analysis.errors" , metrics.DefaultRegistry)
32 }
33
21 type PathToURLConverter func(string) string 34 type PathToURLConverter func(string) string
22 35
23 // LabeledTrace stores a Trace with labels and digests. CommitIds, Digests and 36 // LabeledTrace stores a Trace with labels and digests. CommitIds, Digests and
24 // Labels are of the same length, identical indices refer to the same digest. 37 // Labels are of the same length, identical indices refer to the same digest.
25 type LabeledTrace struct { 38 type LabeledTrace struct {
26 » Params map[string]string 39 » Params map[string]string
27 » CommitIds []int 40 » CommitIds []int
28 » Digests []string 41 » Digests []string
29 » Labels []types.Label 42 » Labels []types.Label
30 » Id int 43 » Id int
44 » IgnoreRules []*types.IgnoreRule
jcgregorio 2015/01/30 17:37:28 This doesn't look like it's used anywhere.
stephana 2015/01/30 20:27:45 It's used below by addIgnoreRules. It's not used i
31 } 45 }
32 46
33 func NewLabeledTrace(params map[string]string, capacity int, traceId int) *Label edTrace { 47 func NewLabeledTrace(params map[string]string, capacity int, traceId int) *Label edTrace {
34 return &LabeledTrace{ 48 return &LabeledTrace{
35 » » Params: params, 49 » » Params: params,
36 » » CommitIds: make([]int, 0, capacity), 50 » » CommitIds: make([]int, 0, capacity),
37 » » Digests: make([]string, 0, capacity), 51 » » Digests: make([]string, 0, capacity),
38 » » Labels: make([]types.Label, 0, capacity), 52 » » Labels: make([]types.Label, 0, capacity),
39 » » Id: traceId, 53 » » Id: traceId,
54 » » IgnoreRules: []*types.IgnoreRule{},
40 } 55 }
41 } 56 }
42 57
43 // addLabledDigests adds the given tripples of commitIds, digests and labels to this LabeledTrace. 58 // addLabledDigests adds the given tripples of commitIds, digests and labels to this LabeledTrace.
44 func (lt *LabeledTrace) addLabeledDigests(commitIds []int, digests []string, lab els []types.Label) { 59 func (lt *LabeledTrace) addLabeledDigests(commitIds []int, digests []string, lab els []types.Label) {
45 lt.CommitIds = append(lt.CommitIds, commitIds...) 60 lt.CommitIds = append(lt.CommitIds, commitIds...)
46 lt.Digests = append(lt.Digests, digests...) 61 lt.Digests = append(lt.Digests, digests...)
47 lt.Labels = append(lt.Labels, labels...) 62 lt.Labels = append(lt.Labels, labels...)
48 } 63 }
49 64
65 // addIgnoreRules attaches the ignore rules that match his trace.
66 func (lt *LabeledTrace) addIgnoreRules(newRules []*types.IgnoreRule) {
67 lt.IgnoreRules = append(lt.IgnoreRules, newRules...)
68 }
69
50 // LabeledTile aggregates the traces of a tile and provides a slice of commits 70 // LabeledTile aggregates the traces of a tile and provides a slice of commits
51 // that the commitIds in LabeledTrace refer to. 71 // that the commitIds in LabeledTrace refer to.
52 // LabeledTile and LabeledTrace store the cannonical information 72 // LabeledTile and LabeledTrace store the cannonical information
53 // extracted from the unterlying tile store. The (redundant) output data is 73 // extracted from the unterlying tile store. The (redundant) output data is
54 // derived from these. 74 // derived from these.
55 type LabeledTile struct { 75 type LabeledTile struct {
56 Commits []*ptypes.Commit 76 Commits []*ptypes.Commit
57 77
58 // Traces are indexed by the primary key (test name). This is somewhat 78 // Traces are indexed by the primary key (test name). This is somewhat
59 // redundant, but this also output format. 79 // redundant, but this also output format.
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 // GUITileCounts is an output type for the aggregated label counts. 140 // GUITileCounts is an output type for the aggregated label counts.
121 type GUITileCounts struct { 141 type GUITileCounts struct {
122 Commits []*ptypes.Commit `json:"commits"` 142 Commits []*ptypes.Commit `json:"commits"`
123 Ticks []interface{} `json:"ticks"` 143 Ticks []interface{} `json:"ticks"`
124 Aggregated *LabelCounts `json:"aggregated"` 144 Aggregated *LabelCounts `json:"aggregated"`
125 Counts map[string]*LabelCounts `json:"counts"` 145 Counts map[string]*LabelCounts `json:"counts"`
126 AllParams map[string][]string `json:"allParams"` 146 AllParams map[string][]string `json:"allParams"`
127 Query map[string][]string `json:"query"` 147 Query map[string][]string `json:"query"`
128 } 148 }
129 149
150 // AnalyzeState captures the state of a partition of the incoming data.
151 // When a tile is read from disk it is partitioned into two tiles: current
152 // and ignored. current contains everything we want to be able to review
153 // continuously and ignored contains all ignored traces.
154 // This struct is the container for one of these partitions and the derived
155 // information.
156 type AnalyzeState struct {
157 // Canonical data structure to hold our information about commits, diges ts
158 // and labels.
159 Tile *LabeledTile
160
161 // Index to query the Tile.
162 Index *LabeledTileIndex
163
164 // Output data structures that are derived from Tile.
165 TileCounts *GUITileCounts
166 TestDetails *GUITestDetails
167 Status *GUIStatus
168 }
169
130 // Analyzer continuously manages tasks like polling for new traces 170 // Analyzer continuously manages tasks like polling for new traces
131 // on disk and generating diffs between images. It is the primary interface 171 // on disk and generating diffs between images. It is the primary interface
132 // to be called by the HTTP frontend. 172 // to be called by the HTTP frontend.
133 type Analyzer struct { 173 type Analyzer struct {
134 » expStore expstorage.ExpectationsStore 174 » expStore expstorage.ExpectationsStore
135 » diffStore diff.DiffStore 175 » diffStore diff.DiffStore
136 » tileStore ptypes.TileStore 176 » tileStore ptypes.TileStore
177 » ignoreStore types.IgnoreStore
137 178
138 » // Canonical data structure to hold our information about commits, diges ts 179 » current *AnalyzeState
139 » // and labels. 180 » ignored *AnalyzeState
140 » currentTile *LabeledTile
141
142 » // Index to query the current tile.
143 » currentIndex *LabeledTileIndex
144
145 » // Output data structures that are derived from currentTile.
146 » currentTileCounts *GUITileCounts
147 » currentTestDetails *GUITestDetails
148 » currentStatus *GUIStatus
149
150 » // Maps from trace ids to the actual instances of LabeledTrace.
151 » traceMap map[int]*LabeledTrace
152 181
153 // converter supplied by the client of the type to convert a path to a U RL 182 // converter supplied by the client of the type to convert a path to a U RL
154 pathToURLConverter PathToURLConverter 183 pathToURLConverter PathToURLConverter
155 184
156 // Lock to protect the expectations and current* variables. 185 // Lock to protect the expectations and current* variables.
157 mutex sync.RWMutex 186 mutex sync.RWMutex
158 187
159 // Counts the number of times the main event loop has executed. 188 // Counts the number of times the main event loop has executed.
160 // This is for testing only. 189 // This is for testing only.
161 loopCounter int 190 loopCounter int
162 } 191 }
163 192
164 func NewAnalyzer(expStore expstorage.ExpectationsStore, tileStore ptypes.TileSto re, diffStore diff.DiffStore, puConverter PathToURLConverter, timeBetweenPolls t ime.Duration) *Analyzer { 193 func NewAnalyzer(expStore expstorage.ExpectationsStore, tileStore ptypes.TileSto re, diffStore diff.DiffStore, ignoreStore types.IgnoreStore, puConverter PathToU RLConverter, timeBetweenPolls time.Duration) *Analyzer {
165 result := &Analyzer{ 194 result := &Analyzer{
166 expStore: expStore, 195 expStore: expStore,
167 diffStore: diffStore, 196 diffStore: diffStore,
168 tileStore: tileStore, 197 tileStore: tileStore,
198 ignoreStore: ignoreStore,
169 pathToURLConverter: puConverter, 199 pathToURLConverter: puConverter,
170 200
171 » » currentTile: NewLabeledTile(), 201 » » current: &AnalyzeState{},
202 » » ignored: &AnalyzeState{},
172 } 203 }
173 204
174 go result.loop(timeBetweenPolls) 205 go result.loop(timeBetweenPolls)
175 return result 206 return result
176 } 207 }
177 208
178 // GetTileCounts returns an entire Tile which is a collection of 'traces' over 209 // GetTileCounts returns an entire Tile which is a collection of 'traces' over
179 // a series of commits. Each trace contains the digests and their labels 210 // a series of commits. Each trace contains the digests and their labels
180 // based on our knowledge about digests (expectations). 211 // based on our knowledge about digests (expectations).
181 func (a *Analyzer) GetTileCounts(query map[string][]string) (*GUITileCounts, err or) { 212 func (a *Analyzer) GetTileCounts(query map[string][]string) (*GUITileCounts, err or) {
182 a.mutex.RLock() 213 a.mutex.RLock()
183 defer a.mutex.RUnlock() 214 defer a.mutex.RUnlock()
184 215
185 if len(query) > 0 { 216 if len(query) > 0 {
186 tile, effectiveQuery := a.getSubTile(query) 217 tile, effectiveQuery := a.getSubTile(query)
187 if len(effectiveQuery) > 0 { 218 if len(effectiveQuery) > 0 {
188 » » » ret := a.getOutputCounts(tile) 219 » » » ret := a.getOutputCounts(tile, a.current.Index)
189 ret.Query = effectiveQuery 220 ret.Query = effectiveQuery
190 return ret, nil 221 return ret, nil
191 } 222 }
192 } 223 }
193 224
194 » return a.currentTileCounts, nil 225 » return a.current.TileCounts, nil
195 } 226 }
196 227
197 // ListTestDetails returns a list of triage details based on the supplied 228 // ListTestDetails returns a list of triage details based on the supplied
198 // query. It's complementary to GetTestDetails which returns a single test 229 // query. It's complementary to GetTestDetails which returns a single test
199 // detail. 230 // detail.
200 // TODO(stephana): This should provide pagination since the list is potentially 231 // TODO(stephana): This should provide pagination since the list is potentially
201 // very long. If we don't add pagination, this should be merged with 232 // very long. If we don't add pagination, this should be merged with
202 // GetTestDetail. 233 // GetTestDetail.
203 func (a *Analyzer) ListTestDetails(query map[string][]string) (*GUITestDetails, error) { 234 func (a *Analyzer) ListTestDetails(query map[string][]string) (*GUITestDetails, error) {
204 a.mutex.RLock() 235 a.mutex.RLock()
205 defer a.mutex.RUnlock() 236 defer a.mutex.RUnlock()
206 237
207 if len(query) == 0 { 238 if len(query) == 0 {
208 » » return a.currentTestDetails, nil 239 » » return a.current.TestDetails, nil
209 } 240 }
210 241
211 effectiveQuery := make(map[string][]string, len(query)) 242 effectiveQuery := make(map[string][]string, len(query))
212 foundUntriaged := a.getUntriagedTestDetails(query, effectiveQuery, true) 243 foundUntriaged := a.getUntriagedTestDetails(query, effectiveQuery, true)
213 tests := make([]*GUITestDetail, 0, len(foundUntriaged)) 244 tests := make([]*GUITestDetail, 0, len(foundUntriaged))
214 245
215 for testName, untriaged := range foundUntriaged { 246 for testName, untriaged := range foundUntriaged {
216 » » testDetail := a.currentTestDetails.lookup(testName) 247 » » testDetail := a.current.TestDetails.lookup(testName)
217 tests = append(tests, &GUITestDetail{ 248 tests = append(tests, &GUITestDetail{
218 Name: testName, 249 Name: testName,
219 Untriaged: untriaged, 250 Untriaged: untriaged,
220 Positive: testDetail.Positive, 251 Positive: testDetail.Positive,
221 Negative: testDetail.Negative, 252 Negative: testDetail.Negative,
222 }) 253 })
223 } 254 }
224 255
225 // Sort the test details. 256 // Sort the test details.
226 sort.Sort(GUITestDetailSortable(tests)) 257 sort.Sort(GUITestDetailSortable(tests))
227 258
228 return &GUITestDetails{ 259 return &GUITestDetails{
229 » » Commits: a.currentTestDetails.Commits, 260 » » Commits: a.current.TestDetails.Commits,
230 » » AllParams: a.currentIndex.getAllParams(query), 261 » » AllParams: a.current.Index.getAllParams(query),
231 Query: effectiveQuery, 262 Query: effectiveQuery,
232 Tests: tests, 263 Tests: tests,
233 }, nil 264 }, nil
234 } 265 }
235 266
236 // PolyGUISimple is basic info about a test. Returned from PolyListTestSimple. 267 // PolyGUISimple is basic info about a test. Returned from PolyListTestSimple.
237 type PolyGUISimple struct { 268 type PolyGUISimple struct {
238 Name string `json:"name"` 269 Name string `json:"name"`
239 Diameter int `json:"diameter"` 270 Diameter int `json:"diameter"`
240 Pos int `json:"pos"` 271 Pos int `json:"pos"`
(...skipping 27 matching lines...) Expand all
268 if hasQuery { 299 if hasQuery {
269 for _, tr := range tile.Traces { 300 for _, tr := range tile.Traces {
270 if ptypes.Matches(tr, query) { 301 if ptypes.Matches(tr, query) {
271 if name, ok := tr.Params()[types.PRIMARY_KEY_FIE LD]; ok { 302 if name, ok := tr.Params()[types.PRIMARY_KEY_FIE LD]; ok {
272 names[name] = true 303 names[name] = true
273 } 304 }
274 } 305 }
275 } 306 }
276 } 307 }
277 308
278 » for _, t := range a.currentTestDetails.Tests { 309 » for _, t := range a.current.TestDetails.Tests {
279 if hasQuery { 310 if hasQuery {
280 if _, ok := names[t.Name]; !ok { 311 if _, ok := names[t.Name]; !ok {
281 continue 312 continue
282 } 313 }
283 } 314 }
284 315
285 ret = append(ret, &PolyGUISimple{ 316 ret = append(ret, &PolyGUISimple{
286 Name: t.Name, 317 Name: t.Name,
287 Diameter: t.Diameter, 318 Diameter: t.Diameter,
288 Pos: len(t.Positive), 319 Pos: len(t.Positive),
(...skipping 21 matching lines...) Expand all
310 // If query is not empty then we will return traces that match the query. 341 // If query is not empty then we will return traces that match the query.
311 // If the query is empty and testName is not empty we will return the 342 // If the query is empty and testName is not empty we will return the
312 // traces of the corresponding test.If both query and testName are empty 343 // traces of the corresponding test.If both query and testName are empty
313 // we will return all traces. 344 // we will return all traces.
314 // TODO (stephana): If the result is too big we should add pagination. 345 // TODO (stephana): If the result is too big we should add pagination.
315 func (a *Analyzer) GetTestDetails(testName string, query map[string][]string) (* GUITestDetails, error) { 346 func (a *Analyzer) GetTestDetails(testName string, query map[string][]string) (* GUITestDetails, error) {
316 a.mutex.RLock() 347 a.mutex.RLock()
317 defer a.mutex.RUnlock() 348 defer a.mutex.RUnlock()
318 349
319 var effectiveQuery map[string][]string 350 var effectiveQuery map[string][]string
320 » testDetail := a.currentTestDetails.lookup(testName) 351 » testDetail := a.current.TestDetails.lookup(testName)
321 untriaged := testDetail.Untriaged 352 untriaged := testDetail.Untriaged
322 if len(query) > 0 { 353 if len(query) > 0 {
323 effectiveQuery = map[string][]string{} 354 effectiveQuery = map[string][]string{}
324 355
325 // Filter by only this test. 356 // Filter by only this test.
326 query[types.PRIMARY_KEY_FIELD] = []string{testName} 357 query[types.PRIMARY_KEY_FIELD] = []string{testName}
327 foundUntriaged := a.getUntriagedTestDetails(query, effectiveQuer y, false) 358 foundUntriaged := a.getUntriagedTestDetails(query, effectiveQuer y, false)
328 delete(effectiveQuery, types.PRIMARY_KEY_FIELD) 359 delete(effectiveQuery, types.PRIMARY_KEY_FIELD)
329 360
330 // Only consider the result if some query parameters were valid. 361 // Only consider the result if some query parameters were valid.
331 if len(effectiveQuery) > 0 { 362 if len(effectiveQuery) > 0 {
332 if temp, ok := foundUntriaged[testName]; ok { 363 if temp, ok := foundUntriaged[testName]; ok {
333 untriaged = temp 364 untriaged = temp
334 } else { 365 } else {
335 untriaged = map[string]*GUIUntriagedDigest{} 366 untriaged = map[string]*GUIUntriagedDigest{}
336 } 367 }
337 } 368 }
338 } 369 }
339 370
340 return &GUITestDetails{ 371 return &GUITestDetails{
341 » » Commits: a.currentTestDetails.Commits, 372 » » Commits: a.current.TestDetails.Commits,
342 » » CommitsByDigest: map[string]map[string][]int{testName: a.current TestDetails.CommitsByDigest[testName]}, 373 » » CommitsByDigest: map[string]map[string][]int{testName: a.current .TestDetails.CommitsByDigest[testName]},
343 » » AllParams: a.currentIndex.getAllParams(query), 374 » » AllParams: a.current.Index.getAllParams(query),
344 Query: effectiveQuery, 375 Query: effectiveQuery,
345 Tests: []*GUITestDetail{ 376 Tests: []*GUITestDetail{
346 &GUITestDetail{ 377 &GUITestDetail{
347 Name: testName, 378 Name: testName,
348 Untriaged: untriaged, 379 Untriaged: untriaged,
349 Positive: testDetail.Positive, 380 Positive: testDetail.Positive,
350 Negative: testDetail.Negative, 381 Negative: testDetail.Negative,
351 }, 382 },
352 }, 383 },
353 }, nil 384 }, nil
354 } 385 }
355 386
356 // SetDigestLabels sets the labels for the given digest and records the user 387 // SetDigestLabels sets the labels for the given digest and records the user
357 // that made the classification. 388 // that made the classification.
358 func (a *Analyzer) SetDigestLabels(labeledTestDigests map[string]types.TestClass ification, userId string) (*GUITestDetails, error) { 389 func (a *Analyzer) SetDigestLabels(labeledTestDigests map[string]types.TestClass ification, userId string) (*GUITestDetails, error) {
359 a.mutex.Lock() 390 a.mutex.Lock()
360 defer a.mutex.Unlock() 391 defer a.mutex.Unlock()
361 392
362 expectations, err := a.expStore.Get(true) 393 expectations, err := a.expStore.Get(true)
363 if err != nil { 394 if err != nil {
364 return nil, err 395 return nil, err
365 } 396 }
366 expectations.AddDigests(labeledTestDigests) 397 expectations.AddDigests(labeledTestDigests)
367 if err = a.expStore.Put(expectations, userId); err != nil { 398 if err = a.expStore.Put(expectations, userId); err != nil {
368 return nil, err 399 return nil, err
369 } 400 }
370 401
371 // Let's update our knowledge of the labels. 402 // Let's update our knowledge of the labels.
372 » a.updateDerivedOutputs(labeledTestDigests, expectations) 403 » a.updateDerivedOutputs(labeledTestDigests, expectations, a.current)
404 » a.updateDerivedOutputs(labeledTestDigests, expectations, a.ignored)
373 405
374 result := make([]*GUITestDetail, 0, len(labeledTestDigests)) 406 result := make([]*GUITestDetail, 0, len(labeledTestDigests))
375 for testName := range labeledTestDigests { 407 for testName := range labeledTestDigests {
376 » » result = append(result, a.currentTestDetails.lookup(testName)) 408 » » result = append(result, a.current.TestDetails.lookup(testName))
377 } 409 }
378 410
379 return &GUITestDetails{ 411 return &GUITestDetails{
380 » » Commits: a.currentTestDetails.Commits, 412 » » Commits: a.current.TestDetails.Commits,
381 » » AllParams: a.currentIndex.getAllParams(nil), 413 » » AllParams: a.current.Index.getAllParams(nil),
382 Tests: result, 414 Tests: result,
383 }, nil 415 }, nil
384 } 416 }
385 417
386 func (a *Analyzer) GetStatus() *GUIStatus { 418 func (a *Analyzer) GetStatus() *GUIStatus {
387 » return a.currentStatus 419 » return a.current.Status
420 }
421
422 // ListIgnoreRules returns all current ignore rules.
423 func (a *Analyzer) ListIgnoreRules() ([]*types.IgnoreRule, error) {
424 » rules, err := a.ignoreStore.List()
425 » if err != nil {
426 » » return nil, err
427 » }
428
429 » // for _, r := range rules {
jcgregorio 2015/01/30 17:37:28 commented out ?
stephana 2015/01/30 20:27:45 Added TODO.
430 » // » r.Count = a.ignored.currentIgnoreCounts[r.ID]
431 » // }
432
433 » return rules, nil
434 }
435
436 // AddIgnoreRule adds a new ignore rule and recalculates the new state of the
437 // system.
438 func (a *Analyzer) AddIgnoreRule(ignoreRule *types.IgnoreRule) error {
439 » if err := a.ignoreStore.Create(ignoreRule); err != nil {
440 » » return err
441 » }
442
443 » a.processTile()
444
445 » return nil
446 }
447
448 // DeleteIgnoreRule delets the ignore rule and recalculates the state of the
jcgregorio 2015/01/30 17:37:28 deletes
stephana 2015/01/30 20:27:45 Done.
449 // system.
450 func (a *Analyzer) DeleteIgnoreRule(ruleId int, user string) error {
451 » count, err := a.ignoreStore.Delete(ruleId, user)
452 » if err != nil {
453 » » return err
454 » }
455
456 » if count > 0 {
457 » » a.processTile()
458 » }
459
460 » return nil
388 } 461 }
389 462
390 // loop is the main event loop. 463 // loop is the main event loop.
391 func (a *Analyzer) loop(timeBetweenPolls time.Duration) { 464 func (a *Analyzer) loop(timeBetweenPolls time.Duration) {
392 // The number of times we've successfully loaded and processed a tile.
393 runsCounter := metrics.NewRegisteredCounter("analysis.runs", metrics.Def aultRegistry)
394
395 // The number of times an error has ocurred when trying to load a tile.
396 errorTileLoadingCounter := metrics.NewRegisteredCounter("analysis.errors ", metrics.DefaultRegistry)
397
398 processOneTile := func() {
399 glog.Info("Reading tiles ... ")
400
401 // Load the tile and process it.
402 tile, err := a.tileStore.GetModifiable(0, -1)
403 glog.Info("Done reading tiles.")
404
405 if err != nil {
406 glog.Errorf("Error reading tile store: %s\n", err)
407 errorTileLoadingCounter.Inc(1)
408 } else {
409 // Protect the tile and expectations with the write lock .
410 a.mutex.Lock()
411 defer a.mutex.Unlock()
412
413 // Retrieve the current expectations.
414 expectations, err := a.expStore.Get(false)
415 if err != nil {
416 glog.Errorf("Error retrieving expectations: %s", err)
417 return
418 }
419
420 newLabeledTile := a.processTile(tile)
421 a.setDerivedOutputs(newLabeledTile, expectations)
422 }
423 glog.Info("Done processing tiles.")
424 runsCounter.Inc(1)
425 a.loopCounter++
426 }
427
428 // process a tile immediately and then at fixed points in time. 465 // process a tile immediately and then at fixed points in time.
429 » processOneTile() 466 » a.processTile()
430 for _ = range time.Tick(timeBetweenPolls) { 467 for _ = range time.Tick(timeBetweenPolls) {
431 » » processOneTile() 468 » » a.processTile()
432 } 469 }
433 } 470 }
434 471
435 // processTile processes the last two tiles and updates the cannonical and 472 // processTile loads a tile (built by the ingest process) and partitions it
436 // output data structures. 473 // into two labeled tiles one with the traces of interest and the traces we
437 func (a *Analyzer) processTile(tile *ptypes.Tile) *LabeledTile { 474 // are ignoring.
475 func (a *Analyzer) processTile() {
476 » glog.Info("Reading tiles ... ")
477
478 » // Load the tile and process it.
479 » tile, err := a.tileStore.GetModifiable(0, -1)
480 » glog.Info("Done reading tiles.")
481
482 » if err != nil {
483 » » glog.Errorf("Error reading tile store: %s\n", err)
484 » » errorTileLoadingCounter.Inc(1)
485 » } else {
486 » » // Protect the tile and expectations with the write lock.
487 » » a.mutex.Lock()
488 » » defer a.mutex.Unlock()
489
490 » » // Retrieve the current expectations.
491 » » expectations, err := a.expStore.Get(false)
492 » » if err != nil {
493 » » » glog.Errorf("Error retrieving expectations: %s", err)
494 » » » return
495 » » }
496
497 » » newLabeledTile, ignoredLabeledTile := a.partitionRawTile(tile)
498 » » a.setDerivedOutputs(newLabeledTile, expectations, a.current)
499 » » a.setDerivedOutputs(ignoredLabeledTile, expectations, a.ignored)
500 » }
501 » glog.Info("Done processing tiles.")
502 » runsCounter.Inc(1)
503 » a.loopCounter++
504 }
505
506 // partitionRawTile partitions the input tile into two tiles (current and ignore d)
507 // and derives the output data structures for both.
508 func (a *Analyzer) partitionRawTile(tile *ptypes.Tile) (*LabeledTile, *LabeledTi le) {
438 glog.Info("Processing tile into LabeledTile ...") 509 glog.Info("Processing tile into LabeledTile ...")
439 result := NewLabeledTile()
440 510
511 // Shared between both tiles.
441 tileLen := tile.LastCommitIndex() + 1 512 tileLen := tile.LastCommitIndex() + 1
442 result.Commits = tile.Commits[:tileLen]
443 commitsByDigestMap := map[string]map[string]map[int]bool{}
444 513
445 » ignorableDigests := a.diffStore.IgnorableDigests() 514 » // Set the up the result tile and a tile for ignored traces.
446 » glog.Infof("Ignorable digests: %v", ignorableDigests) 515 » resultTile := NewLabeledTile()
516 » resultTile.Commits = tile.Commits[:tileLen]
517 » resultCommitsByDigestMap := map[string]map[string]map[int]bool{}
518
519 » ignoredTile := NewLabeledTile()
520 » ignoredTile.Commits = tile.Commits[:tileLen]
521 » ignoredCommitsByDigestMap := map[string]map[string]map[int]bool{}
522
523 » // Get the digests that are unavailable, e.g. they cannot be fetched
524 » // from GS or they are not valid images.
525 » unavailableDigests := a.diffStore.UnavailableDigests()
526 » glog.Infof("Unavailable digests: %v", unavailableDigests)
527
528 » // Get the rule matcher to find traces to ignore.
529 » ruleMatcher, err := a.ignoreStore.BuildRuleMatcher()
530 » if err != nil {
531 » » glog.Errorf("Unable to build rule matcher: %s", err)
532 » }
447 533
448 // Note: We are assumming that the number and order of traces will chang e 534 // Note: We are assumming that the number and order of traces will chang e
449 // over time. 535 // over time.
536 var targetTile *LabeledTile
537 var commitsByDigestMap map[string]map[string]map[int]bool
450 for _, v := range tile.Traces { 538 for _, v := range tile.Traces {
539 // Determine if this tile is to be in the result or the ignored tile.
540 matchedRules, isIgnored := ruleMatcher(v.Params())
541 if isIgnored {
542 targetTile = ignoredTile
543 commitsByDigestMap = ignoredCommitsByDigestMap
544 } else {
545 targetTile = resultTile
546 commitsByDigestMap = resultCommitsByDigestMap
547 }
548
451 tempCommitIds := make([]int, 0, tileLen) 549 tempCommitIds := make([]int, 0, tileLen)
452 tempLabels := make([]types.Label, 0, tileLen) 550 tempLabels := make([]types.Label, 0, tileLen)
453 tempDigests := make([]string, 0, tileLen) 551 tempDigests := make([]string, 0, tileLen)
454 gTrace := v.(*ptypes.GoldenTrace) 552 gTrace := v.(*ptypes.GoldenTrace)
455 testName := gTrace.Params()[types.PRIMARY_KEY_FIELD] 553 testName := gTrace.Params()[types.PRIMARY_KEY_FIELD]
456 554
457 // Iterate over the digests in this trace. 555 // Iterate over the digests in this trace.
458 for i, v := range gTrace.Values[:tileLen] { 556 for i, v := range gTrace.Values[:tileLen] {
459 » » » if (v != ptypes.MISSING_DIGEST) && !ignorableDigests[v] { 557 » » » if (v != ptypes.MISSING_DIGEST) && !unavailableDigests[v ] {
460 tempCommitIds = append(tempCommitIds, i) 558 tempCommitIds = append(tempCommitIds, i)
461 tempDigests = append(tempDigests, v) 559 tempDigests = append(tempDigests, v)
462 tempLabels = append(tempLabels, types.UNTRIAGED) 560 tempLabels = append(tempLabels, types.UNTRIAGED)
463 561
464 // Keep track of the commits by digest. 562 // Keep track of the commits by digest.
465 if _, ok := commitsByDigestMap[testName]; !ok { 563 if _, ok := commitsByDigestMap[testName]; !ok {
466 commitsByDigestMap[testName] = map[strin g]map[int]bool{v: map[int]bool{i: true}} 564 commitsByDigestMap[testName] = map[strin g]map[int]bool{v: map[int]bool{i: true}}
467 } else if _, ok := commitsByDigestMap[testName][ v]; !ok { 565 } else if _, ok := commitsByDigestMap[testName][ v]; !ok {
468 commitsByDigestMap[testName][v] = map[in t]bool{i: true} 566 commitsByDigestMap[testName][v] = map[in t]bool{i: true}
469 } else { 567 } else {
470 commitsByDigestMap[testName][v][i] = tru e 568 commitsByDigestMap[testName][v][i] = tru e
471 } 569 }
472 } 570 }
473 } 571 }
474 572
475 // Only consider traces that are not empty. 573 // Only consider traces that are not empty.
476 if len(tempLabels) > 0 { 574 if len(tempLabels) > 0 {
477 // Label the digests and add them to the labeled traces. 575 // Label the digests and add them to the labeled traces.
478 » » » _, targetLabeledTrace := result.getLabeledTrace(v) 576 » » » _, targetLabeledTrace := targetTile.getLabeledTrace(v)
479 targetLabeledTrace.addLabeledDigests(tempCommitIds, temp Digests, tempLabels) 577 targetLabeledTrace.addLabeledDigests(tempCommitIds, temp Digests, tempLabels)
578 if isIgnored {
579 targetLabeledTrace.addIgnoreRules(matchedRules)
580 }
480 } 581 }
481 } 582 }
482 583
584 getCommitsByDigest(resultTile, resultCommitsByDigestMap)
585 getCommitsByDigest(ignoredTile, ignoredCommitsByDigestMap)
586
587 glog.Info("Done processing tile into LabeledTile.")
588 return resultTile, ignoredTile
589 }
590
591 func getCommitsByDigest(labeledTile *LabeledTile, commitsByDigestMap map[string] map[string]map[int]bool) {
483 for testName, cbd := range commitsByDigestMap { 592 for testName, cbd := range commitsByDigestMap {
484 » » result.CommitsByDigest[testName] = make(map[string][]int, len(cb d)) 593 » » labeledTile.CommitsByDigest[testName] = make(map[string][]int, l en(cbd))
485 for d, commitIds := range cbd { 594 for d, commitIds := range cbd {
486 » » » result.CommitsByDigest[testName][d] = util.KeysOfIntSet( commitIds) 595 » » » labeledTile.CommitsByDigest[testName][d] = util.KeysOfIn tSet(commitIds)
487 » » » sort.Ints(result.CommitsByDigest[testName][d]) 596 » » » sort.Ints(labeledTile.CommitsByDigest[testName][d])
488 } 597 }
489 } 598 }
490
491 glog.Info("Done processing tile into LabeledTile.")
492 return result
493 } 599 }
494 600
495 // setDerivedOutputs derives the output data from the given tile and 601 // setDerivedOutputs derives the output data from the given tile and
496 // updates the outputs and tile in the analyzer. 602 // updates the outputs and tile in the analyzer.
497 func (a *Analyzer) setDerivedOutputs(labeledTile *LabeledTile, expectations *exp storage.Expectations) { 603 func (a *Analyzer) setDerivedOutputs(labeledTile *LabeledTile, expectations *exp storage.Expectations, state *AnalyzeState) {
498 // Assign all the labels. 604 // Assign all the labels.
499 for testName, traces := range labeledTile.Traces { 605 for testName, traces := range labeledTile.Traces {
500 for _, trace := range traces { 606 for _, trace := range traces {
501 » » » a.labelDigests(testName, trace.Digests, trace.Labels, ex pectations) 607 » » » labelDigests(testName, trace.Digests, trace.Labels, expe ctations)
502 } 608 }
503 } 609 }
504 610
505 // Generate the lookup index for the tile and get all parameters. 611 // Generate the lookup index for the tile and get all parameters.
506 » a.currentIndex = NewLabeledTileIndex(labeledTile) 612 » state.Index = NewLabeledTileIndex(labeledTile)
507 613
508 // calculate all the output data. 614 // calculate all the output data.
509 » a.currentTile = labeledTile 615 » state.Tile = labeledTile
510 » a.currentTileCounts = a.getOutputCounts(labeledTile) 616 » state.TileCounts = a.getOutputCounts(state.Tile, state.Index)
511 » a.currentTestDetails = a.getTestDetails(labeledTile) 617 » state.TestDetails = a.getTestDetails(state)
512 » a.currentStatus = a.calcStatus(labeledTile) 618 » state.Status = calcStatus(state)
513 } 619 }
514 620
515 // updateLabels iterates over the traces in of the tiles that have changed and 621 // updateLabels iterates over the traces in of the tiles that have changed and
516 // labels them according to our current expecatations. 622 // labels them according to our current expecatations.
517 623
518 // updateDerivedOutputs 624 // updateDerivedOutputs
519 func (a *Analyzer) updateDerivedOutputs(labeledTestDigests map[string]types.Test Classification, expectations *expstorage.Expectations) { 625 func (a *Analyzer) updateDerivedOutputs(labeledTestDigests map[string]types.Test Classification, expectations *expstorage.Expectations, state *AnalyzeState) {
520 // Update the labels of the traces that have changed. 626 // Update the labels of the traces that have changed.
521 for testName := range labeledTestDigests { 627 for testName := range labeledTestDigests {
522 » » if traces, ok := a.currentTile.Traces[testName]; ok { 628 » » if traces, ok := state.Tile.Traces[testName]; ok {
523 for _, trace := range traces { 629 for _, trace := range traces {
524 // Note: This is potentially slower than using l abels in 630 // Note: This is potentially slower than using l abels in
525 // labeledTestDigests directly, but it keeps the code simpler. 631 // labeledTestDigests directly, but it keeps the code simpler.
526 » » » » a.labelDigests(testName, trace.Digests, trace.La bels, expectations) 632 » » » » labelDigests(testName, trace.Digests, trace.Labe ls, expectations)
527 } 633 }
528 } 634 }
529 } 635 }
530 636
531 // Update all the output data structures. 637 // Update all the output data structures.
532 // TODO(stephana): Evaluate whether the counts are really useful or if t hey can be removed. 638 // TODO(stephana): Evaluate whether the counts are really useful or if t hey can be removed.
533 // If we need them uncomment the following line and implement the corres ponding function. 639 // If we need them uncomment the following line and implement the corres ponding function.
534 //a.updateOutputCounts(labeledTestDigests) 640 //a.updateOutputCounts(labeledTestDigests)
535 641
536 // Update the tests that have changed and the status. 642 // Update the tests that have changed and the status.
537 » a.updateTestDetails(labeledTestDigests) 643 » a.updateTestDetails(labeledTestDigests, state)
538 » a.currentStatus = a.calcStatus(a.currentTile) 644 » state.Status = calcStatus(state)
539 } 645 }
540 646
541 // labelDigest assignes a label to the given digests based on the expectations. 647 // labelDigest assignes a label to the given digests based on the expectations.
542 // Its assumes that targetLabels are pre-initialized, usualy with UNTRIAGED, 648 // Its assumes that targetLabels are pre-initialized, usualy with UNTRIAGED,
543 // because it will not change the label if the given test and digest cannot be 649 // because it will not change the label if the given test and digest cannot be
544 // found. 650 // found.
545 func (a *Analyzer) labelDigests(testName string, digests []string, targetLabels []types.Label, expectations *expstorage.Expectations) { 651 func labelDigests(testName string, digests []string, targetLabels []types.Label, expectations *expstorage.Expectations) {
546 for idx, digest := range digests { 652 for idx, digest := range digests {
547 if test, ok := expectations.Tests[testName]; ok { 653 if test, ok := expectations.Tests[testName]; ok {
548 if foundLabel, ok := test[digest]; ok { 654 if foundLabel, ok := test[digest]; ok {
549 targetLabels[idx] = foundLabel 655 targetLabels[idx] = foundLabel
550 } 656 }
551 } 657 }
552 } 658 }
553 } 659 }
554 660
555 // getUntriagedTestDetails returns the untriaged digests of a specific test that 661 // getUntriagedTestDetails returns the untriaged digests of a specific test that
556 // match the given query. In addition to the digests it returns the query 662 // match the given query. In addition to the digests it returns the query
557 // that was used to retrieve them. 663 // that was used to retrieve them.
558 func (a *Analyzer) getUntriagedTestDetails(query, effectiveQuery map[string][]st ring, includeAllTests bool) map[string]map[string]*GUIUntriagedDigest { 664 func (a *Analyzer) getUntriagedTestDetails(query, effectiveQuery map[string][]st ring, includeAllTests bool) map[string]map[string]*GUIUntriagedDigest {
559 » traces, startCommitId, endCommitId, showHead := a.currentIndex.query(que ry, effectiveQuery) 665 » traces, startCommitId, endCommitId, showHead := a.current.Index.query(qu ery, effectiveQuery)
560 endCommitId++ 666 endCommitId++
561 667
562 if len(effectiveQuery) == 0 { 668 if len(effectiveQuery) == 0 {
563 return nil 669 return nil
564 } 670 }
565 671
566 » ret := make(map[string]map[string]*GUIUntriagedDigest, len(a.currentTest Details.Tests)) 672 » ret := make(map[string]map[string]*GUIUntriagedDigest, len(a.current.Tes tDetails.Tests))
567 673
568 // This includes an empty list for tests that we have not found. 674 // This includes an empty list for tests that we have not found.
569 if includeAllTests { 675 if includeAllTests {
570 » » for _, testName := range a.currentIndex.getTestNames(query) { 676 » » for _, testName := range a.current.Index.getTestNames(query) {
571 ret[testName] = nil 677 ret[testName] = nil
572 } 678 }
573 } 679 }
574 680
575 if !showHead { 681 if !showHead {
576 for _, trace := range traces { 682 for _, trace := range traces {
577 testName := trace.Params[types.PRIMARY_KEY_FIELD] 683 testName := trace.Params[types.PRIMARY_KEY_FIELD]
578 » » » current := a.currentTestDetails.lookup(testName).Untriag ed 684 » » » current := a.current.TestDetails.lookup(testName).Untria ged
579 685
580 startIdx := sort.SearchInts(trace.CommitIds, startCommit Id) 686 startIdx := sort.SearchInts(trace.CommitIds, startCommit Id)
581 endIdx := sort.SearchInts(trace.CommitIds, endCommitId) 687 endIdx := sort.SearchInts(trace.CommitIds, endCommitId)
582 if (endIdx < len(trace.CommitIds)) && (trace.CommitIds[e ndIdx] == endCommitId) { 688 if (endIdx < len(trace.CommitIds)) && (trace.CommitIds[e ndIdx] == endCommitId) {
583 endIdx++ 689 endIdx++
584 } 690 }
585 691
586 for idx := startIdx; idx < endIdx; idx++ { 692 for idx := startIdx; idx < endIdx; idx++ {
587 if trace.Labels[idx] == types.UNTRIAGED { 693 if trace.Labels[idx] == types.UNTRIAGED {
588 if found, ok := ret[testName]; !ok || fo und == nil { 694 if found, ok := ret[testName]; !ok || fo und == nil {
589 ret[testName] = make(map[string] *GUIUntriagedDigest, len(current)) 695 ret[testName] = make(map[string] *GUIUntriagedDigest, len(current))
590 } 696 }
591 ret[testName][trace.Digests[idx]] = curr ent[trace.Digests[idx]] 697 ret[testName][trace.Digests[idx]] = curr ent[trace.Digests[idx]]
592 } 698 }
593 } 699 }
594 } 700 }
595 } else { 701 } else {
596 for _, trace := range traces { 702 for _, trace := range traces {
597 lastIdx := len(trace.Labels) - 1 703 lastIdx := len(trace.Labels) - 1
598 if (lastIdx >= 0) && (trace.Labels[lastIdx] == types.UNT RIAGED) { 704 if (lastIdx >= 0) && (trace.Labels[lastIdx] == types.UNT RIAGED) {
599 testName := trace.Params[types.PRIMARY_KEY_FIELD ] 705 testName := trace.Params[types.PRIMARY_KEY_FIELD ]
600 » » » » current := a.currentTestDetails.lookup(testName) .Untriaged 706 » » » » current := a.current.TestDetails.lookup(testName ).Untriaged
601 if found, ok := ret[testName]; !ok || found == n il { 707 if found, ok := ret[testName]; !ok || found == n il {
602 ret[testName] = map[string]*GUIUntriaged Digest{} 708 ret[testName] = map[string]*GUIUntriaged Digest{}
603 } 709 }
604 ret[testName][trace.Digests[lastIdx]] = current[ trace.Digests[lastIdx]] 710 ret[testName][trace.Digests[lastIdx]] = current[ trace.Digests[lastIdx]]
605 } 711 }
606 } 712 }
607 } 713 }
608 714
609 return ret 715 return ret
610 } 716 }
611 717
612 // getSubTile queries the index and returns a LabeledTile that contains the 718 // getSubTile queries the index and returns a LabeledTile that contains the
613 // set of found traces. It also returns the subset of 'query' that contained 719 // set of found traces. It also returns the subset of 'query' that contained
614 // valid parameters and values. 720 // valid parameters and values.
615 // If the returned query is empty the first return value is set to Nil, 721 // If the returned query is empty the first return value is set to Nil,
616 // because now valid filter parameters were found in the query. 722 // because now valid filter parameters were found in the query.
617 func (a *Analyzer) getSubTile(query map[string][]string) (*LabeledTile, map[stri ng][]string) { 723 func (a *Analyzer) getSubTile(query map[string][]string) (*LabeledTile, map[stri ng][]string) {
618 // TODO(stephana): Use the commitStart and commitEnd return values 724 // TODO(stephana): Use the commitStart and commitEnd return values
619 // if we really need this method. GetTileCounts and getSubTile might be 725 // if we really need this method. GetTileCounts and getSubTile might be
620 // removed. 726 // removed.
621 effectiveQuery := make(map[string][]string, len(query)) 727 effectiveQuery := make(map[string][]string, len(query))
622 » traces, _, _, _ := a.currentIndex.query(query, effectiveQuery) 728 » traces, _, _, _ := a.current.Index.query(query, effectiveQuery)
623 if len(effectiveQuery) == 0 { 729 if len(effectiveQuery) == 0 {
624 return nil, effectiveQuery 730 return nil, effectiveQuery
625 } 731 }
626 732
627 result := NewLabeledTile() 733 result := NewLabeledTile()
628 » result.Commits = a.currentTile.Commits 734 » result.Commits = a.current.Tile.Commits
629 735
630 result.Traces = map[string][]*LabeledTrace{} 736 result.Traces = map[string][]*LabeledTrace{}
631 for _, t := range traces { 737 for _, t := range traces {
632 testName := t.Params[types.PRIMARY_KEY_FIELD] 738 testName := t.Params[types.PRIMARY_KEY_FIELD]
633 if _, ok := result.Traces[testName]; !ok { 739 if _, ok := result.Traces[testName]; !ok {
634 result.Traces[testName] = []*LabeledTrace{} 740 result.Traces[testName] = []*LabeledTrace{}
635 } 741 }
636 result.Traces[testName] = append(result.Traces[testName], t) 742 result.Traces[testName] = append(result.Traces[testName], t)
637 } 743 }
638 744
639 return result, effectiveQuery 745 return result, effectiveQuery
640 } 746 }
641 747
642 // getOutputCounts derives the output counts from the given labeled tile. 748 // getOutputCounts derives the output counts from the given labeled tile.
643 func (a *Analyzer) getOutputCounts(labeledTile *LabeledTile) *GUITileCounts { 749 func (a *Analyzer) getOutputCounts(labeledTile *LabeledTile, index *LabeledTileI ndex) *GUITileCounts {
644 glog.Info("Starting to process output counts.") 750 glog.Info("Starting to process output counts.")
645 // Stores the aggregated counts of a tile for each test. 751 // Stores the aggregated counts of a tile for each test.
646 tileCountsMap := make(map[string]*LabelCounts, len(labeledTile.Traces)) 752 tileCountsMap := make(map[string]*LabelCounts, len(labeledTile.Traces))
647 753
648 // Overall aggregated counts over all tests. 754 // Overall aggregated counts over all tests.
649 overallAggregates := newLabelCounts(len(labeledTile.Commits)) 755 overallAggregates := newLabelCounts(len(labeledTile.Commits))
650 756
651 updateCounts(labeledTile, tileCountsMap, overallAggregates) 757 updateCounts(labeledTile, tileCountsMap, overallAggregates)
652 758
653 // TODO (stephana): Factor out human.FlotTickMarks and move it from 759 // TODO (stephana): Factor out human.FlotTickMarks and move it from
654 // perf to the shared go library. 760 // perf to the shared go library.
655 // Generate the tickmarks for the commits. 761 // Generate the tickmarks for the commits.
656 ts := make([]int64, 0, len(labeledTile.Commits)) 762 ts := make([]int64, 0, len(labeledTile.Commits))
657 for _, c := range labeledTile.Commits { 763 for _, c := range labeledTile.Commits {
658 if c.CommitTime != 0 { 764 if c.CommitTime != 0 {
659 ts = append(ts, c.CommitTime) 765 ts = append(ts, c.CommitTime)
660 } 766 }
661 } 767 }
662 768
663 tileCounts := &GUITileCounts{ 769 tileCounts := &GUITileCounts{
664 Commits: labeledTile.Commits, 770 Commits: labeledTile.Commits,
665 Ticks: human.FlotTickMarks(ts), 771 Ticks: human.FlotTickMarks(ts),
666 Aggregated: overallAggregates, 772 Aggregated: overallAggregates,
667 Counts: tileCountsMap, 773 Counts: tileCountsMap,
668 » » AllParams: a.currentIndex.getAllParams(nil), 774 » » AllParams: index.getAllParams(nil),
669 } 775 }
670 776
671 glog.Info("Done processing output counts.") 777 glog.Info("Done processing output counts.")
672 778
673 return tileCounts 779 return tileCounts
674 } 780 }
675 781
676 func updateCounts(labeledTile *LabeledTile, tileCountsMap map[string]*LabelCount s, overallAggregates *LabelCounts) { 782 func updateCounts(labeledTile *LabeledTile, tileCountsMap map[string]*LabelCount s, overallAggregates *LabelCounts) {
677 for testName, testTraces := range labeledTile.Traces { 783 for testName, testTraces := range labeledTile.Traces {
678 acc := newLabelCounts(len(labeledTile.Commits)) 784 acc := newLabelCounts(len(labeledTile.Commits))
(...skipping 14 matching lines...) Expand all
693 tileCountsMap[testName] = acc 799 tileCountsMap[testName] = acc
694 800
695 // Add the aggregates fro this test to the overall aggregates. 801 // Add the aggregates fro this test to the overall aggregates.
696 for idx, u := range acc.Unt { 802 for idx, u := range acc.Unt {
697 overallAggregates.Unt[idx] += u 803 overallAggregates.Unt[idx] += u
698 overallAggregates.Pos[idx] += acc.Pos[idx] 804 overallAggregates.Pos[idx] += acc.Pos[idx]
699 overallAggregates.Neg[idx] += acc.Neg[idx] 805 overallAggregates.Neg[idx] += acc.Neg[idx]
700 } 806 }
701 } 807 }
702 } 808 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698