Add unit tests for CountMetricProducer, EventMetricProducer

And other miscellaneous fixes.
+ clang-format
+ 2 bug fixes, one in dump-report command, one in ResourcePowerManagerPuller

Test: statsd_test

Change-Id: Ibd164d948ad62adcc529d813df1210781e38be47
This commit is contained in:
Yao Chen
2017-11-02 13:52:59 -07:00
parent d7f98747b3
commit 93fe3a34a0
26 changed files with 598 additions and 247 deletions

View File

@@ -157,8 +157,10 @@ LOCAL_SRC_FILES := \
tests/LogReader_test.cpp \
tests/MetricsManager_test.cpp \
tests/UidMap_test.cpp \
tests/OringDurationTracker_test.cpp \
tests/MaxDurationTracker_test.cpp
tests/metrics/OringDurationTracker_test.cpp \
tests/metrics/MaxDurationTracker_test.cpp \
tests/metrics/CountMetricProducer_test.cpp \
tests/metrics/EventMetricProducer_test.cpp
LOCAL_STATIC_LIBRARIES := \

View File

@@ -89,7 +89,7 @@ ConfigMetricsReport StatsLogProcessor::onDumpReport(const ConfigKey& key) {
*dest = m;
}
auto temp = mUidMap->getOutput(key);
report.set_allocated_uid_map(&temp);
report.mutable_uid_map()->Swap(&temp);
return report;
}

View File

