/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_EVENT_METRIC_H_
#define ANDROID_EVENT_METRIC_H_
#include <media/MediaAnalyticsItem.h>
#include <utils/Timers.h>
namespace android {
// This is a simple holder for the statistics recorded in EventMetric.
struct EventStatistics {
// The count of times the event occurred.
int64_t count;
// The minimum and maximum values recorded in the Record method.
double min;
double max;
// The average (mean) of all values recorded.
double mean;
// The sum of squared devation. Variance can be calculated from
// this value.
// var = sum_squared_deviation / count;
double sum_squared_deviation;
};
// The EventMetric class is used to accumulate stats about an event over time.
// A common use case is to track clock timings for a method call or operation.
// An EventMetric can break down stats by a dimension specified by the
// application. E.g. an application may want to track counts broken out by
// error code or the size of some parameter.
//
// Example:
//
// struct C {
// status_t DoWork() {
// unsigned long start_time = now();
// status_t result;
//
// // DO WORK and determine result;
//
// work_event_.Record(now() - start_time, result);
//
// return result;
// }
// EventMetric<status_t> work_event_;
// };
//
// C c;
// c.DoWork();
//
// std::map<int, int64_t> values;
// metric.ExportValues(
// [&] (int attribute_value, int64_t value) {
// values[attribute_value] = value;
// });
// // Do something with the exported stat.
//
template<typename AttributeType>
class EventMetric {
public:
// Instantiate the counter with the given metric name and
// attribute names. |attribute_names| must not be null.
EventMetric(
const std::string& metric_name,
const std::string& attribute_name)
: metric_name_(metric_name),
attribute_name_(attribute_name) {}
// Increment the count of times the operation occurred with this
// combination of attributes.
void Record(double value, AttributeType attribute) {
if (values_.find(attribute) != values_.end()) {
EventStatistics* stats = values_[attribute].get();
// Using method of provisional means.
double deviation = value - stats->mean;
stats->mean = stats->mean + (deviation / stats->count);
stats->sum_squared_deviation =
stats->sum_squared_deviation + (deviation * (value - stats->mean));
stats->count++;
stats->min = stats->min < value ? stats->min : value;
stats->max = stats->max > value ? stats->max : value;
} else {
std::unique_ptr<EventStatistics> stats =
std::make_unique<EventStatistics>();
stats->count = 1;
stats->min = value;
stats->max = value;
stats->mean = value;
stats->sum_squared_deviation = 0;
values_[attribute] = std::move(stats);
}
};
// Export the metrics to the provided |function|. Each value for Attribute
// has a separate set of stats. As such, |function| will be called once per
// value of Attribute.
void ExportValues(
std::function<void (const AttributeType&,
const EventStatistics&)> function) const {
for (auto it = values_.begin(); it != values_.end(); it++) {
function(it->first, *(it->second));
}
}
const std::string& metric_name() const { return metric_name_; };
private:
const std::string metric_name_;
const std::string attribute_name_;
std::map<AttributeType, std::unique_ptr<struct EventStatistics>> values_;
};
// The EventTimer is a supporting class for EventMetric instances that are used
// to time methods. The EventTimer starts a timer when first in scope, and
// records the timing when exiting scope.
//
// Example:
//
// EventMetric<int> my_metric;
//
// {
// EventTimer<int> my_timer(&my_metric);
// // Set the attribute to associate with this timing.
// my_timer.SetAttribtue(42);
//
// // Do some work that you want to time.
//
// } // The EventTimer destructor will record the the timing in my_metric;
//
template<typename AttributeType>
class EventTimer {
public:
explicit EventTimer(EventMetric<AttributeType>* metric)
:start_time_(systemTime()), metric_(metric) {
}
virtual ~EventTimer() {
if (metric_) {
metric_->Record(ns2us(systemTime() - start_time_), attribute_);
}
}
// Set the attribute to associate with this timing. E.g. this can be used to
// record the return code from the work that was timed.
void SetAttribute(const AttributeType& attribute) {
attribute_ = attribute;
}
protected:
// Visible for testing only.
nsecs_t start_time_;
private:
EventMetric<AttributeType>* metric_;
AttributeType attribute_;
};
} // namespace android
#endif // ANDROID_EVENT_METRIC_H_