1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5'use strict';
6
7base.require('guid');
8base.require('range');
9
10/**
11 * @fileoverview Provides the Counter class.
12 */
13base.exportTo('tracing.model', function() {
14
15  /**
16   * Stores all the samples for a given counter.
17   * @constructor
18   */
19  function Counter(parent, id, category, name) {
20    this.guid_ = tracing.GUID.allocate();
21
22    this.parent = parent;
23    this.id = id;
24    this.category = category || '';
25    this.name = name;
26    this.seriesNames = [];
27    this.seriesColors = [];
28    this.timestamps = [];
29    this.samples = [];
30    this.bounds = new base.Range();
31  }
32
33  Counter.prototype = {
34    __proto__: Object.prototype,
35
36    /*
37     * @return {Number} A globally unique identifier for this counter.
38     */
39    get guid() {
40      return this.guid_;
41    },
42
43    toJSON: function() {
44      var obj = new Object();
45      var keys = Object.keys(this);
46      for (var i = 0; i < keys.length; i++) {
47        var key = keys[i];
48        if (typeof this[key] == 'function')
49          continue;
50        if (key == 'parent') {
51          obj[key] = this[key].guid;
52          continue;
53        }
54        obj[key] = this[key];
55      }
56      return obj;
57    },
58
59    get numSeries() {
60      return this.seriesNames.length;
61    },
62
63    get numSamples() {
64      return this.timestamps.length;
65    },
66
67    getSampleValue: function(index, seriesIndex) {
68      return this.samples[index * this.numSeries + seriesIndex];
69    },
70
71    /**
72     * Obtains min, max, avg, values, start, and end for different series for
73     * a given counter
74     *     getSampleStatistics([0,1])
75     * The statistics objects that this returns are an array of objects, one
76     * object for each series for the counter in the form:
77     * {min: minVal, max: maxVal, avg: avgVal, start: startVal, end: endVal}
78     *
79     * @param {Array.<Number>} Indices to summarize.
80     * @return {Object} An array of statistics. Each element in the array
81     * has data for one of the series in the selected counter.
82     */
83    getSampleStatistics: function(sampleIndices) {
84      sampleIndices.sort();
85      var sampleIndex = this.sampleIndex;
86      var numSeries = this.numSeries;
87      var numSamples = this.numSamples;
88
89      var ret = [];
90
91      for (var i = 0; i < numSeries; ++i) {
92        var sum = 0;
93        var min = Number.MAX_VALUE;
94        var max = -Number.MAX_VALUE;
95        for (var j = 0; j < sampleIndices.length; j++) {
96          var x = sampleIndices[j];
97          sum += this.getSampleValue(x, i);
98          min = Math.min(this.getSampleValue(x, i), min);
99          max = Math.max(this.getSampleValue(x, i), max);
100        }
101        var avg = sum / sampleIndices.length;
102        var start = this.getSampleValue(sampleIndices[0], i);
103        var end = this.getSampleValue(
104            sampleIndices[sampleIndices.length - 1], i);
105
106        ret.push({min: min,
107          max: max,
108          avg: avg,
109          start: start,
110          end: end});
111      }
112      return ret;
113    },
114
115    /**
116     * Shifts all the timestamps inside this counter forward by the amount
117     * specified.
118     */
119    shiftTimestampsForward: function(amount) {
120      for (var sI = 0; sI < this.timestamps.length; sI++)
121        this.timestamps[sI] = (this.timestamps[sI] + amount);
122    },
123
124    /**
125     * Updates the bounds for this counter based on the samples it contains.
126     */
127    updateBounds: function() {
128      if (this.seriesNames.length != this.seriesColors.length)
129        throw new Error('seriesNames.length must match seriesColors.length');
130      if (this.numSeries * this.numSamples != this.samples.length)
131        throw new Error('samples.length must be a multiple of numSamples.');
132
133      this.totals = [];
134      this.maxTotal = 0;
135      this.bounds.reset();
136      if (this.samples.length == 0)
137        return;
138
139      this.bounds.addValue(this.timestamps[0]);
140      this.bounds.addValue(this.timestamps[this.timestamps.length - 1]);
141
142      var numSeries = this.numSeries;
143      var maxTotal = -Infinity;
144      for (var i = 0; i < this.timestamps.length; i++) {
145        var total = 0;
146        for (var j = 0; j < numSeries; j++) {
147          total += this.samples[i * numSeries + j];
148          this.totals.push(total);
149        }
150        if (total > maxTotal)
151          maxTotal = total;
152      }
153      this.maxTotal = maxTotal;
154    }
155
156  };
157
158  /**
159   * Comparison between counters that orders by parent.compareTo, then name.
160   */
161  Counter.compare = function(x, y) {
162    var tmp = x.parent.compareTo(y);
163    if (tmp != 0)
164      return tmp;
165    var tmp = x.name.localeCompare(y.name);
166    if (tmp == 0)
167      return x.tid - y.tid;
168    return tmp;
169  };
170
171  return {
172    Counter: Counter
173  };
174});
175