@@ -83,28 +83,31 @@ bool ResourcePowerManagerPuller::Pull(const int tagId, vector<shared_ptr<LogEven
return false;
}
uint64_t timestamp = time(nullptr) * NS_PER_SEC;
data->clear();
Return<void> ret = gPowerHalV1_0->getPlatformLowPowerStats(
[&data](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
[&data, timestamp](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
if (status != Status::SUCCESS) return;
for (size_t i = 0; i < states.size(); i++) {
const PowerStatePlatformSleepState& state = states[i];
auto statePtr = make_shared<LogEvent>(power_state_platform_sleep_state_tag);
auto statePtr =
make_shared<LogEvent>(power_state_platform_sleep_state_tag, timestamp);
auto elemList = statePtr->GetAndroidLogEventList();
*elemList << state.name;
*elemList << state.residencyInMsecSinceBoot;
*elemList << state.totalTransitions;
*elemList << state.supportedOnlyInSuspend;
statePtr->init();
data->push_back(statePtr);
VLOG("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
(long long)state.residencyInMsecSinceBoot,
(long long)state.totalTransitions, state.supportedOnlyInSuspend ? 1 : 0);
for (auto voter : state.voters) {
auto voterPtr = make_shared<LogEvent>(power_state_voter_tag);
auto voterPtr = make_shared<LogEvent>(power_state_voter_tag, timestamp);
auto elemList = voterPtr->GetAndroidLogEventList();
*elemList << state.name;
*elemList << voter.name;
@@ -128,7 +131,7 @@ bool ResourcePowerManagerPuller::Pull(const int tagId, vector<shared_ptr<LogEven
android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
if (gPowerHal_1_1 != nullptr) {
ret = gPowerHal_1_1->getSubsystemLowPowerStats(
[&data](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
[&data, timestamp](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
if (status != Status::SUCCESS) return;
@@ -137,8 +140,8 @@ bool ResourcePowerManagerPuller::Pull(const int tagId, vector<shared_ptr<LogEven
const PowerStateSubsystem& subsystem = subsystems[i];
for (size_t j = 0; j < subsystem.states.size(); j++) {
const PowerStateSubsystemSleepState& state = subsystem.states[j];
auto subsystemStatePtr =
make_shared<LogEvent>(power_state_subsystem_state_tag);
auto subsystemStatePtr = make_shared<LogEvent>(
power_state_subsystem_state_tag, timestamp);
auto elemList = subsystemStatePtr->GetAndroidLogEventList();
*elemList << subsystem.name;
*elemList << state.name;
@@ -146,6 +149,7 @@ bool ResourcePowerManagerPuller::Pull(const int tagId, vector<shared_ptr<LogEven
*elemList << state.totalTransitions;
*elemList << state.lastEntryTimestampMs;
*elemList << state.supportedOnlyInSuspend;
subsystemStatePtr->init();
data->push_back(subsystemStatePtr);
VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
subsystem.name.c_str(), state.name.c_str(),

View File

@@ -30,11 +30,11 @@
#include <iostream>
using std::make_shared;
using std::map;
using std::shared_ptr;
using std::string;
using std::vector;
using std::make_shared;
using std::shared_ptr;
namespace android {
namespace os {
@@ -42,40 +42,35 @@ namespace statsd {
StatsPullerManager::StatsPullerManager()
: mCurrentPullingInterval(LONG_MAX), mPullStartTimeMs(get_pull_start_time_ms()) {
shared_ptr<StatsPuller> statsCompanionServicePuller = make_shared<StatsCompanionServicePuller>();
shared_ptr <StatsPuller>
resourcePowerManagerPuller = make_shared<ResourcePowerManagerPuller>();
shared_ptr<StatsPuller> statsCompanionServicePuller =
make_shared<StatsCompanionServicePuller>();
shared_ptr<StatsPuller> resourcePowerManagerPuller = make_shared<ResourcePowerManagerPuller>();
mPullers.insert({android::util::KERNEL_WAKELOCK_PULLED,
statsCompanionServicePuller});
mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED,
statsCompanionServicePuller});
mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED,
statsCompanionServicePuller});
mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED_BY_FG_BG,
statsCompanionServicePuller});
mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED_BY_FG_BG,
statsCompanionServicePuller});
mPullers.insert({android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED,
resourcePowerManagerPuller});
mPullers.insert({android::util::POWER_STATE_VOTER_PULLED,
resourcePowerManagerPuller});
mPullers.insert({android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED,
resourcePowerManagerPuller});
mPullers.insert({android::util::KERNEL_WAKELOCK_PULLED, statsCompanionServicePuller});
mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED, statsCompanionServicePuller});
mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED, statsCompanionServicePuller});
mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED_BY_FG_BG, statsCompanionServicePuller});
mPullers.insert(
{android::util::MOBILE_BYTES_TRANSFERRED_BY_FG_BG, statsCompanionServicePuller});
mPullers.insert(
{android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED, resourcePowerManagerPuller});
mPullers.insert({android::util::POWER_STATE_VOTER_PULLED, resourcePowerManagerPuller});
mPullers.insert(
{android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED, resourcePowerManagerPuller});
mStatsCompanionService = StatsService::getStatsCompanionService();
}
bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
if (DEBUG) ALOGD("Initiating pulling %d", tagId);
bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
if (DEBUG) ALOGD("Initiating pulling %d", tagId);
if (mPullers.find(tagId) != mPullers.end()) {
return mPullers.find(tagId)->second->Pull(tagId, data);
} else {
ALOGD("Unknown tagId %d", tagId);
return false; // Return early since we don't know what to pull.
}
}
if (mPullers.find(tagId) != mPullers.end()) {
return mPullers.find(tagId)->second->Pull(tagId, data);
} else {
ALOGD("Unknown tagId %d", tagId);
return false; // Return early since we don't know what to pull.
}
}
StatsPullerManager& StatsPullerManager::GetInstance() {
static StatsPullerManager instance;
@@ -91,7 +86,8 @@ long StatsPullerManager::get_pull_start_time_ms() {
return time(nullptr) * 1000;
}
void StatsPullerManager::RegisterReceiver(int tagId, sp<PullDataReceiver> receiver, long intervalMs) {
void StatsPullerManager::RegisterReceiver(int tagId, sp<PullDataReceiver> receiver,
long intervalMs) {
AutoMutex _l(mReceiversLock);
vector<ReceiverInfo>& receivers = mReceivers[tagId];
for (auto it = receivers.begin(); it != receivers.end(); it++) {
@@ -143,8 +139,8 @@ void StatsPullerManager::OnAlarmFired() {
vector<pair<int, vector<ReceiverInfo*>>>();
for (auto& pair : mReceivers) {
vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
if (pair.second.size() != 0){
for(auto& receiverInfo : pair.second) {
if (pair.second.size() != 0) {
for (auto& receiverInfo : pair.second) {
if (receiverInfo.timeInfo.first + receiverInfo.timeInfo.second > currentTimeMs) {
receivers.push_back(&receiverInfo);
}

View File

@@ -35,7 +35,7 @@ LogEvent::LogEvent(log_msg& msg) : mList(msg) {
init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &mList);
}
LogEvent::LogEvent(int tag) : mList(tag) {
LogEvent::LogEvent(int tag, uint64_t timestampNs) : mList(tag), mTimestampNs(timestampNs) {
}
LogEvent::~LogEvent() {

View File

@@ -50,7 +50,7 @@ public:
* any of the values. This constructor is useful for unit-testing since we can't pass in an
* android_log_event_list since there is no copy constructor or assignment operator available.
*/
explicit LogEvent(int tag);
explicit LogEvent(int tag, uint64_t timestampNs);
~LogEvent();
@@ -123,7 +123,9 @@ private:
vector<android_log_list_element> mElements;
// Need a copy of the android_log_event_list so the strings are not cleared.
android_log_event_list mList;
long mTimestampNs;
uint64_t mTimestampNs;
int mTagId;
};

View File

@@ -58,10 +58,9 @@ const int FIELD_ID_COUNT = 3;
// TODO: add back AnomalyTracker.
CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex,
const sp<ConditionWizard>& wizard)
// TODO: Pass in the start time from MetricsManager, instead of calling time() here.
: MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard),
mMetric(metric) {
const sp<ConditionWizard>& wizard,
const uint64_t startTimeNs)
: MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) {
// TODO: evaluate initial conditions. and set mConditionMet.
if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
@@ -114,15 +113,17 @@ void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
}
StatsLogReport CountMetricProducer::onDumpReport() {
long long endTime = time(nullptr) * NANO_SECONDS_IN_A_SECOND;
long long endTime = time(nullptr) * NS_PER_SEC;
// Dump current bucket if it's stale.
// If current bucket is still on-going, don't force dump current bucket.
// In finish(), We can force dump current bucket.
flushCounterIfNeeded(endTime);
VLOG("metric %lld dump report now...", mMetric.metric_id());
for (const auto& counter : mPastBucketProtos) {
for (const auto& counter : mPastBuckets) {
const HashableDimensionKey& hashableKey = counter.first;
VLOG(" dimension key %s", hashableKey.c_str());
auto it = mDimensionKeyMap.find(hashableKey);
if (it == mDimensionKeyMap.end()) {
ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
@@ -147,20 +148,17 @@ StatsLogReport CountMetricProducer::onDumpReport() {
}
// Then fill bucket_info (CountBucketInfo).
for (const auto& proto : counter.second) {
size_t bufferSize = proto->size();
char* buffer(new char[bufferSize]);
size_t pos = 0;
auto it = proto->data();
while (it.readBuffer() != NULL) {
size_t toRead = it.currentToRead();
std::memcpy(&buffer[pos], it.readBuffer(), toRead);
pos += toRead;
it.rp()->move(toRead);
}
mProto->write(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION, buffer, bufferSize);
for (const auto& bucket : counter.second) {
long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
(long long)bucket.mBucketStartNs);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
(long long)bucket.mBucketEndNs);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount);
mProto->end(bucketInfoToken);
VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
(long long)bucket.mBucketEndNs, (long long)bucket.mCount);
}
mProto->end(wrapperToken);
}
@@ -169,8 +167,8 @@ StatsLogReport CountMetricProducer::onDumpReport() {
(long long)mCurrentBucketStartTimeNs);
size_t bufferSize = mProto->size();
VLOG("metric %lld dump report now...", mMetric.metric_id());
std::unique_ptr<uint8_t[]> buffer(new uint8_t[bufferSize]);
size_t pos = 0;
auto it = mProto->data();
while (it.readBuffer() != NULL) {
@@ -181,7 +179,7 @@ StatsLogReport CountMetricProducer::onDumpReport() {
}
startNewProtoOutputStream(endTime);
mPastBucketProtos.clear();
mPastBuckets.clear();
mByteSize = 0;
// TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
@@ -239,20 +237,16 @@ void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) {
// adjust the bucket start time
int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
CountBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
for (const auto& counter : mCurrentSlicedCounter) {
unique_ptr<ProtoOutputStream> proto = make_unique<ProtoOutputStream>();
proto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
(long long)mCurrentBucketStartTimeNs);
proto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
(long long)mCurrentBucketStartTimeNs + mBucketSizeNs);
proto->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)counter.second);
auto& bucketList = mPastBucketProtos[counter.first];
mByteSize += proto->size();
bucketList.push_back(std::move(proto));
info.mCount = counter.second;
auto& bucketList = mPastBuckets[counter.first];
bucketList.push_back(info);
VLOG("metric %lld, dump key value: %s -> %d", mMetric.metric_id(), counter.first.c_str(),
counter.second);
mByteSize += sizeof(info);
}
// TODO: Re-add anomaly detection (similar to):

View File

@@ -20,6 +20,7 @@
#include <unordered_map>
#include <android/util/ProtoOutputStream.h>
#include <gtest/gtest_prod.h>
#include "../condition/ConditionTracker.h"
#include "../matchers/matcher_util.h"
#include "CountAnomalyTracker.h"
@@ -34,11 +35,17 @@ namespace android {
namespace os {
namespace statsd {
struct CountBucket {
int64_t mBucketStartNs;
int64_t mBucketEndNs;
int64_t mCount;
};
class CountMetricProducer : public MetricProducer {
public:
// TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
CountMetricProducer(const CountMetric& countMetric, const int conditionIndex,
const sp<ConditionWizard>& wizard);
const sp<ConditionWizard>& wizard, const uint64_t startTimeNs);
virtual ~CountMetricProducer();
@@ -66,8 +73,7 @@ protected:
private:
const CountMetric mMetric;
std::unordered_map<HashableDimensionKey,
std::vector<unique_ptr<android::util::ProtoOutputStream>>> mPastBucketProtos;
std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets;
size_t mByteSize;
@@ -83,6 +89,10 @@ private:
long long mProtoToken;
void startNewProtoOutputStream(long long timestamp);
FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
};
} // namespace statsd

View File

@@ -35,9 +35,9 @@ DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
const int conditionIndex, const size_t startIndex,
const size_t stopIndex, const size_t stopAllIndex,
const sp<ConditionWizard>& wizard,
const vector<KeyMatcher>& internalDimension)
// TODO: Pass in the start time from MetricsManager, instead of calling time() here.
: MetricProducer(time(nullptr) * NANO_SECONDS_IN_A_SECOND, conditionIndex, wizard),
const vector<KeyMatcher>& internalDimension,
const uint64_t startTimeNs)
: MetricProducer(startTimeNs, conditionIndex, wizard),
mMetric(metric),
mStartIndex(startIndex),
mStopIndex(stopIndex),
@@ -131,7 +131,7 @@ StatsLogReport DurationMetricProducer::onDumpReport() {
// Dump current bucket if it's stale.
// If current bucket is still on-going, don't force dump current bucket.
// In finish(), We can force dump current bucket.
flushIfNeeded(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
flushIfNeeded(time(nullptr) * NS_PER_SEC);
report.set_end_report_nanos(mCurrentBucketStartTimeNs);
StatsLogReport_DurationMetricDataWrapper* wrapper = report.mutable_duration_metrics();
@@ -195,10 +195,10 @@ void DurationMetricProducer::onMatchedLogEventInternal(
}
size_t DurationMetricProducer::byteSize() {
// TODO: return actual proto size when ProtoOutputStream is ready for use for
// DurationMetricsProducer.
// return mProto->size();
return 0;
// TODO: return actual proto size when ProtoOutputStream is ready for use for
// DurationMetricsProducer.
// return mProto->size();
return 0;
}
} // namespace statsd

View File

@@ -40,7 +40,7 @@ public:
DurationMetricProducer(const DurationMetric& durationMetric, const int conditionIndex,
const size_t startIndex, const size_t stopIndex,
const size_t stopAllIndex, const sp<ConditionWizard>& wizard,
const vector<KeyMatcher>& internalDimension);
const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs);
virtual ~DurationMetricProducer();

View File

@@ -46,10 +46,9 @@ const int FIELD_ID_TIMESTAMP_NANOS = 1;
const int FIELD_ID_STATS_EVENTS = 2;
EventMetricProducer::EventMetricProducer(const EventMetric& metric, const int conditionIndex,
const sp<ConditionWizard>& wizard)
// TODO: Pass in the start time from MetricsManager, instead of calling time() here.
: MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard),
mMetric(metric) {
const sp<ConditionWizard>& wizard,
const uint64_t startTimeNs)
: MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) {
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
@@ -82,7 +81,7 @@ void EventMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
}
StatsLogReport EventMetricProducer::onDumpReport() {
long long endTime = time(nullptr) * NANO_SECONDS_IN_A_SECOND;
long long endTime = time(nullptr) * NS_PER_SEC;
mProto->end(mProtoToken);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime);
@@ -114,7 +113,6 @@ void EventMetricProducer::onMatchedLogEventInternal(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
const LogEvent& event, bool scheduledPull) {
if (!condition) {
return;
}
@@ -128,7 +126,7 @@ void EventMetricProducer::onMatchedLogEventInternal(
}
size_t EventMetricProducer::byteSize() {
return mProto->size();
return mProto->size();
}
} // namespace statsd

View File

@@ -20,6 +20,7 @@
#include <unordered_map>
#include <android/util/ProtoOutputStream.h>
#include "../condition/ConditionTracker.h"
#include "../matchers/matcher_util.h"
#include "MetricProducer.h"
@@ -35,13 +36,14 @@ class EventMetricProducer : public MetricProducer {
public:
// TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
EventMetricProducer(const EventMetric& eventMetric, const int conditionIndex,
const sp<ConditionWizard>& wizard);
const sp<ConditionWizard>& wizard, const uint64_t startTimeNs);
virtual ~EventMetricProducer();
void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey,
bool condition, const LogEvent& event, bool scheduledPull) override;
bool condition, const LogEvent& event,
bool scheduledPull) override;
void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;

