Index: golden/go/analysis/analysis.go |
diff --git a/golden/go/analysis/analysis.go b/golden/go/analysis/analysis.go |
index ede75ef81bb61ee232d13f56b865c0f9c3294238..4710eddc6831664415981aff4d26106f9f9c6da1 100644 |
--- a/golden/go/analysis/analysis.go |
+++ b/golden/go/analysis/analysis.go |
@@ -14,13 +14,13 @@ import ( |
ptypes "skia.googlesource.com/buildbot.git/perf/go/types" |
) |
-// Stores a Trace with labels and digests in memory. CommitIds, Digests and |
+// LabeledTrace stores a Trace with labels and digests. CommitIds, Digests and |
// Labels are of the same length, identical indices refer to the same digest. |
type LabeledTrace struct { |
- Params map[string]string `json:"params"` |
- CommitIds []int `json:"commitIds"` |
- Digests []string `json:"digests"` |
- Labels []types.Label `json:"labels` |
+ Params map[string]string |
+ CommitIds []int |
+ Digests []string |
+ Labels []types.Label |
} |
func NewLabeledTrace(params map[string]string, capacity int) *LabeledTrace { |
@@ -32,21 +32,24 @@ func NewLabeledTrace(params map[string]string, capacity int) *LabeledTrace { |
} |
} |
-// Add the given tripples of commitIds, digests and labels to this LabeledTrace. |
+// addLabledDigests adds the given tripples of commitIds, digests and labels to this LabeledTrace. |
func (lt *LabeledTrace) addLabeledDigests(commitIds []int, digests []string, labels []types.Label) { |
lt.CommitIds = append(lt.CommitIds, commitIds...) |
lt.Digests = append(lt.Digests, digests...) |
lt.Labels = append(lt.Labels, labels...) |
} |
-// Aggregates the Traces in tile and provides the commits that the |
-// CommitIds in LabeledTrace refer to. |
+// LabeledTile aggregates the traces of a tile and provides a slice of commits |
+// that the commitIds in LabeledTrace refer to. |
+// LabeledTile and LabeledTrace store the cannonical information |
+// extracted from the unterlying tile store. The (redundant) output data is |
+// derived from these. |
type LabeledTile struct { |
- Commits []*ptypes.Commit `json:"commits"` |
+ Commits []*ptypes.Commit |
// Traces are indexed by the primary key (test name). This is somewhat |
// redundant, but this also output format. |
- Traces map[string][]*LabeledTrace `json:"traces"` |
+ Traces map[string][]*LabeledTrace |
} |
func NewLabeledTile() *LabeledTile { |
@@ -56,9 +59,9 @@ func NewLabeledTile() *LabeledTile { |
} |
} |
-// Utility function that returns the testName and a labeled trace for the given |
-// Trace (read from a TileStore). If the LabeledTrace does not exist it will be |
-// added. |
+// getLabeledTrace is a utility function that returns the testName and a labeled |
+// trace for the given trace (read from a TileStore). If the LabeledTrace does |
+// not exist it will be added. |
func (t *LabeledTile) getLabeledTrace(trace ptypes.Trace) (string, *LabeledTrace) { |
params := trace.Params() |
pKey := params[types.PRIMARY_KEY_FIELD] |
@@ -81,16 +84,59 @@ func (t *LabeledTile) getLabeledTrace(trace ptypes.Trace) (string, *LabeledTrace |
return pKey, newLT |
} |
-// Analyzer continuously manages the tasks, like pollint for new traces |
-// on disk, etc. |
+// LabelCounts is an output type to hold counts for classification labels. |
+type LabelCounts struct { |
+ Unt int `json:"unt"` // Untriaged |
+ Pos int `json:"pos"` // Positive |
+ Neg int `json:"neg"` // Negative |
+} |
+ |
+// GUITileCounts is an output type for the aggregated label counts. |
+type GUITileCounts struct { |
+ Commits []*ptypes.Commit `json:"commits"` |
+ Counts map[string][]LabelCounts `json:"counts"` |
+} |
+ |
+// GUITestCounts is an output type for a single test that contains the |
+// aggregated counts over all traces and also the individual traces |
+// and their labels. |
+type GUITestCounts struct { |
+ Commits []*ptypes.Commit `json:"commits"` |
+ Aggregated []LabelCounts `json:"aggregated"` |
+ Traces []*GUILabeledTrace `json:"traces"` |
+} |
+ |
+// GUILabeledTrace is an output type for the labels of a trace. |
+type GUILabeledTrace struct { |
+ Params map[string]string `json:"params"` |
+ |
+ // List of commitId and Label pairs. |
+ Labels []IdLabel `json:"labels"` |
+} |
+ |
+// IdLabel stores the commitId and the label for one entry in a trace. |
+type IdLabel struct { |
+ Id int `json:"id"` |
+ Label int `json:"label"` |
+} |
+ |
+// Analyzer continuously manages tasks like polling for new traces |
+// on disk and generating diffs between images. It is the primary interface |
+// to be called by the HTTP frontend. |
type Analyzer struct { |
expStore expstorage.ExpectationsStore |
diffStore diff.DiffStore |
tileStore ptypes.TileStore |
+ // Canonical data structure to hold our information about commits, digests |
+ // and labels. |
currentTile *LabeledTile |
- // Lock to protect the expectations and the current labeled tile. |
+ // Output data structures that are derived from currentTile. |
+ currentTileCounts *GUITileCounts |
+ currentTestCounts map[string]*GUITestCounts |
+ |
+ // Lock to protect the expectations and current* variables. |
mutex sync.Mutex |
} |
@@ -107,23 +153,29 @@ func NewAnalyzer(expStore expstorage.ExpectationsStore, tileStore ptypes.TileSto |
return result |
} |
-// Returns an entire Tile which is a collection of 'traces' over a series of |
-// of commits. Each trace contains the digests and their labels based on |
-// out knowledge base about digests (expectations). |
-func (a *Analyzer) GetLabeledTile() *LabeledTile { |
+// GetTileCounts returns an entire Tile which is a collection of 'traces' over |
+// a series of commits. Each trace contains the digests and their labels |
+// based on our knowledge about digests (expectations). |
+func (a *Analyzer) GetTileCounts() (*GUITileCounts, error) { |
a.mutex.Lock() |
defer a.mutex.Unlock() |
- return a.currentTile |
+ return a.currentTileCounts, nil |
} |
-func (a *Analyzer) GetLabeledTraces(testName string) []*LabeledTrace { |
+// GetTestCounts returns the classification counts for a specific tests. |
+func (a *Analyzer) GetTestCounts(testName string) (*GUITestCounts, error) { |
a.mutex.Lock() |
defer a.mutex.Unlock() |
- return a.currentTile.Traces[testName] |
+ // TODO (stephana): This should return any error that occurs during reading |
+ // of the tiles. We would rather get an error on the front-end than |
+ // look at outdated data. |
+ return a.currentTestCounts[testName], nil |
} |
+// SetDigestLabels sets the labels for the given digest and records the user |
+// that made the classification. |
func (a *Analyzer) SetDigestLabels(labeledTestDigests map[string]types.TestClassification, userId string) (map[string][]*LabeledTrace, error) { |
a.mutex.Lock() |
defer a.mutex.Unlock() |
@@ -139,10 +191,11 @@ func (a *Analyzer) SetDigestLabels(labeledTestDigests map[string]types.TestClass |
// Let's update our knowledge of the labels. |
updatedTraces := a.relabelTraces(labeledTestDigests) |
+ |
return updatedTraces, nil |
} |
-// Main loop. |
+// loop is the main event loop. |
func (a *Analyzer) loop(timeBetweenPolls time.Duration) { |
// The number of times we've successfully loaded and processed a tile. |
runsCounter := metrics.NewRegisteredCounter("analysis.runs", metrics.DefaultRegistry) |
@@ -150,7 +203,7 @@ func (a *Analyzer) loop(timeBetweenPolls time.Duration) { |
// The number of times an error has ocurred when trying to load a tile. |
errorTileLoadingCounter := metrics.NewRegisteredCounter("analysis.errors", metrics.DefaultRegistry) |
- for { |
+ for _ = range time.Tick(timeBetweenPolls) { |
glog.Info("Reading tiles ... ") |
// Load the tile and process it. |
@@ -160,18 +213,21 @@ func (a *Analyzer) loop(timeBetweenPolls time.Duration) { |
errorTileLoadingCounter.Inc(1) |
} else { |
newLabeledTile := a.processTile(tile) |
+ newTileCounts, newTestCounts := a.getOutputCounts(newLabeledTile) |
+ |
a.mutex.Lock() |
a.currentTile = newLabeledTile |
+ a.currentTileCounts = newTileCounts |
+ a.currentTestCounts = newTestCounts |
a.mutex.Unlock() |
} |
+ glog.Info("Done processing tiles.") |
runsCounter.Inc(1) |
- |
- // Sleep for a while until the next poll. |
- time.Sleep(timeBetweenPolls) |
} |
} |
-// Process a tile segment and add it to the currentTile. |
+// processTile processes the last two tiles and updates the cannonical and |
+// output data structures. |
func (a *Analyzer) processTile(tile *ptypes.Tile) *LabeledTile { |
result := NewLabeledTile() |
@@ -208,8 +264,8 @@ func (a *Analyzer) processTile(tile *ptypes.Tile) *LabeledTile { |
return result |
} |
-// Run over the traces in of the tiles that have changed and label them |
-// according to our current expecatations. |
+// relabelTraces iterates over the traces in of the tiles that have changed and |
+// labels them according to our current expecatations. |
func (a *Analyzer) relabelTraces(labeledTestDigests map[string]types.TestClassification) map[string][]*LabeledTrace { |
result := map[string][]*LabeledTrace{} |
@@ -248,3 +304,53 @@ func (a *Analyzer) labelDigests(testName string, digests []string, targetLabels |
return nil |
} |
+ |
+// getOutputCounts derives the output counts from the given labeled tile. |
+func (a *Analyzer) getOutputCounts(labeledTile *LabeledTile) (*GUITileCounts, map[string]*GUITestCounts) { |
+ // Stores the aggregated counts of a tile for each test. |
+ tileCountsMap := make(map[string][]LabelCounts, len(labeledTile.Traces)) |
+ |
+ // Stores the aggregated counts for each test and individual trace information. |
+ testCountsMap := make(map[string]*GUITestCounts, len(labeledTile.Traces)) |
+ |
+ for testName, testTraces := range labeledTile.Traces { |
+ acc := make([]LabelCounts, len(labeledTile.Commits)) |
+ tempTraces := make([]*GUILabeledTrace, 0, len(testTraces)) |
+ |
+ for _, oneTrace := range testTraces { |
+ tempTrace := &GUILabeledTrace{ |
+ Params: oneTrace.Params, |
+ Labels: make([]IdLabel, len(oneTrace.CommitIds)), |
+ } |
+ |
+ for i, ci := range oneTrace.CommitIds { |
+ switch oneTrace.Labels[i] { |
+ case types.UNTRIAGED: |
+ acc[ci].Unt++ |
+ case types.POSITIVE: |
+ acc[ci].Pos++ |
+ case types.NEGATIVE: |
+ acc[ci].Neg++ |
+ } |
+ tempTrace.Labels[i].Id = ci |
+ tempTrace.Labels[i].Label = int(oneTrace.Labels[i]) |
+ } |
+ |
+ tempTraces = append(tempTraces, tempTrace) |
+ } |
+ |
+ tileCountsMap[testName] = acc |
+ testCountsMap[testName] = &GUITestCounts{ |
+ Commits: labeledTile.Commits, |
+ Aggregated: acc, |
+ Traces: tempTraces, |
+ } |
+ } |
+ |
+ tileCounts := &GUITileCounts{ |
+ Commits: labeledTile.Commits, |
+ Counts: tileCountsMap, |
+ } |
+ |
+ return tileCounts, testCountsMap |
+} |