OLD | NEW |
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 |
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 Loading... |
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 Loading... |
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 Loading... |
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 » // TODO(stephana): Inject Count and other statistics about the |
| 430 » // ignored traces. This will be based on LabeledTrace.IgnoreRules. |
| 431 |
| 432 » return rules, nil |
| 433 } |
| 434 |
| 435 // AddIgnoreRule adds a new ignore rule and recalculates the new state of the |
| 436 // system. |
| 437 func (a *Analyzer) AddIgnoreRule(ignoreRule *types.IgnoreRule) error { |
| 438 » if err := a.ignoreStore.Create(ignoreRule); err != nil { |
| 439 » » return err |
| 440 » } |
| 441 |
| 442 » a.processTile() |
| 443 |
| 444 » return nil |
| 445 } |
| 446 |
| 447 // DeleteIgnoreRule deletes the ignore rule and recalculates the state of the |
| 448 // system. |
| 449 func (a *Analyzer) DeleteIgnoreRule(ruleId int, user string) error { |
| 450 » count, err := a.ignoreStore.Delete(ruleId, user) |
| 451 » if err != nil { |
| 452 » » return err |
| 453 » } |
| 454 |
| 455 » if count > 0 { |
| 456 » » a.processTile() |
| 457 » } |
| 458 |
| 459 » return nil |
388 } | 460 } |
389 | 461 |
390 // loop is the main event loop. | 462 // loop is the main event loop. |
391 func (a *Analyzer) loop(timeBetweenPolls time.Duration) { | 463 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. | 464 // process a tile immediately and then at fixed points in time. |
429 » processOneTile() | 465 » a.processTile() |
430 for _ = range time.Tick(timeBetweenPolls) { | 466 for _ = range time.Tick(timeBetweenPolls) { |
431 » » processOneTile() | 467 » » a.processTile() |
432 } | 468 } |
433 } | 469 } |
434 | 470 |
435 // processTile processes the last two tiles and updates the cannonical and | 471 // processTile loads a tile (built by the ingest process) and partitions it |
436 // output data structures. | 472 // into two labeled tiles one with the traces of interest and the traces we |
437 func (a *Analyzer) processTile(tile *ptypes.Tile) *LabeledTile { | 473 // are ignoring. |
| 474 func (a *Analyzer) processTile() { |
| 475 » glog.Info("Reading tiles ... ") |
| 476 |
| 477 » // Load the tile and process it. |
| 478 » tile, err := a.tileStore.GetModifiable(0, -1) |
| 479 » glog.Info("Done reading tiles.") |
| 480 |
| 481 » if err != nil { |
| 482 » » glog.Errorf("Error reading tile store: %s\n", err) |
| 483 » » errorTileLoadingCounter.Inc(1) |
| 484 » } else { |
| 485 » » // Protect the tile and expectations with the write lock. |
| 486 » » a.mutex.Lock() |
| 487 » » defer a.mutex.Unlock() |
| 488 |
| 489 » » // Retrieve the current expectations. |
| 490 » » expectations, err := a.expStore.Get(false) |
| 491 » » if err != nil { |
| 492 » » » glog.Errorf("Error retrieving expectations: %s", err) |
| 493 » » » return |
| 494 » » } |
| 495 |
| 496 » » newLabeledTile, ignoredLabeledTile := a.partitionRawTile(tile) |
| 497 » » a.setDerivedOutputs(newLabeledTile, expectations, a.current) |
| 498 » » a.setDerivedOutputs(ignoredLabeledTile, expectations, a.ignored) |
| 499 » } |
| 500 » glog.Info("Done processing tiles.") |
| 501 » runsCounter.Inc(1) |
| 502 » a.loopCounter++ |
| 503 } |
| 504 |
| 505 // partitionRawTile partitions the input tile into two tiles (current and ignore
d) |
| 506 // and derives the output data structures for both. |
| 507 func (a *Analyzer) partitionRawTile(tile *ptypes.Tile) (*LabeledTile, *LabeledTi
le) { |
438 glog.Info("Processing tile into LabeledTile ...") | 508 glog.Info("Processing tile into LabeledTile ...") |
439 result := NewLabeledTile() | |
440 | 509 |
| 510 // Shared between both tiles. |
441 tileLen := tile.LastCommitIndex() + 1 | 511 tileLen := tile.LastCommitIndex() + 1 |
442 result.Commits = tile.Commits[:tileLen] | |
443 commitsByDigestMap := map[string]map[string]map[int]bool{} | |
444 | 512 |
445 » ignorableDigests := a.diffStore.IgnorableDigests() | 513 » // Set the up the result tile and a tile for ignored traces. |
446 » glog.Infof("Ignorable digests: %v", ignorableDigests) | 514 » resultTile := NewLabeledTile() |
| 515 » resultTile.Commits = tile.Commits[:tileLen] |
| 516 » resultCommitsByDigestMap := map[string]map[string]map[int]bool{} |
| 517 |
| 518 » ignoredTile := NewLabeledTile() |
| 519 » ignoredTile.Commits = tile.Commits[:tileLen] |
| 520 » ignoredCommitsByDigestMap := map[string]map[string]map[int]bool{} |
| 521 |
| 522 » // Get the digests that are unavailable, e.g. they cannot be fetched |
| 523 » // from GS or they are not valid images. |
| 524 » unavailableDigests := a.diffStore.UnavailableDigests() |
| 525 » glog.Infof("Unavailable digests: %v", unavailableDigests) |
| 526 |
| 527 » // Get the rule matcher to find traces to ignore. |
| 528 » ruleMatcher, err := a.ignoreStore.BuildRuleMatcher() |
| 529 » if err != nil { |
| 530 » » glog.Errorf("Unable to build rule matcher: %s", err) |
| 531 » } |
447 | 532 |
448 // Note: We are assumming that the number and order of traces will chang
e | 533 // Note: We are assumming that the number and order of traces will chang
e |
449 // over time. | 534 // over time. |
| 535 var targetTile *LabeledTile |
| 536 var commitsByDigestMap map[string]map[string]map[int]bool |
450 for _, v := range tile.Traces { | 537 for _, v := range tile.Traces { |
| 538 // Determine if this tile is to be in the result or the ignored
tile. |
| 539 matchedRules, isIgnored := ruleMatcher(v.Params()) |
| 540 if isIgnored { |
| 541 targetTile = ignoredTile |
| 542 commitsByDigestMap = ignoredCommitsByDigestMap |
| 543 } else { |
| 544 targetTile = resultTile |
| 545 commitsByDigestMap = resultCommitsByDigestMap |
| 546 } |
| 547 |
451 tempCommitIds := make([]int, 0, tileLen) | 548 tempCommitIds := make([]int, 0, tileLen) |
452 tempLabels := make([]types.Label, 0, tileLen) | 549 tempLabels := make([]types.Label, 0, tileLen) |
453 tempDigests := make([]string, 0, tileLen) | 550 tempDigests := make([]string, 0, tileLen) |
454 gTrace := v.(*ptypes.GoldenTrace) | 551 gTrace := v.(*ptypes.GoldenTrace) |
455 testName := gTrace.Params()[types.PRIMARY_KEY_FIELD] | 552 testName := gTrace.Params()[types.PRIMARY_KEY_FIELD] |
456 | 553 |
457 // Iterate over the digests in this trace. | 554 // Iterate over the digests in this trace. |
458 for i, v := range gTrace.Values[:tileLen] { | 555 for i, v := range gTrace.Values[:tileLen] { |
459 » » » if (v != ptypes.MISSING_DIGEST) && !ignorableDigests[v]
{ | 556 » » » if (v != ptypes.MISSING_DIGEST) && !unavailableDigests[v
] { |
460 tempCommitIds = append(tempCommitIds, i) | 557 tempCommitIds = append(tempCommitIds, i) |
461 tempDigests = append(tempDigests, v) | 558 tempDigests = append(tempDigests, v) |
462 tempLabels = append(tempLabels, types.UNTRIAGED) | 559 tempLabels = append(tempLabels, types.UNTRIAGED) |
463 | 560 |
464 // Keep track of the commits by digest. | 561 // Keep track of the commits by digest. |
465 if _, ok := commitsByDigestMap[testName]; !ok { | 562 if _, ok := commitsByDigestMap[testName]; !ok { |
466 commitsByDigestMap[testName] = map[strin
g]map[int]bool{v: map[int]bool{i: true}} | 563 commitsByDigestMap[testName] = map[strin
g]map[int]bool{v: map[int]bool{i: true}} |
467 } else if _, ok := commitsByDigestMap[testName][
v]; !ok { | 564 } else if _, ok := commitsByDigestMap[testName][
v]; !ok { |
468 commitsByDigestMap[testName][v] = map[in
t]bool{i: true} | 565 commitsByDigestMap[testName][v] = map[in
t]bool{i: true} |
469 } else { | 566 } else { |
470 commitsByDigestMap[testName][v][i] = tru
e | 567 commitsByDigestMap[testName][v][i] = tru
e |
471 } | 568 } |
472 } | 569 } |
473 } | 570 } |
474 | 571 |
475 // Only consider traces that are not empty. | 572 // Only consider traces that are not empty. |
476 if len(tempLabels) > 0 { | 573 if len(tempLabels) > 0 { |
477 // Label the digests and add them to the labeled traces. | 574 // Label the digests and add them to the labeled traces. |
478 » » » _, targetLabeledTrace := result.getLabeledTrace(v) | 575 » » » _, targetLabeledTrace := targetTile.getLabeledTrace(v) |
479 targetLabeledTrace.addLabeledDigests(tempCommitIds, temp
Digests, tempLabels) | 576 targetLabeledTrace.addLabeledDigests(tempCommitIds, temp
Digests, tempLabels) |
| 577 if isIgnored { |
| 578 targetLabeledTrace.addIgnoreRules(matchedRules) |
| 579 } |
480 } | 580 } |
481 } | 581 } |
482 | 582 |
| 583 getCommitsByDigest(resultTile, resultCommitsByDigestMap) |
| 584 getCommitsByDigest(ignoredTile, ignoredCommitsByDigestMap) |
| 585 |
| 586 glog.Info("Done processing tile into LabeledTile.") |
| 587 return resultTile, ignoredTile |
| 588 } |
| 589 |
| 590 func getCommitsByDigest(labeledTile *LabeledTile, commitsByDigestMap map[string]
map[string]map[int]bool) { |
483 for testName, cbd := range commitsByDigestMap { | 591 for testName, cbd := range commitsByDigestMap { |
484 » » result.CommitsByDigest[testName] = make(map[string][]int, len(cb
d)) | 592 » » labeledTile.CommitsByDigest[testName] = make(map[string][]int, l
en(cbd)) |
485 for d, commitIds := range cbd { | 593 for d, commitIds := range cbd { |
486 » » » result.CommitsByDigest[testName][d] = util.KeysOfIntSet(
commitIds) | 594 » » » labeledTile.CommitsByDigest[testName][d] = util.KeysOfIn
tSet(commitIds) |
487 » » » sort.Ints(result.CommitsByDigest[testName][d]) | 595 » » » sort.Ints(labeledTile.CommitsByDigest[testName][d]) |
488 } | 596 } |
489 } | 597 } |
490 | |
491 glog.Info("Done processing tile into LabeledTile.") | |
492 return result | |
493 } | 598 } |
494 | 599 |
495 // setDerivedOutputs derives the output data from the given tile and | 600 // setDerivedOutputs derives the output data from the given tile and |
496 // updates the outputs and tile in the analyzer. | 601 // updates the outputs and tile in the analyzer. |
497 func (a *Analyzer) setDerivedOutputs(labeledTile *LabeledTile, expectations *exp
storage.Expectations) { | 602 func (a *Analyzer) setDerivedOutputs(labeledTile *LabeledTile, expectations *exp
storage.Expectations, state *AnalyzeState) { |
498 // Assign all the labels. | 603 // Assign all the labels. |
499 for testName, traces := range labeledTile.Traces { | 604 for testName, traces := range labeledTile.Traces { |
500 for _, trace := range traces { | 605 for _, trace := range traces { |
501 » » » a.labelDigests(testName, trace.Digests, trace.Labels, ex
pectations) | 606 » » » labelDigests(testName, trace.Digests, trace.Labels, expe
ctations) |
502 } | 607 } |
503 } | 608 } |
504 | 609 |
505 // Generate the lookup index for the tile and get all parameters. | 610 // Generate the lookup index for the tile and get all parameters. |
506 » a.currentIndex = NewLabeledTileIndex(labeledTile) | 611 » state.Index = NewLabeledTileIndex(labeledTile) |
507 | 612 |
508 // calculate all the output data. | 613 // calculate all the output data. |
509 » a.currentTile = labeledTile | 614 » state.Tile = labeledTile |
510 » a.currentTileCounts = a.getOutputCounts(labeledTile) | 615 » state.TileCounts = a.getOutputCounts(state.Tile, state.Index) |
511 » a.currentTestDetails = a.getTestDetails(labeledTile) | 616 » state.TestDetails = a.getTestDetails(state) |
512 » a.currentStatus = a.calcStatus(labeledTile) | 617 » state.Status = calcStatus(state) |
513 } | 618 } |
514 | 619 |
515 // updateLabels iterates over the traces in of the tiles that have changed and | 620 // updateLabels iterates over the traces in of the tiles that have changed and |
516 // labels them according to our current expecatations. | 621 // labels them according to our current expecatations. |
517 | 622 |
518 // updateDerivedOutputs | 623 // updateDerivedOutputs |
519 func (a *Analyzer) updateDerivedOutputs(labeledTestDigests map[string]types.Test
Classification, expectations *expstorage.Expectations) { | 624 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. | 625 // Update the labels of the traces that have changed. |
521 for testName := range labeledTestDigests { | 626 for testName := range labeledTestDigests { |
522 » » if traces, ok := a.currentTile.Traces[testName]; ok { | 627 » » if traces, ok := state.Tile.Traces[testName]; ok { |
523 for _, trace := range traces { | 628 for _, trace := range traces { |
524 // Note: This is potentially slower than using l
abels in | 629 // Note: This is potentially slower than using l
abels in |
525 // labeledTestDigests directly, but it keeps the
code simpler. | 630 // labeledTestDigests directly, but it keeps the
code simpler. |
526 » » » » a.labelDigests(testName, trace.Digests, trace.La
bels, expectations) | 631 » » » » labelDigests(testName, trace.Digests, trace.Labe
ls, expectations) |
527 } | 632 } |
528 } | 633 } |
529 } | 634 } |
530 | 635 |
531 // Update all the output data structures. | 636 // Update all the output data structures. |
532 // TODO(stephana): Evaluate whether the counts are really useful or if t
hey can be removed. | 637 // 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. | 638 // If we need them uncomment the following line and implement the corres
ponding function. |
534 //a.updateOutputCounts(labeledTestDigests) | 639 //a.updateOutputCounts(labeledTestDigests) |
535 | 640 |
536 // Update the tests that have changed and the status. | 641 // Update the tests that have changed and the status. |
537 » a.updateTestDetails(labeledTestDigests) | 642 » a.updateTestDetails(labeledTestDigests, state) |
538 » a.currentStatus = a.calcStatus(a.currentTile) | 643 » state.Status = calcStatus(state) |
539 } | 644 } |
540 | 645 |
541 // labelDigest assignes a label to the given digests based on the expectations. | 646 // labelDigest assignes a label to the given digests based on the expectations. |
542 // Its assumes that targetLabels are pre-initialized, usualy with UNTRIAGED, | 647 // 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 | 648 // because it will not change the label if the given test and digest cannot be |
544 // found. | 649 // found. |
545 func (a *Analyzer) labelDigests(testName string, digests []string, targetLabels
[]types.Label, expectations *expstorage.Expectations) { | 650 func labelDigests(testName string, digests []string, targetLabels []types.Label,
expectations *expstorage.Expectations) { |
546 for idx, digest := range digests { | 651 for idx, digest := range digests { |
547 if test, ok := expectations.Tests[testName]; ok { | 652 if test, ok := expectations.Tests[testName]; ok { |
548 if foundLabel, ok := test[digest]; ok { | 653 if foundLabel, ok := test[digest]; ok { |
549 targetLabels[idx] = foundLabel | 654 targetLabels[idx] = foundLabel |
550 } | 655 } |
551 } | 656 } |
552 } | 657 } |
553 } | 658 } |
554 | 659 |
555 // getUntriagedTestDetails returns the untriaged digests of a specific test that | 660 // getUntriagedTestDetails returns the untriaged digests of a specific test that |
556 // match the given query. In addition to the digests it returns the query | 661 // match the given query. In addition to the digests it returns the query |
557 // that was used to retrieve them. | 662 // that was used to retrieve them. |
558 func (a *Analyzer) getUntriagedTestDetails(query, effectiveQuery map[string][]st
ring, includeAllTests bool) map[string]map[string]*GUIUntriagedDigest { | 663 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) | 664 » traces, startCommitId, endCommitId, showHead := a.current.Index.query(qu
ery, effectiveQuery) |
560 endCommitId++ | 665 endCommitId++ |
561 | 666 |
562 if len(effectiveQuery) == 0 { | 667 if len(effectiveQuery) == 0 { |
563 return nil | 668 return nil |
564 } | 669 } |
565 | 670 |
566 » ret := make(map[string]map[string]*GUIUntriagedDigest, len(a.currentTest
Details.Tests)) | 671 » ret := make(map[string]map[string]*GUIUntriagedDigest, len(a.current.Tes
tDetails.Tests)) |
567 | 672 |
568 // This includes an empty list for tests that we have not found. | 673 // This includes an empty list for tests that we have not found. |
569 if includeAllTests { | 674 if includeAllTests { |
570 » » for _, testName := range a.currentIndex.getTestNames(query) { | 675 » » for _, testName := range a.current.Index.getTestNames(query) { |
571 ret[testName] = nil | 676 ret[testName] = nil |
572 } | 677 } |
573 } | 678 } |
574 | 679 |
575 if !showHead { | 680 if !showHead { |
576 for _, trace := range traces { | 681 for _, trace := range traces { |
577 testName := trace.Params[types.PRIMARY_KEY_FIELD] | 682 testName := trace.Params[types.PRIMARY_KEY_FIELD] |
578 » » » current := a.currentTestDetails.lookup(testName).Untriag
ed | 683 » » » current := a.current.TestDetails.lookup(testName).Untria
ged |
579 | 684 |
580 startIdx := sort.SearchInts(trace.CommitIds, startCommit
Id) | 685 startIdx := sort.SearchInts(trace.CommitIds, startCommit
Id) |
581 endIdx := sort.SearchInts(trace.CommitIds, endCommitId) | 686 endIdx := sort.SearchInts(trace.CommitIds, endCommitId) |
582 if (endIdx < len(trace.CommitIds)) && (trace.CommitIds[e
ndIdx] == endCommitId) { | 687 if (endIdx < len(trace.CommitIds)) && (trace.CommitIds[e
ndIdx] == endCommitId) { |
583 endIdx++ | 688 endIdx++ |
584 } | 689 } |
585 | 690 |
586 for idx := startIdx; idx < endIdx; idx++ { | 691 for idx := startIdx; idx < endIdx; idx++ { |
587 if trace.Labels[idx] == types.UNTRIAGED { | 692 if trace.Labels[idx] == types.UNTRIAGED { |
588 if found, ok := ret[testName]; !ok || fo
und == nil { | 693 if found, ok := ret[testName]; !ok || fo
und == nil { |
589 ret[testName] = make(map[string]
*GUIUntriagedDigest, len(current)) | 694 ret[testName] = make(map[string]
*GUIUntriagedDigest, len(current)) |
590 } | 695 } |
591 ret[testName][trace.Digests[idx]] = curr
ent[trace.Digests[idx]] | 696 ret[testName][trace.Digests[idx]] = curr
ent[trace.Digests[idx]] |
592 } | 697 } |
593 } | 698 } |
594 } | 699 } |
595 } else { | 700 } else { |
596 for _, trace := range traces { | 701 for _, trace := range traces { |
597 lastIdx := len(trace.Labels) - 1 | 702 lastIdx := len(trace.Labels) - 1 |
598 if (lastIdx >= 0) && (trace.Labels[lastIdx] == types.UNT
RIAGED) { | 703 if (lastIdx >= 0) && (trace.Labels[lastIdx] == types.UNT
RIAGED) { |
599 testName := trace.Params[types.PRIMARY_KEY_FIELD
] | 704 testName := trace.Params[types.PRIMARY_KEY_FIELD
] |
600 » » » » current := a.currentTestDetails.lookup(testName)
.Untriaged | 705 » » » » current := a.current.TestDetails.lookup(testName
).Untriaged |
601 if found, ok := ret[testName]; !ok || found == n
il { | 706 if found, ok := ret[testName]; !ok || found == n
il { |
602 ret[testName] = map[string]*GUIUntriaged
Digest{} | 707 ret[testName] = map[string]*GUIUntriaged
Digest{} |
603 } | 708 } |
604 ret[testName][trace.Digests[lastIdx]] = current[
trace.Digests[lastIdx]] | 709 ret[testName][trace.Digests[lastIdx]] = current[
trace.Digests[lastIdx]] |
605 } | 710 } |
606 } | 711 } |
607 } | 712 } |
608 | 713 |
609 return ret | 714 return ret |
610 } | 715 } |
611 | 716 |
612 // getSubTile queries the index and returns a LabeledTile that contains the | 717 // 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 | 718 // set of found traces. It also returns the subset of 'query' that contained |
614 // valid parameters and values. | 719 // valid parameters and values. |
615 // If the returned query is empty the first return value is set to Nil, | 720 // 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. | 721 // because now valid filter parameters were found in the query. |
617 func (a *Analyzer) getSubTile(query map[string][]string) (*LabeledTile, map[stri
ng][]string) { | 722 func (a *Analyzer) getSubTile(query map[string][]string) (*LabeledTile, map[stri
ng][]string) { |
618 // TODO(stephana): Use the commitStart and commitEnd return values | 723 // TODO(stephana): Use the commitStart and commitEnd return values |
619 // if we really need this method. GetTileCounts and getSubTile might be | 724 // if we really need this method. GetTileCounts and getSubTile might be |
620 // removed. | 725 // removed. |
621 effectiveQuery := make(map[string][]string, len(query)) | 726 effectiveQuery := make(map[string][]string, len(query)) |
622 » traces, _, _, _ := a.currentIndex.query(query, effectiveQuery) | 727 » traces, _, _, _ := a.current.Index.query(query, effectiveQuery) |
623 if len(effectiveQuery) == 0 { | 728 if len(effectiveQuery) == 0 { |
624 return nil, effectiveQuery | 729 return nil, effectiveQuery |
625 } | 730 } |
626 | 731 |
627 result := NewLabeledTile() | 732 result := NewLabeledTile() |
628 » result.Commits = a.currentTile.Commits | 733 » result.Commits = a.current.Tile.Commits |
629 | 734 |
630 result.Traces = map[string][]*LabeledTrace{} | 735 result.Traces = map[string][]*LabeledTrace{} |
631 for _, t := range traces { | 736 for _, t := range traces { |
632 testName := t.Params[types.PRIMARY_KEY_FIELD] | 737 testName := t.Params[types.PRIMARY_KEY_FIELD] |
633 if _, ok := result.Traces[testName]; !ok { | 738 if _, ok := result.Traces[testName]; !ok { |
634 result.Traces[testName] = []*LabeledTrace{} | 739 result.Traces[testName] = []*LabeledTrace{} |
635 } | 740 } |
636 result.Traces[testName] = append(result.Traces[testName], t) | 741 result.Traces[testName] = append(result.Traces[testName], t) |
637 } | 742 } |
638 | 743 |
639 return result, effectiveQuery | 744 return result, effectiveQuery |
640 } | 745 } |
641 | 746 |
642 // getOutputCounts derives the output counts from the given labeled tile. | 747 // getOutputCounts derives the output counts from the given labeled tile. |
643 func (a *Analyzer) getOutputCounts(labeledTile *LabeledTile) *GUITileCounts { | 748 func (a *Analyzer) getOutputCounts(labeledTile *LabeledTile, index *LabeledTileI
ndex) *GUITileCounts { |
644 glog.Info("Starting to process output counts.") | 749 glog.Info("Starting to process output counts.") |
645 // Stores the aggregated counts of a tile for each test. | 750 // Stores the aggregated counts of a tile for each test. |
646 tileCountsMap := make(map[string]*LabelCounts, len(labeledTile.Traces)) | 751 tileCountsMap := make(map[string]*LabelCounts, len(labeledTile.Traces)) |
647 | 752 |
648 // Overall aggregated counts over all tests. | 753 // Overall aggregated counts over all tests. |
649 overallAggregates := newLabelCounts(len(labeledTile.Commits)) | 754 overallAggregates := newLabelCounts(len(labeledTile.Commits)) |
650 | 755 |
651 updateCounts(labeledTile, tileCountsMap, overallAggregates) | 756 updateCounts(labeledTile, tileCountsMap, overallAggregates) |
652 | 757 |
653 // TODO (stephana): Factor out human.FlotTickMarks and move it from | 758 // TODO (stephana): Factor out human.FlotTickMarks and move it from |
654 // perf to the shared go library. | 759 // perf to the shared go library. |
655 // Generate the tickmarks for the commits. | 760 // Generate the tickmarks for the commits. |
656 ts := make([]int64, 0, len(labeledTile.Commits)) | 761 ts := make([]int64, 0, len(labeledTile.Commits)) |
657 for _, c := range labeledTile.Commits { | 762 for _, c := range labeledTile.Commits { |
658 if c.CommitTime != 0 { | 763 if c.CommitTime != 0 { |
659 ts = append(ts, c.CommitTime) | 764 ts = append(ts, c.CommitTime) |
660 } | 765 } |
661 } | 766 } |
662 | 767 |
663 tileCounts := &GUITileCounts{ | 768 tileCounts := &GUITileCounts{ |
664 Commits: labeledTile.Commits, | 769 Commits: labeledTile.Commits, |
665 Ticks: human.FlotTickMarks(ts), | 770 Ticks: human.FlotTickMarks(ts), |
666 Aggregated: overallAggregates, | 771 Aggregated: overallAggregates, |
667 Counts: tileCountsMap, | 772 Counts: tileCountsMap, |
668 » » AllParams: a.currentIndex.getAllParams(nil), | 773 » » AllParams: index.getAllParams(nil), |
669 } | 774 } |
670 | 775 |
671 glog.Info("Done processing output counts.") | 776 glog.Info("Done processing output counts.") |
672 | 777 |
673 return tileCounts | 778 return tileCounts |
674 } | 779 } |
675 | 780 |
676 func updateCounts(labeledTile *LabeledTile, tileCountsMap map[string]*LabelCount
s, overallAggregates *LabelCounts) { | 781 func updateCounts(labeledTile *LabeledTile, tileCountsMap map[string]*LabelCount
s, overallAggregates *LabelCounts) { |
677 for testName, testTraces := range labeledTile.Traces { | 782 for testName, testTraces := range labeledTile.Traces { |
678 acc := newLabelCounts(len(labeledTile.Commits)) | 783 acc := newLabelCounts(len(labeledTile.Commits)) |
(...skipping 14 matching lines...) Expand all Loading... |
693 tileCountsMap[testName] = acc | 798 tileCountsMap[testName] = acc |
694 | 799 |
695 // Add the aggregates fro this test to the overall aggregates. | 800 // Add the aggregates fro this test to the overall aggregates. |
696 for idx, u := range acc.Unt { | 801 for idx, u := range acc.Unt { |
697 overallAggregates.Unt[idx] += u | 802 overallAggregates.Unt[idx] += u |
698 overallAggregates.Pos[idx] += acc.Pos[idx] | 803 overallAggregates.Pos[idx] += acc.Pos[idx] |
699 overallAggregates.Neg[idx] += acc.Neg[idx] | 804 overallAggregates.Neg[idx] += acc.Neg[idx] |
700 } | 805 } |
701 } | 806 } |
702 } | 807 } |
OLD | NEW |