View File

@@ -35,7 +35,7 @@ namespace statsd {
GaugeMetricProducer::GaugeMetricProducer(const GaugeMetric& metric, const int conditionIndex,
const sp<ConditionWizard>& wizard, const int pullTagId)
: MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard),
: MetricProducer((time(nullptr) * NS_PER_SEC), conditionIndex, wizard),
mMetric(metric),
mPullTagId(pullTagId) {
if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
@@ -94,7 +94,7 @@ StatsLogReport GaugeMetricProducer::onDumpReport() {
// Dump current bucket if it's stale.
// If current bucket is still on-going, don't force dump current bucket.
// In finish(), We can force dump current bucket.
flushGaugeIfNeededLocked(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
flushGaugeIfNeededLocked(time(nullptr) * NS_PER_SEC);
report.set_end_report_nanos(mCurrentBucketStartTimeNs);
StatsLogReport_GaugeMetricDataWrapper* wrapper = report.mutable_gauge_metrics();

View File

@@ -40,6 +40,7 @@ public:
: mStartTimeNs(startTimeNs),
mCurrentBucketStartTimeNs(startTimeNs),
mCondition(conditionIndex >= 0 ? false : true),
mConditionSliced(false),
mWizard(wizard),
mConditionTrackerIndex(conditionIndex) {
// reuse the same map for non-sliced metrics too. this way, we avoid too many if-else.

View File

@@ -15,17 +15,17 @@
*/
#define DEBUG true // STOPSHIP if true
#include "Log.h"
#include "MetricsManager.h"
#include <log/logprint.h>
#include "../condition/CombinationConditionTracker.h"
#include "../condition/SimpleConditionTracker.h"
#include "../matchers/CombinationLogMatchingTracker.h"
#include "../matchers/SimpleLogMatchingTracker.h"
#include "CountMetricProducer.h"
#include "condition/CombinationConditionTracker.h"
#include "condition/SimpleConditionTracker.h"
#include "matchers/CombinationLogMatchingTracker.h"
#include "matchers/SimpleLogMatchingTracker.h"
#include "metrics_manager_util.h"
#include "stats_util.h"
#include <log/logprint.h>
using std::make_unique;
using std::set;
using std::string;

View File

@@ -96,4 +96,3 @@ private:
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -23,12 +23,12 @@
#include <limits.h>
#include <stdlib.h>
using std::map;
using std::unordered_map;
using std::list;
using std::make_shared;
using std::map;
using std::shared_ptr;
using std::unique_ptr;
using std::unordered_map;
namespace android {
namespace os {
@@ -36,51 +36,50 @@ namespace statsd {
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
const sp<ConditionWizard>& wizard, const int pullTagId)
: MetricProducer((time(nullptr) / 600 * 600 * NANO_SECONDS_IN_A_SECOND), conditionIndex,
wizard),
mMetric(metric),
mPullTagId(pullTagId) {
// TODO: valuemetric for pushed events may need unlimited bucket length
mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
const sp<ConditionWizard>& wizard, const int pullTagId,
const uint64_t startTimeNs)
: MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric), mPullTagId(pullTagId) {
// TODO: valuemetric for pushed events may need unlimited bucket length
mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
mConditionSliced = true;
}
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
mConditionSliced = true;
}
if (!metric.has_condition() && mPullTagId != -1) {
mStatsPullerManager.RegisterReceiver(mPullTagId, this, metric.bucket().bucket_size_millis());
}
if (!metric.has_condition() && mPullTagId != -1) {
mStatsPullerManager.RegisterReceiver(mPullTagId, this,
metric.bucket().bucket_size_millis());
}
VLOG("value metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
VLOG("value metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
ValueMetricProducer::~ValueMetricProducer() {
VLOG("~ValueMetricProducer() called");
VLOG("~ValueMetricProducer() called");
}
void ValueMetricProducer::finish() {
// TODO: write the StatsLogReport to dropbox using
// DropboxWriter.
// TODO: write the StatsLogReport to dropbox using
// DropboxWriter.
}
static void addSlicedCounterToReport(StatsLogReport_ValueMetricDataWrapper& wrapper,
const vector<KeyValuePair>& key,
const vector<ValueBucketInfo>& buckets) {
ValueMetricData* data = wrapper.add_data();
for (const auto& kv : key) {
data->add_dimension()->CopyFrom(kv);
}
for (const auto& bucket : buckets) {
data->add_bucket_info()->CopyFrom(bucket);
VLOG("\t bucket [%lld - %lld] value: %lld", bucket.start_bucket_nanos(),
bucket.end_bucket_nanos(), bucket.value());
}
ValueMetricData* data = wrapper.add_data();
for (const auto& kv : key) {
data->add_dimension()->CopyFrom(kv);
}
for (const auto& bucket : buckets) {
data->add_bucket_info()->CopyFrom(bucket);
VLOG("\t bucket [%lld - %lld] value: %lld", bucket.start_bucket_nanos(),
bucket.end_bucket_nanos(), bucket.value());
}
}
void ValueMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
@@ -88,33 +87,28 @@ void ValueMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
}
StatsLogReport ValueMetricProducer::onDumpReport() {
VLOG("metric %lld dump report now...", mMetric.metric_id());
VLOG("metric %lld dump report now...", mMetric.metric_id());
StatsLogReport report;
report.set_metric_id(mMetric.metric_id());
report.set_start_report_nanos(mStartTimeNs);
StatsLogReport report;
report.set_metric_id(mMetric.metric_id());
report.set_start_report_nanos(mStartTimeNs);
report.set_end_report_nanos(mCurrentBucketStartTimeNs);
// Dump current bucket if it's stale.
// If current bucket is still on-going, don't force dump current bucket.
// In finish(), We can force dump current bucket.
// flush_if_needed(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
report.set_end_report_nanos(mCurrentBucketStartTimeNs);
StatsLogReport_ValueMetricDataWrapper* wrapper = report.mutable_value_metrics();
StatsLogReport_ValueMetricDataWrapper* wrapper = report.mutable_value_metrics();
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
auto it = mDimensionKeyMap.find(hashableKey);
if (it == mDimensionKeyMap.end()) {
ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
continue;
}
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
auto it = mDimensionKeyMap.find(hashableKey);
if (it == mDimensionKeyMap.end()) {
ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
continue;
VLOG(" dimension key %s", hashableKey.c_str());
addSlicedCounterToReport(*wrapper, it->second, pair.second);
}
VLOG(" dimension key %s", hashableKey.c_str());
addSlicedCounterToReport(*wrapper, it->second, pair.second);
}
return report;
// TODO: Clear mPastBuckets, mDimensionKeyMap once the report is dumped.
return report;
// TODO: Clear mPastBuckets, mDimensionKeyMap once the report is dumped.
}
void ValueMetricProducer::onConditionChanged(const bool condition, const uint64_t eventTime) {
@@ -158,50 +152,50 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven
}
void ValueMetricProducer::onMatchedLogEventInternal(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const map<string, HashableDimensionKey>& conditionKey, bool condition,
const LogEvent& event, bool scheduledPull) {
uint64_t eventTimeNs = event.GetTimestampNs();
if (eventTimeNs < mCurrentBucketStartTimeNs) {
VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
(long long)mCurrentBucketStartTimeNs);
return;
}
Interval& interval = mCurrentSlicedBucket[eventKey];
long value = get_value(event);
if (scheduledPull) {
if (interval.raw.size() > 0) {
interval.raw.back().second = value;
} else {
interval.raw.push_back(std::make_pair(value, value));
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const map<string, HashableDimensionKey>& conditionKey, bool condition,
const LogEvent& event, bool scheduledPull) {
uint64_t eventTimeNs = event.GetTimestampNs();
if (eventTimeNs < mCurrentBucketStartTimeNs) {
VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
(long long)mCurrentBucketStartTimeNs);
return;
}
mNextSlicedBucket[eventKey].raw[0].first = value;
} else {
if (mCondition == ConditionState::kTrue) {
interval.raw.push_back(std::make_pair(value, 0));
Interval& interval = mCurrentSlicedBucket[eventKey];
long value = get_value(event);
if (scheduledPull) {
if (interval.raw.size() > 0) {
interval.raw.back().second = value;
} else {
interval.raw.push_back(std::make_pair(value, value));
}
mNextSlicedBucket[eventKey].raw[0].first = value;
} else {
if (interval.raw.size() != 0) {
interval.raw.back().second = value;
}
if (mCondition == ConditionState::kTrue) {
interval.raw.push_back(std::make_pair(value, 0));
} else {
if (interval.raw.size() != 0) {
interval.raw.back().second = value;
}
}
}
if (mPullTagId == -1) {
flush_if_needed(eventTimeNs);
}
}
if (mPullTagId == -1) {
flush_if_needed(eventTimeNs);
}
}
long ValueMetricProducer::get_value(const LogEvent& event) {
status_t err = NO_ERROR;
long val = event.GetLong(mMetric.value_field(), &err);
if (err == NO_ERROR) {
return val;
} else {
VLOG("Can't find value in message.");
return 0;
}
status_t err = NO_ERROR;
long val = event.GetLong(mMetric.value_field(), &err);
if (err == NO_ERROR) {
return val;
} else {
VLOG("Can't find value in message.");
return 0;
}
}
void ValueMetricProducer::flush_if_needed(const uint64_t eventTimeNs) {
@@ -218,22 +212,22 @@ void ValueMetricProducer::flush_if_needed(const uint64_t eventTimeNs) {
info.set_end_bucket_nanos(mCurrentBucketStartTimeNs + mBucketSizeNs);
for (const auto& slice : mCurrentSlicedBucket) {
long value = 0;
for (const auto& pair : slice.second.raw) {
value += pair.second - pair.first;
}
info.set_value(value);
VLOG(" %s, %ld", slice.first.c_str(), value);
// it will auto create new vector of ValuebucketInfo if the key is not found.
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
long value = 0;
for (const auto& pair : slice.second.raw) {
value += pair.second - pair.first;
}
info.set_value(value);
VLOG(" %s, %ld", slice.first.c_str(), value);
// it will auto create new vector of ValuebucketInfo if the key is not found.
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
}
// Reset counters
mCurrentSlicedBucket.swap(mNextSlicedBucket);
mNextSlicedBucket.clear();
int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
if (numBucketsForward >1) {
if (numBucketsForward > 1) {
VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
}
mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
@@ -243,4 +237,4 @@ void ValueMetricProducer::flush_if_needed(const uint64_t eventTimeNs) {
} // namespace statsd
} // namespace os
} // namespace android
} // namespace android

View File

@@ -33,7 +33,8 @@ namespace statsd {
class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex,
const sp<ConditionWizard>& wizard, const int pullTagId);
const sp<ConditionWizard>& wizard, const int pullTagId,
const uint64_t startTimeNs);
virtual ~ValueMetricProducer();
@@ -47,7 +48,9 @@ public:
void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
// TODO: Implement this later.
size_t byteSize() override{return 0;};
size_t byteSize() override {
return 0;
};
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};

View File

@@ -192,10 +192,11 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
unordered_map<int, std::vector<int>>& conditionToMetricMap,
unordered_map<int, std::vector<int>>& trackerToMetricMap) {
sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
const int allMetricsCount =
config.count_metric_size() + config.duration_metric_size() + config.event_metric_size() + config.value_metric_size();
const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
config.event_metric_size() + config.value_metric_size();
allMetricProducers.reserve(allMetricsCount);
StatsPullerManager& statsPullerManager = StatsPullerManager::GetInstance();
uint64_t startTimeNs = time(nullptr) * NS_PER_SEC;
// Build MetricProducers for each metric defined in config.
// build CountMetricProducer
@@ -221,7 +222,8 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
conditionToMetricMap);
}
sp<MetricProducer> countProducer = new CountMetricProducer(metric, conditionIndex, wizard);
sp<MetricProducer> countProducer =
new CountMetricProducer(metric, conditionIndex, wizard, startTimeNs);
allMetricProducers.push_back(countProducer);
}
@@ -282,7 +284,7 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
sp<MetricProducer> durationMetric = new DurationMetricProducer(
metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2],
wizard, internalDimension);
wizard, internalDimension, startTimeNs);
allMetricProducers.push_back(durationMetric);
}
@@ -308,7 +310,9 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
conditionToMetricMap);
}
sp<MetricProducer> eventMetric = new EventMetricProducer(metric, conditionIndex, wizard);
sp<MetricProducer> eventMetric =
new EventMetricProducer(metric, conditionIndex, wizard, startTimeNs);
allMetricProducers.push_back(eventMetric);
}
@@ -348,7 +352,7 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
}
sp<MetricProducer> valueProducer =
new ValueMetricProducer(metric, conditionIndex, wizard, pullTagId);
new ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs);
allMetricProducers.push_back(valueProducer);
}

