| 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 |