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#include "chrome/browser/ui/webui/performance_monitor/performance_monitor_ui_util.h"
6
7#include <algorithm>
8
9#include "base/time/time.h"
10#include "chrome/browser/performance_monitor/metric.h"
11
12namespace performance_monitor {
13
14namespace {
15
16// Sorts the vector and returns the median. We don't need to sort it, but it is
17// a by-product of finding the median, and allows us to pass a pointer, rather
18// than construct another array.
19double SortAndGetMedian(std::vector<double>* values) {
20  size_t size = values->size();
21  if (!size)
22    return 0.0;
23
24  std::sort(values->begin(), values->end());
25  return size % 2 == 0 ?
26      (values->at(size / 2) + values->at((size / 2) - 1)) / 2.0 :
27      values->at(size / 2);
28}
29
30}  // namespace
31
32Aggregator::Aggregator() {
33}
34
35Aggregator::~Aggregator() {
36}
37
38scoped_ptr<VectorOfMetricVectors> Aggregator::AggregateMetrics(
39    MetricType metric_type,
40    const Database::MetricVector* metrics,
41    const base::Time& start,
42    const std::vector<TimeRange>& intervals,
43    const base::TimeDelta& resolution) {
44  scoped_ptr<VectorOfMetricVectors> results(new VectorOfMetricVectors());
45
46  Database::MetricVector::const_iterator metric = metrics->begin();
47  while (metric != metrics->end() && metric->time < start)
48    ++metric;
49
50  // For each interval, advance the metric to the start of the interval, and
51  // append a metric vector for the aggregated data within that interval,
52  // according to the appropriate strategy.
53  for (std::vector<TimeRange>::const_iterator interval = intervals.begin();
54       interval != intervals.end(); ++interval) {
55    while (metric != metrics->end() && metric->time < interval->start)
56      ++metric;
57
58    results->push_back(*AggregateInterval(
59        metric_type,
60      &metric,
61      metrics->end(),
62      interval == intervals.begin() ? start : interval->start,
63      interval->end,
64      resolution));
65  }
66
67  return results.Pass();
68}
69
70scoped_ptr<Database::MetricVector> NoAggregation::AggregateInterval(
71    MetricType metric_type,
72    Database::MetricVector::const_iterator* metric,
73    const Database::MetricVector::const_iterator& metric_end,
74    const base::Time& time_start,
75    const base::Time& time_end,
76    const base::TimeDelta& resolution) {
77  scoped_ptr<Database::MetricVector> aggregated_series(
78      new Database::MetricVector());
79
80  for (; *metric != metric_end && (*metric)->time <= time_end; ++(*metric))
81    aggregated_series->push_back(**metric);
82
83  return aggregated_series.Pass();
84}
85
86scoped_ptr<Database::MetricVector> MedianAggregation::AggregateInterval(
87    MetricType metric_type,
88    Database::MetricVector::const_iterator* metric,
89    const Database::MetricVector::const_iterator& metric_end,
90    const base::Time& time_start,
91    const base::Time& time_end,
92    const base::TimeDelta& resolution) {
93  scoped_ptr<Database::MetricVector> aggregated_series(
94      new Database::MetricVector());
95  base::Time window_start = time_start;
96
97  while (*metric != metric_end && (*metric)->time <= time_end) {
98    std::vector<double> values;
99    while (*metric != metric_end &&
100           (*metric)->time <= time_end &&
101           (*metric)->time < window_start + resolution) {
102      values.push_back((*metric)->value);
103      ++(*metric);
104    }
105
106    if (!values.empty()) {
107      aggregated_series->push_back(Metric(metric_type,
108                                          window_start + resolution,
109                                          SortAndGetMedian(&values)));
110    }
111    window_start += resolution;
112  }
113
114  return aggregated_series.Pass();
115}
116
117scoped_ptr<Database::MetricVector> MeanAggregation::AggregateInterval(
118    MetricType metric_type,
119    Database::MetricVector::const_iterator* metric,
120    const Database::MetricVector::const_iterator& metric_end,
121    const base::Time& time_start,
122    const base::Time& time_end,
123    const base::TimeDelta& resolution) {
124  scoped_ptr<Database::MetricVector> aggregated_series(
125      new Database::MetricVector());
126
127  while (*metric != metric_end && (*metric)->time <= time_end) {
128    // Finds the beginning of the next aggregation window.
129    int64 window_offset = ((*metric)->time - time_start) / resolution;
130    base::Time window_start = time_start + (window_offset * resolution);
131    base::Time window_end = window_start + resolution;
132    base::Time last_sample_time = window_start;
133    double integrated = 0.0;
134    double metric_value = 0.0;
135
136    // Aggregate the step function defined by the Metrics in |metrics|.
137    while (*metric != metric_end && (*metric)->time <= window_end) {
138      metric_value = (*metric)->value;
139      integrated += metric_value *
140                    ((*metric)->time - last_sample_time).InSecondsF();
141      last_sample_time = (*metric)->time;
142      ++(*metric);
143    }
144    if (*metric != metric_end)
145      metric_value = (*metric)->value;
146
147    // If the window splits an area of the step function, split the
148    // aggregation at the end of the window.
149    integrated += metric_value * (window_end - last_sample_time).InSecondsF();
150    double average = integrated / resolution.InSecondsF();
151    aggregated_series->push_back(Metric(metric_type, window_end, average));
152  }
153
154  return aggregated_series.Pass();
155}
156
157double GetConversionFactor(UnitDetails from, UnitDetails to) {
158  if (from.measurement_type != to.measurement_type) {
159    LOG(ERROR) << "Invalid conversion requested";
160    return 0.0;
161  }
162
163  return static_cast<double>(from.amount_in_base_units) /
164      static_cast<double>(to.amount_in_base_units);
165}
166
167scoped_ptr<VectorOfMetricVectors> AggregateMetric(
168    MetricType type,
169    const Database::MetricVector* metrics,
170    const base::Time& start,
171    const std::vector<TimeRange>& intervals,
172    const base::TimeDelta& resolution,
173    AggregationMethod method) {
174  if (!metrics || intervals.empty())
175    return scoped_ptr<VectorOfMetricVectors>();
176
177  CHECK(resolution > base::TimeDelta());
178
179  switch (method) {
180    case AGGREGATION_METHOD_NONE:
181      return NoAggregation().AggregateMetrics(
182          type, metrics, start, intervals, resolution);
183    case AGGREGATION_METHOD_MEDIAN:
184      return MedianAggregation().AggregateMetrics(
185          type, metrics, start, intervals, resolution);
186    case AGGREGATION_METHOD_MEAN:
187      return MeanAggregation().AggregateMetrics(
188          type, metrics, start, intervals, resolution);
189    default:
190      NOTREACHED();
191      return scoped_ptr<VectorOfMetricVectors>();
192  }
193}
194
195}  // namespace performance_monitor
196