View File

@@ -28,7 +28,6 @@ namespace statsd {
#define DEFAULT_DIMENSION_KEY ""
#define MATCHER_NOT_FOUND -2
#define NANO_SECONDS_IN_A_SECOND (1000 * 1000 * 1000)
typedef std::string HashableDimensionKey;

View File

@@ -42,7 +42,7 @@ TEST(LogEntryMatcherTest, TestSimpleMatcher) {
auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
simpleMatcher->set_tag(TAG_ID);
LogEvent event(TAG_ID);
LogEvent event(TAG_ID, 0);
// Convert to a LogEvent
event.init();
@@ -62,7 +62,7 @@ TEST(LogEntryMatcherTest, TestBoolMatcher) {
keyValue2->mutable_key_matcher()->set_key(FIELD_ID_2);
// Set up the event
LogEvent event(TAG_ID);
LogEvent event(TAG_ID, 0);
auto list = event.GetAndroidLogEventList();
*list << true;
*list << false;
@@ -98,7 +98,7 @@ TEST(LogEntryMatcherTest, TestStringMatcher) {
keyValue->set_eq_string("some value");
// Set up the event
LogEvent event(TAG_ID);
LogEvent event(TAG_ID, 0);
auto list = event.GetAndroidLogEventList();
*list << "some value";
@@ -119,7 +119,7 @@ TEST(LogEntryMatcherTest, TestIntComparisonMatcher) {
keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
// Set up the event
LogEvent event(TAG_ID);
LogEvent event(TAG_ID, 0);
auto list = event.GetAndroidLogEventList();
*list << 11;

View File

@@ -0,0 +1,183 @@
// Copyright (C) 2017 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 "metrics_test_helper.h"
#include "src/metrics/CountMetricProducer.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <vector>
using namespace testing;
using android::sp;
using std::set;
using std::unordered_map;
using std::vector;
#ifdef __ANDROID__
namespace android {
namespace os {
namespace statsd {
TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
int tagId = 1;
CountMetric metric;
metric.set_metric_id(1);
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
LogEvent event1(tagId, bucketStartTimeNs + 1);
LogEvent event2(tagId, bucketStartTimeNs + 2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(metric, -1 /*-1 meaning no condition*/, wizard,
bucketStartTimeNs);
// 2 events in bucket 1.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
const auto& bucketInfo = buckets[0];
EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
EXPECT_EQ(2LL, bucketInfo.mCount);
// 1 matched event happens in bucket 2.
LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 2);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3, false);
countProducer.flushCounterIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY].size());
const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY][1];
EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
EXPECT_EQ(1LL, bucketInfo2.mCount);
// nothing happens in bucket 3. we should not record anything for bucket 3.
countProducer.flushCounterIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
EXPECT_EQ(2UL, buckets3.size());
}
TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
CountMetric metric;
metric.set_metric_id(1);
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
metric.set_condition("SCREEN_ON");
LogEvent event1(1, bucketStartTimeNs + 1);
LogEvent event2(1, bucketStartTimeNs + 10);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(metric, 1, wizard, bucketStartTimeNs);
countProducer.onConditionChanged(true, bucketStartTimeNs);
countProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
countProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
const auto& bucketInfo = buckets[0];
EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
EXPECT_EQ(1LL, bucketInfo.mCount);
}
TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
CountMetric metric;
metric.set_metric_id(1);
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
EventConditionLink* link = metric.add_links();
link->set_condition("APP_IN_BACKGROUND_PER_UID");
link->add_key_in_main()->set_key(1);
link->add_key_in_condition()->set_key(2);
LogEvent event1(1, bucketStartTimeNs + 1);
auto list = event1.GetAndroidLogEventList();
*list << "111"; // uid
event1.init();
ConditionKey key1;
key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|";
LogEvent event2(1, bucketStartTimeNs + 10);
auto list2 = event2.GetAndroidLogEventList();
*list2 << "222"; // uid
event2.init();
ConditionKey key2;
key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|";
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
CountMetricProducer countProducer(metric, 1 /*condition tracker index*/, wizard,
bucketStartTimeNs);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
const auto& bucketInfo = buckets[0];
EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
EXPECT_EQ(1LL, bucketInfo.mCount);
}
} // namespace statsd
} // namespace os
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif

