1/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#ifndef ANDROID_EVENT_METRIC_H_
17#define ANDROID_EVENT_METRIC_H_
18
19#include <media/MediaAnalyticsItem.h>
20#include <utils/Timers.h>
21
22namespace android {
23
24// This is a simple holder for the statistics recorded in EventMetric.
25struct EventStatistics {
26  // The count of times the event occurred.
27  int64_t count;
28
29  // The minimum and maximum values recorded in the Record method.
30  double min;
31  double max;
32
33  // The average (mean) of all values recorded.
34  double mean;
35  // The sum of squared devation. Variance can be calculated from
36  // this value.
37  //    var = sum_squared_deviation / count;
38  double sum_squared_deviation;
39};
40
41// The EventMetric class is used to accumulate stats about an event over time.
42// A common use case is to track clock timings for a method call or operation.
43// An EventMetric can break down stats by a dimension specified by the
44// application. E.g. an application may want to track counts broken out by
45// error code or the size of some parameter.
46//
47// Example:
48//
49//   struct C {
50//     status_t DoWork() {
51//       unsigned long start_time = now();
52//       status_t result;
53//
54//       // DO WORK and determine result;
55//
56//       work_event_.Record(now() - start_time, result);
57//
58//       return result;
59//     }
60//     EventMetric<status_t> work_event_;
61//   };
62//
63//   C c;
64//   c.DoWork();
65//
66//   std::map<int, int64_t> values;
67//   metric.ExportValues(
68//       [&] (int attribute_value, int64_t value) {
69//            values[attribute_value] = value;
70//       });
71//   // Do something with the exported stat.
72//
73template<typename AttributeType>
74class EventMetric {
75 public:
76  // Instantiate the counter with the given metric name and
77  // attribute names. |attribute_names| must not be null.
78  EventMetric(
79      const std::string& metric_name,
80      const std::string& attribute_name)
81          : metric_name_(metric_name),
82            attribute_name_(attribute_name) {}
83
84  // Increment the count of times the operation occurred with this
85  // combination of attributes.
86  void Record(double value, AttributeType attribute) {
87    if (values_.find(attribute) != values_.end()) {
88      EventStatistics* stats = values_[attribute].get();
89      // Using method of provisional means.
90      double deviation = value - stats->mean;
91      stats->mean = stats->mean + (deviation / stats->count);
92      stats->sum_squared_deviation =
93          stats->sum_squared_deviation + (deviation * (value - stats->mean));
94      stats->count++;
95
96      stats->min = stats->min < value ? stats->min : value;
97      stats->max = stats->max > value ? stats->max : value;
98    } else {
99      std::unique_ptr<EventStatistics> stats =
100          std::make_unique<EventStatistics>();
101      stats->count = 1;
102      stats->min = value;
103      stats->max = value;
104      stats->mean = value;
105      stats->sum_squared_deviation = 0;
106      values_[attribute] = std::move(stats);
107    }
108  };
109
110  // Export the metrics to the provided |function|. Each value for Attribute
111  // has a separate set of stats. As such, |function| will be called once per
112  // value of Attribute.
113  void ExportValues(
114      std::function<void (const AttributeType&,
115                          const EventStatistics&)> function) const {
116    for (auto it = values_.begin(); it != values_.end(); it++) {
117      function(it->first, *(it->second));
118    }
119  }
120
121  const std::string& metric_name() const { return metric_name_; };
122
123 private:
124  const std::string metric_name_;
125  const std::string attribute_name_;
126  std::map<AttributeType, std::unique_ptr<struct EventStatistics>> values_;
127};
128
129// The EventTimer is a supporting class for EventMetric instances that are used
130// to time methods. The EventTimer starts a timer when first in scope, and
131// records the timing when exiting scope.
132//
133// Example:
134//
135// EventMetric<int> my_metric;
136//
137// {
138//   EventTimer<int> my_timer(&my_metric);
139//   // Set the attribute to associate with this timing.
140//   my_timer.SetAttribtue(42);
141//
142//   // Do some work that you want to time.
143//
144// }  // The EventTimer destructor will record the the timing in my_metric;
145//
146template<typename AttributeType>
147class EventTimer {
148 public:
149  explicit EventTimer(EventMetric<AttributeType>* metric)
150      :start_time_(systemTime()), metric_(metric) {
151  }
152
153  virtual ~EventTimer() {
154    if (metric_) {
155      metric_->Record(ns2us(systemTime() - start_time_), attribute_);
156    }
157  }
158
159  // Set the attribute to associate with this timing. E.g. this can be used to
160  // record the return code from the work that was timed.
161  void SetAttribute(const AttributeType& attribute) {
162    attribute_ = attribute;
163  }
164
165 protected:
166  // Visible for testing only.
167  nsecs_t start_time_;
168
169 private:
170  EventMetric<AttributeType>* metric_;
171  AttributeType attribute_;
172};
173
174}  // namespace android
175
176#endif  // ANDROID_EVENT_METRIC_H_
177