Add EventMetric and a single use case.

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: Ic94bedd5f8293a58a939613a4ae69ce656a772be
gugelfrei
Adam Stone 7 years ago
parent 38428ce1b3
commit f0e618d0ee

@ -18,6 +18,7 @@ cc_library {
"PluginMetricsReporting.cpp",
"SharedLibrary.cpp",
"DrmHal.cpp",
"DrmMetrics.cpp",
"CryptoHal.cpp",
"protos/plugin_metrics.proto",
],

@ -30,6 +30,7 @@
#include <media/DrmHal.h>
#include <media/DrmSessionClientInterface.h>
#include <media/DrmSessionManager.h>
#include <media/EventMetric.h>
#include <media/PluginMetricsReporting.h>
#include <media/drm/DrmAPI.h>
#include <media/stagefright/foundation/ADebug.h>
@ -227,7 +228,6 @@ private:
DrmHal::DrmHal()
: mDrmSessionClient(new DrmSessionClient(this)),
mFactories(makeDrmFactories()),
mOpenSessionCounter("/drm/mediadrm/open_session", "status"),
mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT) {
}
@ -519,7 +519,7 @@ status_t DrmHal::openSession(Vector<uint8_t> &sessionId) {
mOpenSessions.push(sessionId);
}
mOpenSessionCounter.Increment(err);
mMetrics.mOpenSessionCounter.Increment(err);
return err;
}
@ -551,6 +551,7 @@ status_t DrmHal::getKeyRequest(Vector<uint8_t> const &sessionId,
String8 &defaultUrl, DrmPlugin::KeyRequestType *keyRequestType) {
Mutex::Autolock autoLock(mLock);
INIT_CHECK();
EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTiming);
DrmSessionManager::Instance()->useSession(sessionId);
@ -562,6 +563,7 @@ status_t DrmHal::getKeyRequest(Vector<uint8_t> const &sessionId,
} else if (keyType == DrmPlugin::kKeyType_Release) {
hKeyType = KeyType::RELEASE;
} else {
keyRequestTimer.SetAttribute(BAD_VALUE);
return BAD_VALUE;
}
@ -636,7 +638,9 @@ status_t DrmHal::getKeyRequest(Vector<uint8_t> const &sessionId,
}
});
return hResult.isOk() ? err : DEAD_OBJECT;
err = hResult.isOk() ? err : DEAD_OBJECT;
keyRequestTimer.SetAttribute(err);
return err;
}
status_t DrmHal::provideKeyResponse(Vector<uint8_t> const &sessionId,
@ -987,26 +991,12 @@ status_t DrmHal::setPropertyByteArray(String8 const &name,
return toStatusT(status);
}
status_t DrmHal::getMetrics(MediaAnalyticsItem* metrics) {
// TODO: Move mOpenSessionCounter and suffixes to a separate class
// that manages the collection of metrics and exporting them.
std::string success_count_name =
mOpenSessionCounter.metric_name() + "/ok/count";
std::string error_count_name =
mOpenSessionCounter.metric_name() + "/error/count";
mOpenSessionCounter.ExportValues(
[&] (status_t status, int64_t value) {
if (status == OK) {
metrics->setInt64(success_count_name.c_str(), value);
} else {
int64_t total_errors(0);
metrics->getInt64(error_count_name.c_str(), &total_errors);
metrics->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.
}
});
status_t DrmHal::getMetrics(MediaAnalyticsItem* item) {
if (item == nullptr) {
return UNEXPECTED_NULL;
}
mMetrics.Export(item);
return OK;
}

@ -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

@ -10,3 +10,18 @@ cc_test {
"-Wall",
],
}
cc_test {
name: "EventMetric_test",
srcs: ["EventMetric_test.cpp"],
shared_libs: [
"liblog",
"libmediadrm",
"libutils",
],
include_dirs: ["frameworks/av/include/media"],
cflags: [
"-Werror",
"-Wall",
],
}

@ -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

@ -16,6 +16,10 @@
#ifndef ANDROID_COUNTER_METRIC_H_
#define ANDROID_COUNTER_METRIC_H_
#include <functional>
#include <map>
#include <string>
#include <media/MediaAnalyticsItem.h>
#include <utils/Log.h>
@ -71,13 +75,13 @@ class CounterMetric {
// of Attribute.
void ExportValues(
std::function<void (const AttributeType&,
const int64_t count)> function) {
const int64_t count)> function) const {
for (auto it = values_.begin(); it != values_.end(); it++) {
function(it->first, it->second);
}
}
const std::string& metric_name() { return metric_name_; };
const std::string& metric_name() const { return metric_name_; };
private:
const std::string metric_name_;

@ -23,7 +23,7 @@
#include <android/hardware/drm/1.0/IDrmPluginListener.h>
#include <android/hardware/drm/1.0/IDrmFactory.h>
#include <media/CounterMetric.h>
#include <media/DrmMetrics.h>
#include <media/IDrm.h>
#include <media/IDrmClient.h>
#include <media/MediaAnalyticsItem.h>
@ -181,7 +181,7 @@ private:
sp<IDrmPlugin> mPlugin;
sp<drm::V1_1::IDrmPlugin> mPluginV1_1;
CounterMetric<status_t> mOpenSessionCounter;
MediaDrmMetrics mMetrics;
Vector<Vector<uint8_t>> mOpenSessions;
void closeOpenSessions();

@ -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…
Cancel
Save