View File

@@ -0,0 +1,130 @@
// Copyright (C) 2017 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 "metrics_test_helper.h"
#include "src/metrics/EventMetricProducer.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <vector>
using namespace testing;
using android::sp;
using std::set;
using std::unordered_map;
using std::vector;
#ifdef __ANDROID__
namespace android {
namespace os {
namespace statsd {
TEST(EventMetricProducerTest, TestNoCondition) {
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
EventMetric metric;
metric.set_metric_id(1);
LogEvent event1(1 /*tag id*/, bucketStartTimeNs + 1);
LogEvent event2(1 /*tag id*/, bucketStartTimeNs + 2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EventMetricProducer eventProducer(metric, -1 /*-1 meaning no condition*/, wizard,
bucketStartTimeNs);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
// TODO: get the report and check the content after the ProtoOutputStream change is done.
// eventProducer.onDumpReport();
}
TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) {
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
EventMetric metric;
metric.set_metric_id(1);
metric.set_condition("SCREEN_ON");
LogEvent event1(1, bucketStartTimeNs + 1);
LogEvent event2(1, bucketStartTimeNs + 10);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EventMetricProducer eventProducer(metric, 1, wizard, bucketStartTimeNs);
eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
eventProducer.onConditionChanged(false /*condition*/, bucketStartTimeNs + 2);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
// TODO: get the report and check the content after the ProtoOutputStream change is done.
// eventProducer.onDumpReport();
}
TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) {
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
EventMetric metric;
metric.set_metric_id(1);
metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
EventConditionLink* link = metric.add_links();
link->set_condition("APP_IN_BACKGROUND_PER_UID");
link->add_key_in_main()->set_key(1);
link->add_key_in_condition()->set_key(2);
LogEvent event1(1, bucketStartTimeNs + 1);
auto list = event1.GetAndroidLogEventList();
*list << "111"; // uid
event1.init();
ConditionKey key1;
key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|";
LogEvent event2(1, bucketStartTimeNs + 10);
auto list2 = event2.GetAndroidLogEventList();
*list2 << "222"; // uid
event2.init();
ConditionKey key2;
key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|";
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
EventMetricProducer eventProducer(metric, 1, wizard, bucketStartTimeNs);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
// TODO: get the report and check the content after the ProtoOutputStream change is done.
// eventProducer.onDumpReport();
}
} // namespace statsd
} // namespace os
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif

