Adds an EventMetric class, associated unit tests, and an instance of the EventMetric to DrmHal. Also added a unit test for CounterMetric and created a class to hold all of the future metric instances. BUG: 64001676 Test: Added and ran unit tests. Also added a CTS test case. Change-Id: Ic94bedd5f8293a58a939613a4ae69ce656a772begugelfrei
parent
38428ce1b3
commit
f0e618d0ee
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <media/DrmMetrics.h>
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
void ExportCounterMetric(const android::CounterMetric<T>& counter,
|
||||
android::MediaAnalyticsItem* item) {
|
||||
std::string success_count_name = counter.metric_name() + "/ok/count";
|
||||
std::string error_count_name = counter.metric_name() + "/error/count";
|
||||
counter.ExportValues(
|
||||
[&] (const android::status_t status, const int64_t value) {
|
||||
if (status == android::OK) {
|
||||
item->setInt64(success_count_name.c_str(), value);
|
||||
} else {
|
||||
int64_t total_errors(0);
|
||||
item->getInt64(error_count_name.c_str(), &total_errors);
|
||||
item->setInt64(error_count_name.c_str(), total_errors + value);
|
||||
// TODO: Add support for exporting the list of error values.
|
||||
// This probably needs to be added to MediaAnalyticsItem.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ExportEventMetric(const android::EventMetric<T>& event,
|
||||
android::MediaAnalyticsItem* item) {
|
||||
std::string success_count_name = event.metric_name() + "/ok/count";
|
||||
std::string error_count_name = event.metric_name() + "/error/count";
|
||||
std::string timing_name = event.metric_name() + "/average_time_micros";
|
||||
event.ExportValues(
|
||||
[&] (const android::status_t& status,
|
||||
const android::EventStatistics& value) {
|
||||
if (status == android::OK) {
|
||||
item->setInt64(success_count_name.c_str(), value.count);
|
||||
item->setInt64(timing_name.c_str(), value.mean);
|
||||
} else {
|
||||
int64_t total_errors(0);
|
||||
item->getInt64(error_count_name.c_str(), &total_errors);
|
||||
item->setInt64(error_count_name.c_str(),
|
||||
total_errors + value.count);
|
||||
// TODO: Add support for exporting the list of error values.
|
||||
// This probably needs to be added to MediaAnalyticsItem.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
namespace android {
|
||||
|
||||
MediaDrmMetrics::MediaDrmMetrics()
|
||||
: mOpenSessionCounter("/drm/mediadrm/open_session", "status"),
|
||||
mGetKeyRequestTiming("/drm/mediadrm/get_key_request", "status") {
|
||||
}
|
||||
|
||||
void MediaDrmMetrics::Export(MediaAnalyticsItem* item) {
|
||||
ExportCounterMetric(mOpenSessionCounter, item);
|
||||
ExportEventMetric(mGetKeyRequestTiming, item);
|
||||
}
|
||||
|
||||
} // namespace android
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "EventMetric.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
/**
|
||||
* Unit tests for the EventMetric class.
|
||||
*/
|
||||
|
||||
TEST(EventMetricTest, IntDataTypeEmpty) {
|
||||
EventMetric<int> metric("MyMetricName", "MetricAttributeName");
|
||||
|
||||
std::map<int, EventStatistics> values;
|
||||
|
||||
metric.ExportValues(
|
||||
[&] (int attribute_value, const EventStatistics& value) {
|
||||
values[attribute_value] = value;
|
||||
});
|
||||
|
||||
EXPECT_TRUE(values.empty());
|
||||
}
|
||||
|
||||
TEST(EventMetricTest, IntDataType) {
|
||||
EventMetric<int> metric("MyMetricName", "MetricAttributeName");
|
||||
|
||||
std::map<int, EventStatistics> values;
|
||||
|
||||
metric.Record(4, 7);
|
||||
metric.Record(5, 8);
|
||||
metric.Record(5, 8);
|
||||
metric.Record(5, 8);
|
||||
metric.Record(6, 8);
|
||||
metric.Record(6, 8);
|
||||
metric.Record(6, 8);
|
||||
|
||||
metric.ExportValues(
|
||||
[&] (int attribute_value, const EventStatistics& value) {
|
||||
values[attribute_value] = value;
|
||||
});
|
||||
|
||||
ASSERT_EQ(2u, values.size());
|
||||
EXPECT_EQ(4, values[7].min);
|
||||
EXPECT_EQ(4, values[7].max);
|
||||
EXPECT_EQ(4, values[7].mean);
|
||||
EXPECT_EQ(1, values[7].count);
|
||||
|
||||
EXPECT_EQ(5, values[8].min);
|
||||
EXPECT_EQ(6, values[8].max);
|
||||
// This is an approximate value because of the technique we're using.
|
||||
EXPECT_NEAR(5.5, values[8].mean, 0.2);
|
||||
EXPECT_EQ(6, values[8].count);
|
||||
}
|
||||
|
||||
TEST(EventMetricTest, StringDataType) {
|
||||
EventMetric<std::string> metric("MyMetricName", "MetricAttributeName");
|
||||
|
||||
std::map<std::string, EventStatistics> values;
|
||||
|
||||
metric.Record(1, "a");
|
||||
metric.Record(2, "b");
|
||||
metric.Record(2, "b");
|
||||
metric.Record(3, "b");
|
||||
metric.Record(3, "b");
|
||||
|
||||
metric.ExportValues(
|
||||
[&] (std::string attribute_value, const EventStatistics& value) {
|
||||
values[attribute_value] = value;
|
||||
});
|
||||
|
||||
ASSERT_EQ(2u, values.size());
|
||||
EXPECT_EQ(1, values["a"].min);
|
||||
EXPECT_EQ(1, values["a"].max);
|
||||
EXPECT_EQ(1, values["a"].mean);
|
||||
EXPECT_EQ(1, values["a"].count);
|
||||
|
||||
EXPECT_EQ(2, values["b"].min);
|
||||
EXPECT_EQ(3, values["b"].max);
|
||||
EXPECT_NEAR(2.5, values["b"].mean, 0.2);
|
||||
EXPECT_EQ(4, values["b"].count);
|
||||
}
|
||||
|
||||
// Helper class that allows us to mock the clock.
|
||||
template<typename AttributeType>
|
||||
class MockEventTimer : public EventTimer<AttributeType> {
|
||||
public:
|
||||
explicit MockEventTimer(nsecs_t time_delta_ns,
|
||||
EventMetric<AttributeType>* metric)
|
||||
: EventTimer<AttributeType>(metric) {
|
||||
// Pretend the event started earlier.
|
||||
this->start_time_ = systemTime() - time_delta_ns;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(EventTimerTest, IntDataType) {
|
||||
EventMetric<int> metric("MyMetricName", "MetricAttributeName");
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
{
|
||||
// Add a mock time delta.
|
||||
MockEventTimer<int> metric_timer(i * 1000000, &metric);
|
||||
metric_timer.SetAttribute(i % 2);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<int, EventStatistics> values;
|
||||
metric.ExportValues(
|
||||
[&] (int attribute_value, const EventStatistics& value) {
|
||||
values[attribute_value] = value;
|
||||
});
|
||||
|
||||
ASSERT_EQ(2u, values.size());
|
||||
EXPECT_LT(values[0].min, values[0].max);
|
||||
EXPECT_GE(4000, values[0].max);
|
||||
EXPECT_GT(values[0].mean, values[0].min);
|
||||
EXPECT_LE(values[0].mean, values[0].max);
|
||||
EXPECT_EQ(3, values[0].count);
|
||||
|
||||
EXPECT_LT(values[1].min, values[1].max);
|
||||
EXPECT_GE(3000, values[1].max);
|
||||
EXPECT_GT(values[1].mean, values[1].min);
|
||||
EXPECT_LE(values[1].mean, values[1].max);
|
||||
EXPECT_EQ(2, values[1].count);
|
||||
}
|
||||
|
||||
} // namespace android
|
@ -0,0 +1 @@
|
||||
../../media/libmedia/include/media/DrmMetrics.h
|
@ -0,0 +1 @@
|
||||
../../media/libmedia/include/media/EventMetric.h
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 DRM_METRICS_H_
|
||||
#define DRM_METRICS_H_
|
||||
|
||||
#include <media/CounterMetric.h>
|
||||
#include <media/EventMetric.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/**
|
||||
* This class contains the definition of metrics captured within MediaDrm.
|
||||
* It also contains a method for exporting all of the metrics to a
|
||||
* MediaAnalyticsItem instance.
|
||||
*/
|
||||
class MediaDrmMetrics {
|
||||
public:
|
||||
explicit MediaDrmMetrics();
|
||||
// Counter of times openSession was called.
|
||||
CounterMetric<status_t> mOpenSessionCounter;
|
||||
// Counter and timing of the getKeyRequest call.
|
||||
EventMetric<status_t> mGetKeyRequestTiming;
|
||||
|
||||
// TODO: Add the full set of metrics to be captured.
|
||||
|
||||
// Export the metrics to a MediaAnalyticsItem.
|
||||
void Export(MediaAnalyticsItem* item);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // DRM_METRICS_H_
|
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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_
|
Loading…
Reference in new issue