diff options
| author | 2017-11-07 03:05:58 +0000 | |
|---|---|---|
| committer | 2017-11-07 03:05:58 +0000 | |
| commit | 48b31da52661ffa4724f84e2bf71432e09539e3a (patch) | |
| tree | 080c4f211169039636125d3c8d982c4e7609e82b | |
| parent | 843f07e2d31e64b57357bbfc106b5c3a28315332 (diff) | |
| parent | 93fe3a34a02c673eaee4a2d18565ba8df20685cb (diff) | |
Merge "Add unit tests for CountMetricProducer, EventMetricProducer"
26 files changed, 607 insertions, 256 deletions
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 86f353bc615c..87d318bb3493 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -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 := \ diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index c0cedb1b467e..8910523aeda3 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -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; } diff --git a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp index 38953f182586..3608ee459611 100644 --- a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp +++ b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp @@ -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(), diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 003b5c41a507..43543cceeb1c 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -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>(); - - 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}); + 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}); 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); } diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 8220fcb95a8c..913b906986c3 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -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() { diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index df75d9f2e0b1..298494049628 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -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; }; diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 6f5db5178af0..100a7a4489dd 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -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): diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 473a4ba4f428..3bfc7242552a 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -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 diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 340f503fd651..09132bf58fa8 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -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 diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index febf25d45cc2..12ff58e7bd6a 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -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(); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index cbae1d343f03..677ae38bda18 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -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 diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 7dd0e38a0533..0fc2b5bbdc09 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -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; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index bd638b45eccd..285c8f41b317 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -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(); diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 3b117ec4e307..6ba726f4e6eb 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -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. diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 9b708dd63275..80b325f6e701 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -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; diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 44cd6371199e..63e2c338488e 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -96,4 +96,3 @@ private: } // namespace statsd } // namespace os } // namespace android - diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index ca33371e033a..07a078f40485 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -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; - - 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.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); + 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()); + + 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()); + } + + 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()); - - StatsLogReport report; - report.set_metric_id(mMetric.metric_id()); - report.set_start_report_nanos(mStartTimeNs); - - // 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(); - - 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("metric %lld dump report now...", mMetric.metric_id()); + + StatsLogReport report; + report.set_metric_id(mMetric.metric_id()); + report.set_start_report_nanos(mStartTimeNs); + report.set_end_report_nanos(mCurrentBucketStartTimeNs); + + 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; + } - VLOG(" dimension key %s", hashableKey.c_str()); - addSlicedCounterToReport(*wrapper, it->second, pair.second); - } - return report; - // TODO: Clear mPastBuckets, mDimensionKeyMap once the report is dumped. + VLOG(" dimension key %s", hashableKey.c_str()); + addSlicedCounterToReport(*wrapper, it->second, pair.second); + } + 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
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 865398127b47..548cd44d8500 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -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{}; diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index a0fdcdca9c34..ca9cdfb47f0a 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -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); } diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h index d3d7e3709644..a9507bf5a5c7 100644 --- a/cmds/statsd/src/stats_util.h +++ b/cmds/statsd/src/stats_util.h @@ -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; diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp index fdfe8ef46853..d0898b0fa865 100644 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp @@ -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; diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp new file mode 100644 index 000000000000..5a4ee7352eac --- /dev/null +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -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 diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp new file mode 100644 index 000000000000..76dbc73d71b4 --- /dev/null +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -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 diff --git a/cmds/statsd/tests/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index ae8bf4265235..f2abe7b70720 100644 --- a/cmds/statsd/tests/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -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 diff --git a/cmds/statsd/tests/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index 0b7981935ab1..338d55d0dcc6 100644 --- a/cmds/statsd/tests/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -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 diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h new file mode 100644 index 000000000000..5fd7d621637d --- /dev/null +++ b/cmds/statsd/tests/metrics/metrics_test_helper.h @@ -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 |