View File

@@ -12,12 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "metrics_test_helper.h"
#include "src/condition/ConditionWizard.h"
#include "src/metrics/duration_helper/MaxDurationTracker.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <set>
#include <unordered_map>
@@ -32,13 +32,9 @@ using std::vector;
#ifdef __ANDROID__
class MockConditionWizard : public ConditionWizard {
public:
MOCK_METHOD2(
query,
ConditionState(const int conditionIndex,
const std::map<std::string, HashableDimensionKey>& conditionParameters));
};
namespace android {
namespace os {
namespace statsd {
TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -110,6 +106,9 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
EXPECT_EQ(5, buckets[0].duration_nanos());
}
} // namespace statsd
} // namespace os
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif

View File

@@ -12,18 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "metrics_test_helper.h"
#include "src/condition/ConditionWizard.h"
#include "src/metrics/duration_helper/OringDurationTracker.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <set>
#include <unordered_map>
#include <vector>
using namespace android::os::statsd;
using namespace testing;
using android::sp;
using std::set;
@@ -31,14 +30,9 @@ using std::unordered_map;
using std::vector;
#ifdef __ANDROID__
class MockConditionWizard : public ConditionWizard {
public:
MOCK_METHOD2(
query,
ConditionState(const int conditionIndex,
const std::map<std::string, HashableDimensionKey>& conditionParameters));
};
namespace android {
namespace os {
namespace statsd {
TEST(OringDurationTrackerTest, TestDurationOverlap) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -93,7 +87,9 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) {
EXPECT_EQ(1u, buckets.size());
EXPECT_EQ(5, buckets[0].duration_nanos());
}
} // namespace statsd
} // namespace os
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif

View File

@@ -0,0 +1,35 @@
// Copyright (C) 2017 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.
#pragma once
#include "src/condition/ConditionWizard.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace android {
namespace os {
namespace statsd {
class MockConditionWizard : public ConditionWizard {
public:
MOCK_METHOD2(
query,
ConditionState(const int conditionIndex,
const std::map<std::string, HashableDimensionKey>& conditionParameters));
};
} // namespace statsd
} // namespace os
} // namespace android