Merge "Split buckets on boot complete" into rvc-dev

This commit is contained in:
Tej Singh
2020-04-22 01:16:28 +00:00
committed by Android (Google) Code Review
23 changed files with 917 additions and 449 deletions

View File

@@ -104,7 +104,7 @@ cc_defaults {
"src/subscriber/IncidentdReporter.cpp", "src/subscriber/IncidentdReporter.cpp",
"src/subscriber/SubscriberReporter.cpp", "src/subscriber/SubscriberReporter.cpp",
"src/uid_data.proto", "src/uid_data.proto",
"src/utils/NamedLatch.cpp", "src/utils/MultiConditionTrigger.cpp",
], ],
local_include_dirs: [ local_include_dirs: [
@@ -366,7 +366,7 @@ cc_test {
"tests/StatsService_test.cpp", "tests/StatsService_test.cpp",
"tests/storage/StorageManager_test.cpp", "tests/storage/StorageManager_test.cpp",
"tests/UidMap_test.cpp", "tests/UidMap_test.cpp",
"tests/utils/NamedLatch_test.cpp", "tests/utils/MultiConditionTrigger_test.cpp",
], ],
static_libs: [ static_libs: [

View File

@@ -1055,8 +1055,8 @@ int64_t StatsLogProcessor::getLastReportTimeNs(const ConfigKey& key) {
void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk,
const int uid, const int64_t version) { const int uid, const int64_t version) {
std::lock_guard<std::mutex> lock(mMetricsMutex); std::lock_guard<std::mutex> lock(mMetricsMutex);
ALOGW("Received app upgrade"); VLOG("Received app upgrade");
for (auto it : mMetricsManagers) { for (const auto& it : mMetricsManagers) {
it.second->notifyAppUpgrade(eventTimeNs, apk, uid, version); it.second->notifyAppUpgrade(eventTimeNs, apk, uid, version);
} }
} }
@@ -1064,20 +1064,28 @@ void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const strin
void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
const int uid) { const int uid) {
std::lock_guard<std::mutex> lock(mMetricsMutex); std::lock_guard<std::mutex> lock(mMetricsMutex);
ALOGW("Received app removed"); VLOG("Received app removed");
for (auto it : mMetricsManagers) { for (const auto& it : mMetricsManagers) {
it.second->notifyAppRemoved(eventTimeNs, apk, uid); it.second->notifyAppRemoved(eventTimeNs, apk, uid);
} }
} }
void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) { void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex); std::lock_guard<std::mutex> lock(mMetricsMutex);
ALOGW("Received uid map"); VLOG("Received uid map");
for (auto it : mMetricsManagers) { for (const auto& it : mMetricsManagers) {
it.second->onUidMapReceived(eventTimeNs); it.second->onUidMapReceived(eventTimeNs);
} }
} }
void StatsLogProcessor::onStatsdInitCompleted(const int64_t& elapsedTimeNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
VLOG("Received boot completed signal");
for (const auto& it : mMetricsManagers) {
it.second->onStatsdInitCompleted(elapsedTimeNs);
}
}
void StatsLogProcessor::noteOnDiskData(const ConfigKey& key) { void StatsLogProcessor::noteOnDiskData(const ConfigKey& key) {
std::lock_guard<std::mutex> lock(mMetricsMutex); std::lock_guard<std::mutex> lock(mMetricsMutex);
mOnDiskDataConfigs.insert(key); mOnDiskDataConfigs.insert(key);

View File

@@ -120,6 +120,11 @@ public:
/* Notify all MetricsManagers of uid map snapshots received */ /* Notify all MetricsManagers of uid map snapshots received */
void onUidMapReceived(const int64_t& eventTimeNs) override; void onUidMapReceived(const int64_t& eventTimeNs) override;
/* Notify all metrics managers of boot completed
* This will force a bucket split when the boot is finished.
*/
void onStatsdInitCompleted(const int64_t& elapsedTimeNs);
// Reset all configs. // Reset all configs.
void resetConfigs(); void resetConfigs();

View File

@@ -118,7 +118,8 @@ StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQ
} }
})), })),
mEventQueue(queue), mEventQueue(queue),
mBootCompleteLatch({kBootCompleteTag, kUidMapReceivedTag, kAllPullersRegisteredTag}), mBootCompleteTrigger({kBootCompleteTag, kUidMapReceivedTag, kAllPullersRegisteredTag},
[this]() { mProcessor->onStatsdInitCompleted(getElapsedRealtimeNs()); }),
mStatsCompanionServiceDeathRecipient( mStatsCompanionServiceDeathRecipient(
AIBinder_DeathRecipient_new(StatsService::statsCompanionServiceDied)) { AIBinder_DeathRecipient_new(StatsService::statsCompanionServiceDied)) {
mUidMap = UidMap::getInstance(); mUidMap = UidMap::getInstance();
@@ -165,12 +166,6 @@ StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQ
std::thread pushedEventThread([this] { readLogs(); }); std::thread pushedEventThread([this] { readLogs(); });
pushedEventThread.detach(); pushedEventThread.detach();
} }
std::thread bootCompletedThread([this] {
mBootCompleteLatch.wait();
VLOG("In the boot completed thread");
});
bootCompletedThread.detach();
} }
StatsService::~StatsService() { StatsService::~StatsService() {
@@ -946,7 +941,7 @@ Status StatsService::informAllUidData(const ScopedFileDescriptor& fd) {
packageNames, packageNames,
installers); installers);
mBootCompleteLatch.countDown(kUidMapReceivedTag); mBootCompleteTrigger.markComplete(kUidMapReceivedTag);
VLOG("StatsService::informAllUidData UidData proto parsed successfully."); VLOG("StatsService::informAllUidData UidData proto parsed successfully.");
return Status::ok(); return Status::ok();
} }
@@ -1066,7 +1061,7 @@ Status StatsService::bootCompleted() {
ENFORCE_UID(AID_SYSTEM); ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::bootCompleted was called"); VLOG("StatsService::bootCompleted was called");
mBootCompleteLatch.countDown(kBootCompleteTag); mBootCompleteTrigger.markComplete(kBootCompleteTag);
return Status::ok(); return Status::ok();
} }
@@ -1226,7 +1221,7 @@ Status StatsService::allPullersFromBootRegistered() {
ENFORCE_UID(AID_SYSTEM); ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::allPullersFromBootRegistered was called"); VLOG("StatsService::allPullersFromBootRegistered was called");
mBootCompleteLatch.countDown(kAllPullersRegisteredTag); mBootCompleteTrigger.markComplete(kAllPullersRegisteredTag);
return Status::ok(); return Status::ok();
} }

View File

@@ -33,7 +33,7 @@
#include "packages/UidMap.h" #include "packages/UidMap.h"
#include "shell/ShellSubscriber.h" #include "shell/ShellSubscriber.h"
#include "statscompanion_util.h" #include "statscompanion_util.h"
#include "utils/NamedLatch.h" #include "utils/MultiConditionTrigger.h"
using namespace android; using namespace android;
using namespace android::os; using namespace android::os;
@@ -381,7 +381,7 @@ private:
mutable mutex mShellSubscriberMutex; mutable mutex mShellSubscriberMutex;
std::shared_ptr<LogEventQueue> mEventQueue; std::shared_ptr<LogEventQueue> mEventQueue;
NamedLatch mBootCompleteLatch; MultiConditionTrigger mBootCompleteTrigger;
static const inline string kBootCompleteTag = "BOOT_COMPLETE"; static const inline string kBootCompleteTag = "BOOT_COMPLETE";
static const inline string kUidMapReceivedTag = "UID_MAP"; static const inline string kUidMapReceivedTag = "UID_MAP";
static const inline string kAllPullersRegisteredTag = "PULLERS_REGISTERED"; static const inline string kAllPullersRegisteredTag = "PULLERS_REGISTERED";
@@ -394,11 +394,14 @@ private:
FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid); FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs); FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp); FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade); FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval); FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit); FRIEND_TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit);
FRIEND_TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket); FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket); FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket); FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket); FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket);
}; };

View File

@@ -109,10 +109,11 @@ private:
FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition); FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition); FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced); FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade);
FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket);
FRIEND_TEST(CountMetricProducerTest, TestFirstBucket); FRIEND_TEST(CountMetricProducerTest, TestFirstBucket);
FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit); FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit);
FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket);
FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket);
}; };
} // namespace statsd } // namespace statsd

View File

@@ -154,12 +154,14 @@ private:
FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition); FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition); FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState); FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState);
FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade);
FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket);
FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade);
FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates);
FRIEND_TEST(DurationMetricTrackerTest, TestFirstBucket); FRIEND_TEST(DurationMetricTrackerTest, TestFirstBucket);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestSumDuration);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket,
TestSumDurationWithSplitInFollowingBucket);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket);
}; };
} // namespace statsd } // namespace statsd

View File

@@ -73,18 +73,23 @@ public:
bool pullSuccess, int64_t originalPullTimeNs) override; bool pullSuccess, int64_t originalPullTimeNs) override;
// GaugeMetric needs to immediately trigger another pull when we create the partial bucket. // GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, void notifyAppUpgrade(const int64_t& eventTimeNs) override {
const int64_t version) override {
std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex);
if (!mSplitBucketForAppUpgrade) { if (!mSplitBucketForAppUpgrade) {
return; return;
} }
if (eventTimeNs > getCurrentBucketEndTimeNs()) { flushLocked(eventTimeNs);
// Flush full buckets on the normal path up to the latest bucket boundary. if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
flushIfNeededLocked(eventTimeNs); pullAndMatchEventsLocked(eventTimeNs);
} }
flushCurrentBucketLocked(eventTimeNs, eventTimeNs); };
// GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
void onStatsdInitCompleted(const int64_t& eventTimeNs) override {
std::lock_guard<std::mutex> lock(mMutex);
flushLocked(eventTimeNs);
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
pullAndMatchEventsLocked(eventTimeNs); pullAndMatchEventsLocked(eventTimeNs);
} }
@@ -190,13 +195,14 @@ private:
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition); FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition); FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition); FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled); FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection); FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection);
FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket); FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket);
FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger); FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger);
FRIEND_TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput); FRIEND_TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput);
FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents);
FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled);
}; };
} // namespace statsd } // namespace statsd

View File

@@ -141,30 +141,25 @@ public:
} }
/** /**
* Forces this metric to split into a partial bucket right now. If we're past a full bucket, we * Force a partial bucket split on app upgrade
* first call the standard flushing code to flush up to the latest full bucket. Then we call
* the flush again when the end timestamp is forced to be now, and then after flushing, update
* the start timestamp to be now.
*/ */
virtual void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, virtual void notifyAppUpgrade(const int64_t& eventTimeNs) {
const int64_t version) {
std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex);
flushLocked(eventTimeNs);
if (eventTimeNs > getCurrentBucketEndTimeNs()) {
// Flush full buckets on the normal path up to the latest bucket boundary.
flushIfNeededLocked(eventTimeNs);
}
// Now flush a partial bucket.
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
// Don't update the current bucket number so that the anomaly tracker knows this bucket
// is a partial bucket and can merge it with the previous bucket.
}; };
void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) { void notifyAppRemoved(const int64_t& eventTimeNs) {
// Force buckets to split on removal also. // Force buckets to split on removal also.
notifyAppUpgrade(eventTimeNs, apk, uid, 0); notifyAppUpgrade(eventTimeNs);
}; };
/**
* Force a partial bucket split on boot complete.
*/
virtual void onStatsdInitCompleted(const int64_t& eventTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
flushLocked(eventTimeNs);
}
// Consume the parsed stats log entry that already matched the "what" of the metric. // Consume the parsed stats log entry that already matched the "what" of the metric.
void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) { void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex);
@@ -292,8 +287,7 @@ public:
// End: getters/setters // End: getters/setters
protected: protected:
/** /**
* Flushes the current bucket if the eventTime is after the current bucket's end time. This will * Flushes the current bucket if the eventTime is after the current bucket's end time.
also flush the current partial bucket in memory.
*/ */
virtual void flushIfNeededLocked(const int64_t& eventTime){}; virtual void flushIfNeededLocked(const int64_t& eventTime){};

View File

@@ -231,8 +231,8 @@ bool MetricsManager::isConfigValid() const {
void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
const int64_t version) { const int64_t version) {
// Inform all metric producers. // Inform all metric producers.
for (auto it : mAllMetricProducers) { for (const auto& it : mAllMetricProducers) {
it->notifyAppUpgrade(eventTimeNs, apk, uid, version); it->notifyAppUpgrade(eventTimeNs);
} }
// check if we care this package // check if we care this package
if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) { if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
@@ -252,8 +252,8 @@ void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string&
void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
const int uid) { const int uid) {
// Inform all metric producers. // Inform all metric producers.
for (auto it : mAllMetricProducers) { for (const auto& it : mAllMetricProducers) {
it->notifyAppRemoved(eventTimeNs, apk, uid); it->notifyAppRemoved(eventTimeNs);
} }
// check if we care this package // check if we care this package
if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) { if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
@@ -282,6 +282,13 @@ void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) {
initLogSourceWhiteList(); initLogSourceWhiteList();
} }
void MetricsManager::onStatsdInitCompleted(const int64_t& eventTimeNs) {
// Inform all metric producers.
for (const auto& it : mAllMetricProducers) {
it->onStatsdInitCompleted(eventTimeNs);
}
}
void MetricsManager::init() { void MetricsManager::init() {
for (const auto& producer : mAllMetricProducers) { for (const auto& producer : mAllMetricProducers) {
producer->prepareFirstBucket(); producer->prepareFirstBucket();

View File

@@ -70,6 +70,8 @@ public:
void onUidMapReceived(const int64_t& eventTimeNs); void onUidMapReceived(const int64_t& eventTimeNs);
void onStatsdInitCompleted(const int64_t& elapsedTimeNs);
void init(); void init();
vector<int32_t> getPullAtomUids(int32_t atomId) override; vector<int32_t> getPullAtomUids(int32_t atomId) override;

View File

@@ -69,8 +69,7 @@ public:
bool pullSuccess, int64_t originalPullTimeNs) override; bool pullSuccess, int64_t originalPullTimeNs) override;
// ValueMetric needs special logic if it's a pulled atom. // ValueMetric needs special logic if it's a pulled atom.
void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, void notifyAppUpgrade(const int64_t& eventTimeNs) override {
const int64_t version) override {
std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex);
if (!mSplitBucketForAppUpgrade) { if (!mSplitBucketForAppUpgrade) {
return; return;
@@ -81,6 +80,15 @@ public:
flushCurrentBucketLocked(eventTimeNs, eventTimeNs); flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
}; };
// ValueMetric needs special logic if it's a pulled atom.
void onStatsdInitCompleted(const int64_t& eventTimeNs) override {
std::lock_guard<std::mutex> lock(mMutex);
if (mIsPulled && mCondition) {
pullAndMatchEventsLocked(eventTimeNs);
}
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
};
void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey, void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey,
int oldState, int newState) override; int oldState, int newState) override;
@@ -256,7 +264,6 @@ private:
FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection); FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange); FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange); FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition); FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition); FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
@@ -269,10 +276,8 @@ private:
FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled); FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled);
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition); FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket); FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
FRIEND_TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid);
FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff); FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff);
FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff); FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff);
FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated);
FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries); FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries);
FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse); FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse);
FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue); FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue);
@@ -283,15 +288,12 @@ private:
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset); FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset); FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering); FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse);
FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled); FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg); FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax); FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin); FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum); FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition); FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition); FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded); FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded);
FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange); FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange);
@@ -313,6 +315,14 @@ private:
FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit); FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit);
FRIEND_TEST(ValueMetricProducerTest_BucketDrop, FRIEND_TEST(ValueMetricProducerTest_BucketDrop,
TestInvalidBucketWhenAccumulateEventWrongBucket); TestInvalidBucketWhenAccumulateEventWrongBucket);
FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket);
FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid);
FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated);
FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPushedEvents);
FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValue);
FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse);
friend class ValueMetricProducerTestHelper; friend class ValueMetricProducerTestHelper;
}; };

View File

@@ -15,7 +15,9 @@
*/ */
#define DEBUG false // STOPSHIP if true #define DEBUG false // STOPSHIP if true
#include "NamedLatch.h" #include "MultiConditionTrigger.h"
#include <thread>
using namespace std; using namespace std;
@@ -23,26 +25,33 @@ namespace android {
namespace os { namespace os {
namespace statsd { namespace statsd {
NamedLatch::NamedLatch(const set<string>& eventNames) : mRemainingEventNames(eventNames) { MultiConditionTrigger::MultiConditionTrigger(const set<string>& conditionNames,
function<void()> trigger)
: mRemainingConditionNames(conditionNames),
mTrigger(trigger),
mCompleted(mRemainingConditionNames.empty()) {
if (mCompleted) {
thread executorThread([this] { mTrigger(); });
executorThread.detach();
}
} }
void NamedLatch::countDown(const string& eventName) { void MultiConditionTrigger::markComplete(const string& conditionName) {
bool notify = false; bool doTrigger = false;
{ {
lock_guard<mutex> lg(mMutex); lock_guard<mutex> lg(mMutex);
mRemainingEventNames.erase(eventName); if (mCompleted) {
notify = mRemainingEventNames.empty(); return;
} }
if (notify) { mRemainingConditionNames.erase(conditionName);
mConditionVariable.notify_all(); mCompleted = mRemainingConditionNames.empty();
doTrigger = mCompleted;
}
if (doTrigger) {
std::thread executorThread([this] { mTrigger(); });
executorThread.detach();
} }
} }
void NamedLatch::wait() const {
unique_lock<mutex> unique_lk(mMutex);
mConditionVariable.wait(unique_lk, [this] { return mRemainingEventNames.empty(); });
}
} // namespace statsd } // namespace statsd
} // namespace os } // namespace os
} // namespace android } // namespace android

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2020 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 <gtest/gtest_prod.h>
#include <mutex>
#include <set>
namespace android {
namespace os {
namespace statsd {
/**
* This class provides a utility to wait for a set of named conditions to occur.
*
* It will execute the trigger runnable in a detached thread once all conditions have been marked
* true.
*/
class MultiConditionTrigger {
public:
explicit MultiConditionTrigger(const std::set<std::string>& conditionNames,
std::function<void()> trigger);
MultiConditionTrigger(const MultiConditionTrigger&) = delete;
MultiConditionTrigger& operator=(const MultiConditionTrigger&) = delete;
// Mark a specific condition as true. If this condition has called markComplete already or if
// the event was not specified in the constructor, the function is a no-op.
void markComplete(const std::string& eventName);
private:
mutable std::mutex mMutex;
std::set<std::string> mRemainingConditionNames;
std::function<void()> mTrigger;
bool mCompleted;
FRIEND_TEST(MultiConditionTriggerTest, TestCountDownCalledBySameEventName);
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,58 +0,0 @@
/*
* Copyright (C) 2020 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 <gtest/gtest_prod.h>
#include <condition_variable>
#include <mutex>
#include <set>
namespace android {
namespace os {
namespace statsd {
/**
* This class provides a threading primitive similar to a latch.
* The primary difference is that it waits for named events to occur instead of waiting for
* N threads to reach a certain point.
*
* It uses a condition variable under the hood.
*/
class NamedLatch {
public:
explicit NamedLatch(const std::set<std::string>& eventNames);
NamedLatch(const NamedLatch&) = delete;
NamedLatch& operator=(const NamedLatch&) = delete;
// Mark a specific event as completed. If this event has called countDown already or if the
// event was not specified in the constructor, the function is a no-op.
void countDown(const std::string& eventName);
// Blocks the calling thread until all events in eventNames have called countDown.
void wait() const;
private:
mutable std::mutex mMutex;
mutable std::condition_variable mConditionVariable;
std::set<std::string> mRemainingEventNames;
FRIEND_TEST(NamedLatchTest, TestCountDownCalledBySameEventName);
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -89,6 +89,7 @@ StatsdConfig MakeValueMetricConfig(int64_t minTime) {
valueMetric->set_bucket(FIVE_MINUTES); valueMetric->set_bucket(FIVE_MINUTES);
valueMetric->set_min_bucket_size_nanos(minTime); valueMetric->set_min_bucket_size_nanos(minTime);
valueMetric->set_use_absolute_value_on_reset(true); valueMetric->set_use_absolute_value_on_reset(true);
valueMetric->set_skip_zero_diff_output(false);
return config; return config;
} }
@@ -217,6 +218,35 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
} }
TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot) {
shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
SendConfig(service, MakeConfig());
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
// Goes into the first bucket
service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + NS_PER_SEC, 100).get());
int64_t bootCompleteTimeNs = start + 2 * NS_PER_SEC;
service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs);
// Goes into the second bucket.
service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3 * NS_PER_SEC, 100).get());
ConfigMetricsReport report = GetReports(service->mProcessor, start + 4 * NS_PER_SEC);
backfillStartEndTimestamp(&report);
ASSERT_EQ(1, report.metrics_size());
ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
EXPECT_TRUE(report.metrics(0)
.count_metrics()
.data(0)
.bucket_info(0)
.has_start_bucket_elapsed_nanos());
EXPECT_EQ(MillisToNano(NanoToMillis(bootCompleteTimeNs)),
report.metrics(0).count_metrics().data(0).bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
}
TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
service->mPullerManager->RegisterPullAtomCallback( service->mPullerManager->RegisterPullAtomCallback(
@@ -229,13 +259,22 @@ TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
// initialized with. // initialized with.
service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2, int64_t appUpgradeTimeNs = 5 * 60 * NS_PER_SEC + start + 2 * NS_PER_SEC;
String16("v2"), String16("")); service->mUidMap->updateApp(appUpgradeTimeNs, String16(kApp1.c_str()), 1, 2, String16("v2"),
String16(""));
ConfigMetricsReport report = ConfigMetricsReport report =
GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true); GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC);
backfillStartEndTimestamp(&report);
EXPECT_EQ(1, report.metrics_size()); EXPECT_EQ(1, report.metrics_size());
EXPECT_EQ(0, report.metrics(0).value_metrics().skipped_size()); EXPECT_EQ(0, report.metrics(0).value_metrics().skipped_size());
// The fake subsystem state sleep puller returns two atoms.
ASSERT_EQ(2, report.metrics(0).value_metrics().data_size());
ASSERT_EQ(2, report.metrics(0).value_metrics().data(0).bucket_info_size());
EXPECT_EQ(MillisToNano(NanoToMillis(appUpgradeTimeNs)),
report.metrics(0).value_metrics().data(0).bucket_info(1).end_bucket_elapsed_nanos());
} }
TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
@@ -249,13 +288,13 @@ TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with. // initialized with.
const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2; const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2 * NS_PER_SEC;
service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"), service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
String16("")); String16(""));
ConfigMetricsReport report = ConfigMetricsReport report =
GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true); GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC);
backfillStartEndTimestamp(&report); backfillStartEndTimestamp(&report);
ASSERT_EQ(1, report.metrics_size()); ASSERT_EQ(1, report.metrics_size());
@@ -264,10 +303,49 @@ TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
// Can't test the start time since it will be based on the actual time when the pulling occurs. // Can't test the start time since it will be based on the actual time when the pulling occurs.
EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)), EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos()); report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos());
ASSERT_EQ(2, report.metrics(0).value_metrics().data_size());
EXPECT_EQ(1, report.metrics(0).value_metrics().data(0).bucket_info_size());
}
TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket) {
shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
// Initial pull will fail since puller is not registered.
SendConfig(service, MakeValueMetricConfig(0));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
service->mPullerManager->RegisterPullAtomCallback(
/*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
SharedRefBase::make<FakeSubsystemSleepCallback>());
int64_t bootCompleteTimeNs = start + NS_PER_SEC;
service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs);
service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100);
backfillStartEndTimestamp(&report);
// First bucket is dropped due to the initial pull failing
ASSERT_EQ(1, report.metrics_size());
EXPECT_EQ(1, report.metrics(0).value_metrics().skipped_size());
EXPECT_EQ(MillisToNano(NanoToMillis(bootCompleteTimeNs)),
report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos());
// The fake subsystem state sleep puller returns two atoms.
ASSERT_EQ(2, report.metrics(0).value_metrics().data_size());
ASSERT_EQ(1, report.metrics(0).value_metrics().data(0).bucket_info_size());
EXPECT_EQ(
MillisToNano(NanoToMillis(bootCompleteTimeNs)),
report.metrics(0).value_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos());
} }
TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) { TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
service->mPullerManager->RegisterPullAtomCallback(
/*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
SharedRefBase::make<FakeSubsystemSleepCallback>());
// Partial buckets don't occur when app is first installed. // Partial buckets don't occur when app is first installed.
service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeGaugeMetricConfig(0)); SendConfig(service, MakeGaugeMetricConfig(0));
@@ -278,16 +356,22 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2, service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
String16("v2"), String16("")); String16("v2"), String16(""));
ConfigMetricsReport report = ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100);
GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true); backfillStartEndTimestamp(&report);
EXPECT_EQ(1, report.metrics_size()); ASSERT_EQ(1, report.metrics_size());
EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size()); EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
// The fake subsystem state sleep puller returns two atoms.
ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size());
EXPECT_EQ(2, report.metrics(0).gauge_metrics().data(0).bucket_info_size());
} }
TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
// Partial buckets don't occur when app is first installed. // Partial buckets don't occur when app is first installed.
service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
service->mPullerManager->RegisterPullAtomCallback(
/*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
SharedRefBase::make<FakeSubsystemSleepCallback>());
SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */)); SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with. // initialized with.
@@ -298,7 +382,7 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
String16("")); String16(""));
ConfigMetricsReport report = ConfigMetricsReport report =
GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true); GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC);
backfillStartEndTimestamp(&report); backfillStartEndTimestamp(&report);
ASSERT_EQ(1, report.metrics_size()); ASSERT_EQ(1, report.metrics_size());
ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size()); ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
@@ -306,6 +390,38 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos()); EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos());
EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)), EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
report.metrics(0).gauge_metrics().skipped(0).end_bucket_elapsed_nanos()); report.metrics(0).gauge_metrics().skipped(0).end_bucket_elapsed_nanos());
ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size());
EXPECT_EQ(1, report.metrics(0).gauge_metrics().data(0).bucket_info_size());
}
TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket) {
shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
// Initial pull will fail since puller hasn't been registered.
SendConfig(service, MakeGaugeMetricConfig(0));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
service->mPullerManager->RegisterPullAtomCallback(
/*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
SharedRefBase::make<FakeSubsystemSleepCallback>());
int64_t bootCompleteTimeNs = start + NS_PER_SEC;
service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs);
service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100);
backfillStartEndTimestamp(&report);
ASSERT_EQ(1, report.metrics_size());
EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
// The fake subsystem state sleep puller returns two atoms.
ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size());
// No data in the first bucket, so nothing is reported
ASSERT_EQ(1, report.metrics(0).gauge_metrics().data(0).bucket_info_size());
EXPECT_EQ(
MillisToNano(NanoToMillis(bootCompleteTimeNs)),
report.metrics(0).gauge_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos());
} }
#else #else

View File

@@ -38,9 +38,9 @@ namespace android {
namespace os { namespace os {
namespace statsd { namespace statsd {
const ConfigKey kConfigKey(0, 12345);
namespace { namespace {
const ConfigKey kConfigKey(0, 12345);
void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent* statsEvent = AStatsEvent_obtain();
@@ -61,6 +61,13 @@ void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string ui
} // namespace } // namespace
// Setup for parameterized tests.
class CountMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
INSTANTIATE_TEST_SUITE_P(CountMetricProducerTest_PartialBucket,
CountMetricProducerTest_PartialBucket,
testing::Values(APP_UPGRADE, BOOT_COMPLETE));
TEST(CountMetricProducerTest, TestFirstBucket) { TEST(CountMetricProducerTest, TestFirstBucket) {
CountMetric metric; CountMetric metric;
metric.set_id(1); metric.set_id(1);
@@ -237,11 +244,11 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
EXPECT_EQ(1LL, bucketInfo.mCount); EXPECT_EQ(1LL, bucketInfo.mCount);
} }
TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket) {
sp<AlarmMonitor> alarmMonitor; sp<AlarmMonitor> alarmMonitor;
int64_t bucketStartTimeNs = 10000000000; int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; int64_t eventTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
int tagId = 1; int tagId = 1;
int conditionTagId = 2; int conditionTagId = 2;
@@ -260,22 +267,30 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr); EXPECT_TRUE(anomalyTracker != nullptr);
// Bucket is flushed yet. // Bucket is not flushed yet.
LogEvent event1(/*uid=*/0, /*pid=*/0); LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
// App upgrade forces bucket flush. // App upgrade or boot complete forces bucket flush.
// Check that there's a past bucket and the bucket end is not adjusted. // Check that there's a past bucket and the bucket end is not adjusted.
countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); switch (GetParam()) {
case APP_UPGRADE:
countProducer.notifyAppUpgrade(eventTimeNs);
break;
case BOOT_COMPLETE:
countProducer.onStatsdInitCompleted(eventTimeNs);
break;
}
EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ((long long)bucketStartTimeNs, EXPECT_EQ(bucketStartTimeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
EXPECT_EQ((long long)eventUpgradeTimeNs, EXPECT_EQ(eventTimeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(0, countProducer.getCurrentBucketNum());
EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
// Anomaly tracker only contains full buckets. // Anomaly tracker only contains full buckets.
EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
@@ -285,7 +300,8 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
makeLogEvent(&event2, bucketStartTimeNs + 59 * NS_PER_SEC + 10, tagId, /*uid=*/"222"); makeLogEvent(&event2, bucketStartTimeNs + 59 * NS_PER_SEC + 10, tagId, /*uid=*/"222");
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(0, countProducer.getCurrentBucketNum());
EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
// Third event in following bucket. // Third event in following bucket.
@@ -294,13 +310,14 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(1, countProducer.getCurrentBucketNum());
EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
} }
TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) { TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket) {
int64_t bucketStartTimeNs = 10000000000; int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; int64_t eventTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
int tagId = 1; int tagId = 1;
int conditionTagId = 2; int conditionTagId = 2;
@@ -319,15 +336,23 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) {
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
// App upgrade forces bucket flush. // App upgrade or boot complete forces bucket flush.
// Check that there's a past bucket and the bucket end is not adjusted. // Check that there's a past bucket and the bucket end is not adjusted since the upgrade
countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); // occurred after the bucket end time.
switch (GetParam()) {
case APP_UPGRADE:
countProducer.notifyAppUpgrade(eventTimeNs);
break;
case BOOT_COMPLETE:
countProducer.onStatsdInitCompleted(eventTimeNs);
break;
}
EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ((int64_t)bucketStartTimeNs, EXPECT_EQ(bucketStartTimeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
// Next event occurs in same bucket as partial bucket created. // Next event occurs in same bucket as partial bucket created.
LogEvent event2(/*uid=*/0, /*pid=*/0); LogEvent event2(/*uid=*/0, /*pid=*/0);
@@ -340,7 +365,7 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) {
makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333"); makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ((int64_t)eventUpgradeTimeNs, EXPECT_EQ((int64_t)eventTimeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs); countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs); countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs);

View File

@@ -41,10 +41,10 @@ namespace android {
namespace os { namespace os {
namespace statsd { namespace statsd {
const ConfigKey kConfigKey(0, 12345);
namespace { namespace {
const ConfigKey kConfigKey(0, 12345);
void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId); AStatsEvent_setAtomId(statsEvent, atomId);
@@ -55,6 +55,13 @@ void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
} // namespace } // namespace
// Setup for parameterized tests.
class DurationMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
INSTANTIATE_TEST_SUITE_P(DurationMetricProducerTest_PartialBucket,
DurationMetricProducerTest_PartialBucket,
testing::Values(APP_UPGRADE, BOOT_COMPLETE));
TEST(DurationMetricTrackerTest, TestFirstBucket) { TEST(DurationMetricTrackerTest, TestFirstBucket) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
DurationMetric metric; DurationMetric metric;
@@ -205,7 +212,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) {
EXPECT_EQ(1LL, buckets2[0].mDuration); EXPECT_EQ(1LL, buckets2[0].mDuration);
} }
TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) {
/** /**
* The duration starts from the first bucket, through the two partial buckets (10-70sec), * The duration starts from the first bucket, through the two partial buckets (10-70sec),
* another bucket, and ends at the beginning of the next full bucket. * another bucket, and ends at the beginning of the next full bucket.
@@ -217,15 +224,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) {
*/ */
int64_t bucketStartTimeNs = 10000000000; int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
int tagId = 1; int tagId = 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
DurationMetric metric; DurationMetric metric;
metric.set_id(1); metric.set_id(1);
@@ -238,32 +237,47 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) {
3 /* stop_all index */, false /*nesting*/, wizard, 3 /* stop_all index */, false /*nesting*/, wizard,
dimensions, bucketStartTimeNs, bucketStartTimeNs); dimensions, bucketStartTimeNs, bucketStartTimeNs);
int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1); durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
switch (GetParam()) {
case APP_UPGRADE:
durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
break;
case BOOT_COMPLETE:
durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
break;
}
EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
std::vector<DurationBucket> buckets = std::vector<DurationBucket> buckets =
durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs); EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketEndNs);
EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration); EXPECT_EQ(partialBucketSplitTimeNs - startTimeNs, buckets[0].mDuration);
EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(0, durationProducer.getCurrentBucketNum());
// We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(3UL, buckets.size()); EXPECT_EQ(3UL, buckets.size());
EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs); EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - partialBucketSplitTimeNs, buckets[1].mDuration);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
EXPECT_EQ(bucketSizeNs, buckets[2].mDuration); EXPECT_EQ(bucketSizeNs, buckets[2].mDuration);
} }
TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollowingBucket) {
/** /**
* Expected buckets (start at 11s, upgrade at 75s, end at 135s): * Expected buckets (start at 11s, upgrade at 75s, end at 135s):
* - [10,70]: 59 secs * - [10,70]: 59 secs
@@ -272,15 +286,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
*/ */
int64_t bucketStartTimeNs = 10000000000; int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
int tagId = 1; int tagId = 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
DurationMetric metric; DurationMetric metric;
metric.set_id(1); metric.set_id(1);
@@ -293,11 +299,22 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
3 /* stop_all index */, false /*nesting*/, wizard, 3 /* stop_all index */, false /*nesting*/, wizard,
dimensions, bucketStartTimeNs, bucketStartTimeNs); dimensions, bucketStartTimeNs, bucketStartTimeNs);
int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1); durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
switch (GetParam()) {
case APP_UPGRADE:
durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
break;
case BOOT_COMPLETE:
durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
break;
}
EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
std::vector<DurationBucket> buckets = std::vector<DurationBucket> buckets =
durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
@@ -305,32 +322,29 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs); EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketEndNs);
EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration); EXPECT_EQ(partialBucketSplitTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration);
EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(1, durationProducer.getCurrentBucketNum());
// We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(3UL, buckets.size()); EXPECT_EQ(3UL, buckets.size());
EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs); EXPECT_EQ(partialBucketSplitTimeNs, buckets[2].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - partialBucketSplitTimeNs,
buckets[2].mDuration);
} }
TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) {
sp<AlarmMonitor> alarmMonitor; sp<AlarmMonitor> alarmMonitor;
int64_t bucketStartTimeNs = 10000000000; int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
int64_t startTimeNs = bucketStartTimeNs + 1;
int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
int tagId = 1; int tagId = 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
// Setup metric with alert. // Setup metric with alert.
DurationMetric metric; DurationMetric metric;
@@ -351,27 +365,35 @@ TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) {
sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr); EXPECT_TRUE(anomalyTracker != nullptr);
int64_t startTimeNs = bucketStartTimeNs + 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1); durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
switch (GetParam()) {
case APP_UPGRADE:
durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
break;
case BOOT_COMPLETE:
durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
break;
}
// We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs,
anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
} }
TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) { TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) {
int64_t bucketStartTimeNs = 10000000000; int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
int64_t startTimeNs = bucketStartTimeNs + 1;
int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
int tagId = 1; int tagId = 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
DurationMetric metric; DurationMetric metric;
metric.set_id(1); metric.set_id(1);
@@ -385,15 +407,30 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) {
3 /* stop_all index */, false /*nesting*/, wizard, 3 /* stop_all index */, false /*nesting*/, wizard,
dimensions, bucketStartTimeNs, bucketStartTimeNs); dimensions, bucketStartTimeNs, bucketStartTimeNs);
int64_t startTimeNs = bucketStartTimeNs + 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1); durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
switch (GetParam()) {
case APP_UPGRADE:
durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
break;
case BOOT_COMPLETE:
durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
break;
}
EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(0, durationProducer.getCurrentBucketNum());
// We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
@@ -406,18 +443,10 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) {
EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
} }
TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) { TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket) {
int64_t bucketStartTimeNs = 10000000000; int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
int64_t startTimeNs = bucketStartTimeNs + 1;
int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC;
int tagId = 1; int tagId = 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
DurationMetric metric; DurationMetric metric;
metric.set_id(1); metric.set_id(1);
@@ -431,24 +460,39 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) {
3 /* stop_all index */, false /*nesting*/, wizard, 3 /* stop_all index */, false /*nesting*/, wizard,
dimensions, bucketStartTimeNs, bucketStartTimeNs); dimensions, bucketStartTimeNs, bucketStartTimeNs);
int64_t startTimeNs = bucketStartTimeNs + 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1); durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
switch (GetParam()) {
case APP_UPGRADE:
durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
break;
case BOOT_COMPLETE:
durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
break;
}
EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(1, durationProducer.getCurrentBucketNum());
// Stop occurs in the same partial bucket as created for the app upgrade. // Stop occurs in the same partial bucket as created for the app upgrade.
int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC;
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
std::vector<DurationBucket> buckets = std::vector<DurationBucket> buckets =
durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size()); EXPECT_EQ(1UL, buckets.size());
EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs); EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs);
EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
} }

View File

@@ -42,6 +42,8 @@ namespace android {
namespace os { namespace os {
namespace statsd { namespace statsd {
namespace {
const ConfigKey kConfigKey(0, 12345); const ConfigKey kConfigKey(0, 12345);
const int tagId = 1; const int tagId = 1;
const int64_t metricId = 123; const int64_t metricId = 123;
@@ -52,9 +54,8 @@ const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000L
const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; const int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
namespace {
shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t value1, string str1, shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t value1, string str1,
int32_t value2) { int32_t value2) {
AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent* statsEvent = AStatsEvent_obtain();
@@ -71,6 +72,13 @@ shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t v
} }
} // anonymous namespace } // anonymous namespace
// Setup for parameterized tests.
class GaugeMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
INSTANTIATE_TEST_SUITE_P(GaugeMetricProducerTest_PartialBucket,
GaugeMetricProducerTest_PartialBucket,
testing::Values(APP_UPGRADE, BOOT_COMPLETE));
/* /*
* Tests that the first bucket works correctly * Tests that the first bucket works correctly
*/ */
@@ -194,7 +202,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) {
EXPECT_EQ(25L, it->mValue.int_value); EXPECT_EQ(25L, it->mValue.int_value);
} }
TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { TEST_P(GaugeMetricProducerTest_PartialBucket, TestPushedEvents) {
sp<AlarmMonitor> alarmMonitor; sp<AlarmMonitor> alarmMonitor;
GaugeMetric metric; GaugeMetric metric;
metric.set_id(metricId); metric.set_id(metricId);
@@ -230,11 +238,22 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); switch (GetParam()) {
case APP_UPGRADE:
gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
break;
case BOOT_COMPLETE:
gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
break;
}
EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucketStartTimeNs,
gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
EXPECT_EQ(partialBucketSplitTimeNs,
gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
EXPECT_EQ(eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
// Partial buckets are not sent to anomaly tracker. // Partial buckets are not sent to anomaly tracker.
EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
@@ -244,7 +263,11 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(bucketStartTimeNs,
gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
EXPECT_EQ(partialBucketSplitTimeNs,
gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
EXPECT_EQ((int64_t)partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
// Partial buckets are not sent to anomaly tracker. // Partial buckets are not sent to anomaly tracker.
EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
@@ -267,7 +290,7 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
} }
TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) {
GaugeMetric metric; GaugeMetric metric;
metric.set_id(metricId); metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE); metric.set_bucket(ONE_MINUTE);
@@ -293,7 +316,8 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) {
.WillOnce(Invoke( .WillOnce(Invoke(
[](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) { [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear(); data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventUpgradeTimeNs, 2)); data->push_back(
CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 2));
return true; return true;
})); }));
@@ -311,10 +335,21 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) {
.mFields->begin() .mFields->begin()
->mValue.int_value); ->mValue.int_value);
gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); switch (GetParam()) {
case APP_UPGRADE:
gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
break;
case BOOT_COMPLETE:
gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
break;
}
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucketStartTimeNs,
gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
EXPECT_EQ(partialBucketSplitTimeNs,
gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin() EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front() ->second.front()
@@ -370,7 +405,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
.mFields->begin() .mFields->begin()
->mValue.int_value); ->mValue.int_value);
gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
EXPECT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);

View File

@@ -41,6 +41,8 @@ namespace android {
namespace os { namespace os {
namespace statsd { namespace statsd {
namespace {
const ConfigKey kConfigKey(0, 12345); const ConfigKey kConfigKey(0, 12345);
const int tagId = 1; const int tagId = 1;
const int64_t metricId = 123; const int64_t metricId = 123;
@@ -58,10 +60,18 @@ double epsilon = 0.001;
static void assertPastBucketValuesSingleKey( static void assertPastBucketValuesSingleKey(
const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets, const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets,
const std::initializer_list<int>& expectedValuesList, const std::initializer_list<int>& expectedValuesList,
const std::initializer_list<int64_t>& expectedDurationNsList) { const std::initializer_list<int64_t>& expectedDurationNsList,
std::vector<int> expectedValues(expectedValuesList); const std::initializer_list<int64_t>& expectedStartTimeNsList,
std::vector<int64_t> expectedDurationNs(expectedDurationNsList); const std::initializer_list<int64_t>& expectedEndTimeNsList) {
vector<int> expectedValues(expectedValuesList);
vector<int64_t> expectedDurationNs(expectedDurationNsList);
vector<int64_t> expectedStartTimeNs(expectedStartTimeNsList);
vector<int64_t> expectedEndTimeNs(expectedEndTimeNsList);
ASSERT_EQ(expectedValues.size(), expectedDurationNs.size()); ASSERT_EQ(expectedValues.size(), expectedDurationNs.size());
ASSERT_EQ(expectedValues.size(), expectedStartTimeNs.size());
ASSERT_EQ(expectedValues.size(), expectedEndTimeNs.size());
if (expectedValues.size() == 0) { if (expectedValues.size() == 0) {
ASSERT_EQ(0, mPastBuckets.size()); ASSERT_EQ(0, mPastBuckets.size());
return; return;
@@ -70,15 +80,21 @@ static void assertPastBucketValuesSingleKey(
ASSERT_EQ(1, mPastBuckets.size()); ASSERT_EQ(1, mPastBuckets.size());
ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size()); ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size());
auto buckets = mPastBuckets.begin()->second; const vector<ValueBucket>& buckets = mPastBuckets.begin()->second;
for (int i = 0; i < expectedValues.size(); i++) { for (int i = 0; i < expectedValues.size(); i++) {
EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value) EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value)
<< "Values differ at index " << i; << "Values differ at index " << i;
EXPECT_EQ(expectedDurationNs[i], buckets[i].mConditionTrueNs) EXPECT_EQ(expectedDurationNs[i], buckets[i].mConditionTrueNs)
<< "Condition duration value differ at index " << i; << "Condition duration value differ at index " << i;
EXPECT_EQ(expectedStartTimeNs[i], buckets[i].mBucketStartNs)
<< "Start time differs at index " << i;
EXPECT_EQ(expectedEndTimeNs[i], buckets[i].mBucketEndNs)
<< "End time differs at index " << i;
} }
} }
} // anonymous namespace
class ValueMetricProducerTestHelper { class ValueMetricProducerTestHelper {
public: public:
static sp<ValueMetricProducer> createValueProducerNoConditions( static sp<ValueMetricProducer> createValueProducerNoConditions(
@@ -191,6 +207,13 @@ public:
} }
}; };
// Setup for parameterized tests.
class ValueMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
INSTANTIATE_TEST_SUITE_P(ValueMetricProducerTest_PartialBucket,
ValueMetricProducerTest_PartialBucket,
testing::Values(APP_UPGRADE, BOOT_COMPLETE));
/* /*
* Tests that the first bucket works correctly * Tests that the first bucket works correctly
*/ */
@@ -325,9 +348,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs); EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs);
} }
TEST(ValueMetricProducerTest, TestPartialBucketCreated) { TEST_P(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialize bucket. // Initialize bucket.
.WillOnce(Invoke([](int tagId, const ConfigKey&, .WillOnce(Invoke([](int tagId, const ConfigKey&,
@@ -337,10 +361,12 @@ TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
return true; return true;
})) }))
// Partial bucket. // Partial bucket.
.WillOnce(Invoke([](int tagId, const ConfigKey&, .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
vector<std::shared_ptr<LogEvent>>* data, bool) { vector<std::shared_ptr<LogEvent>>* data,
bool) {
data->clear(); data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5)); data->push_back(
CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs + 8, 5));
return true; return true;
})); }));
@@ -354,19 +380,21 @@ TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs); valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs);
// Partial buckets created in 2nd bucket. // Partial buckets created in 2nd bucket.
valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1); switch (GetParam()) {
case APP_UPGRADE:
valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
break;
case BOOT_COMPLETE:
valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
break;
}
EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
EXPECT_EQ(1, valueProducer->getCurrentBucketNum());
// One full bucket and one partial bucket. assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1, 3},
EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); {bucketSizeNs, partialBucketSplitTimeNs - bucket2StartTimeNs},
vector<ValueBucket> buckets = valueProducer->mPastBuckets.begin()->second; {bucketStartTimeNs, bucket2StartTimeNs},
EXPECT_EQ(2UL, buckets.size()); {bucket2StartTimeNs, partialBucketSplitTimeNs});
// Full bucket (2 - 1)
EXPECT_EQ(1, buckets[0].values[0].long_value);
EXPECT_EQ(bucketSizeNs, buckets[0].mConditionTrueNs);
// Full bucket (5 - 3)
EXPECT_EQ(3, buckets[1].values[0].long_value);
// partial bucket [bucket2StartTimeNs, bucket2StartTimeNs + 2]
EXPECT_EQ(2, buckets[1].mConditionTrueNs);
} }
/* /*
@@ -613,7 +641,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
allData.clear(); allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
// has one slice // has one slice
EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
@@ -625,7 +654,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(10, curInterval.value.long_value);
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
// has one slice // has one slice
EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
@@ -636,10 +666,12 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curBaseInfo.hasBase);
valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1); valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1},
{bucketStartTimeNs, bucket2StartTimeNs},
{bucket2StartTimeNs, bucket3StartTimeNs});
} }
TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { TEST_P(ValueMetricProducerTest_PartialBucket, TestPushedEvents) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
UidMap uidMap; UidMap uidMap;
@@ -660,25 +692,46 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
valueProducer.notifyAppUpgrade(bucketStartTimeNs + 150, "ANY.APP", 1, 1); int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 150;
EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); switch (GetParam()) {
EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs); case APP_UPGRADE:
valueProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
break;
case BOOT_COMPLETE:
valueProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
break;
}
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10},
{partialBucketSplitTimeNs - bucketStartTimeNs},
{bucketStartTimeNs}, {partialBucketSplitTimeNs});
EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(0, valueProducer.getCurrentBucketNum());
// Event arrives after the bucket split.
LogEvent event2(/*uid=*/0, /*pid=*/0); LogEvent event2(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 10); CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 20);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs); assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10},
{partialBucketSplitTimeNs - bucketStartTimeNs},
{bucketStartTimeNs}, {partialBucketSplitTimeNs});
EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(0, valueProducer.getCurrentBucketNum());
// Next value should create a new bucket. // Next value should create a new bucket.
LogEvent event3(/*uid=*/0, /*pid=*/0); LogEvent event3(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 10); CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 5 * NS_PER_SEC, 10);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10, 20},
{partialBucketSplitTimeNs - bucketStartTimeNs,
bucket2StartTimeNs - partialBucketSplitTimeNs},
{bucketStartTimeNs, partialBucketSplitTimeNs},
{partialBucketSplitTimeNs, bucket2StartTimeNs});
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(1, valueProducer.getCurrentBucketNum());
} }
TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValue) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
UidMap uidMap; UidMap uidMap;
@@ -689,14 +742,16 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150;
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Return(true)) .WillOnce(Return(true))
.WillOnce(Invoke([](int tagId, const ConfigKey&, .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
vector<std::shared_ptr<LogEvent>>* data, bool) { vector<std::shared_ptr<LogEvent>>* data,
bool) {
data->clear(); data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 149, 120)); data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 120));
return true; return true;
})); }));
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
@@ -711,20 +766,27 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1); switch (GetParam()) {
EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); case APP_UPGRADE:
EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs); valueProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150}); break;
case BOOT_COMPLETE:
valueProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
break;
}
EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(1, valueProducer.getCurrentBucketNum());
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150}, {bucket2StartTimeNs},
{partialBucketSplitTimeNs});
allData.clear(); allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 150)); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 150));
valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(20L, EXPECT_EQ(2, valueProducer.getCurrentBucketNum());
valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value); assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30}, {150, bucketSizeNs - 150},
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30}, {bucket2StartTimeNs, partialBucketSplitTimeNs},
{150, bucketSizeNs - 150}); {partialBucketSplitTimeNs, bucket3StartTimeNs});
} }
TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
@@ -754,12 +816,12 @@ TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1); valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150);
EXPECT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
} }
TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) { TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -784,14 +846,21 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100); valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100);
EXPECT_FALSE(valueProducer->mCondition); EXPECT_FALSE(valueProducer->mCondition);
valueProducer->notifyAppUpgrade(bucket2StartTimeNs - 50, "ANY.APP", 1, 1); int64_t partialBucketSplitTimeNs = bucket2StartTimeNs - 50;
switch (GetParam()) {
case APP_UPGRADE:
valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
break;
case BOOT_COMPLETE:
valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
break;
}
// Expect one full buckets already done and starting a partial bucket. // Expect one full buckets already done and starting a partial bucket.
EXPECT_EQ(bucket2StartTimeNs - 50, valueProducer->mCurrentBucketStartTimeNs); EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
EXPECT_EQ(1UL, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(0, valueProducer->getCurrentBucketNum());
EXPECT_EQ(bucketStartTimeNs,
valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20},
{(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)}); {(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)},
{bucketStartTimeNs}, {partialBucketSplitTimeNs});
EXPECT_FALSE(valueProducer->mCondition); EXPECT_FALSE(valueProducer->mCondition);
} }
TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
@@ -834,7 +903,8 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
EXPECT_EQ(30, curInterval.value.long_value); EXPECT_EQ(30, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs); valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs}); assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
} }
TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
@@ -895,7 +965,8 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
EXPECT_EQ(50, curInterval.value.long_value); EXPECT_EQ(50, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs); valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20}); assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20}, {bucketStartTimeNs},
{bucket2StartTimeNs});
} }
TEST(ValueMetricProducerTest, TestAnomalyDetection) { TEST(ValueMetricProducerTest, TestAnomalyDetection) {
@@ -1012,7 +1083,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(23, curBaseInfo.base.long_value); EXPECT_EQ(23, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(false, curInterval.hasValue);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs},
{bucket2StartTimeNs}, {bucket3StartTimeNs});
// pull 3 come late. // pull 3 come late.
// The previous bucket gets closed with error. (Has start value 23, no ending) // The previous bucket gets closed with error. (Has start value 23, no ending)
@@ -1028,7 +1100,15 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(36, curBaseInfo.base.long_value); EXPECT_EQ(36, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(false, curInterval.hasValue);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs},
{bucket2StartTimeNs}, {bucket3StartTimeNs});
// The 3rd bucket is dropped due to multiple buckets being skipped.
ASSERT_EQ(1, valueProducer->mSkippedBuckets.size());
EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs);
EXPECT_EQ(bucket4StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs);
ASSERT_EQ(1, valueProducer->mSkippedBuckets[0].dropEvents.size());
EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[0].dropEvents[0].reason);
EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[0].dropEvents[0].dropTimeNs);
} }
/* /*
@@ -1073,7 +1153,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curBaseInfo.hasBase);
// Now the alarm is delivered. // Now the alarm is delivered.
@@ -1082,7 +1163,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110)); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curBaseInfo.hasBase);
@@ -1090,8 +1172,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
} }
/* /*
* Test pulled event with non sliced condition. The pull on boundary come late, after the * Test pulled event with non sliced condition. The pull on boundary come late, after the condition
condition
* change to false, and then true again. This is due to alarm delivered late. * change to false, and then true again. This is due to alarm delivered late.
*/ */
TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
@@ -1139,7 +1220,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
// pull on bucket boundary come late, condition change happens before it // pull on bucket boundary come late, condition change happens before it
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
@@ -1148,7 +1230,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
// condition changed to true again, before the pull alarm is delivered // condition changed to true again, before the pull alarm is delivered
valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25); valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -1167,13 +1250,15 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
EXPECT_EQ(140, curBaseInfo.base.long_value); EXPECT_EQ(140, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(10, curInterval.value.long_value);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
allData.clear(); allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 160)); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 160));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30}, assertPastBucketValuesSingleKey(
{bucketSizeNs - 8, bucketSizeNs - 24}); valueProducer->mPastBuckets, {20, 30}, {bucketSizeNs - 8, bucketSizeNs - 24},
{bucketStartTimeNs, bucket2StartTimeNs}, {bucket2StartTimeNs, bucket3StartTimeNs});
} }
TEST(ValueMetricProducerTest, TestPushedAggregateMin) { TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
@@ -1216,7 +1301,8 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(10, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs); valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs}); assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
} }
TEST(ValueMetricProducerTest, TestPushedAggregateMax) { TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
@@ -1239,9 +1325,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
LogEvent event1(/*uid=*/0, /*pid=*/0); LogEvent event1(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice // has one slice
@@ -1251,6 +1334,8 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(true, curInterval.hasValue);
LogEvent event2(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice // has one slice
@@ -1258,10 +1343,9 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(20, curInterval.value.long_value); EXPECT_EQ(20, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs); valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
/* EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); */ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {bucketSizeNs},
/* EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); */ {bucketStartTimeNs}, {bucket2StartTimeNs});
/* EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); */
} }
TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
@@ -1351,7 +1435,8 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
EXPECT_EQ(25, curInterval.value.long_value); EXPECT_EQ(25, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs); valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs}); assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
} }
TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
@@ -1375,10 +1460,8 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
LogEvent event1(/*uid=*/0, /*pid=*/0); LogEvent event1(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice // has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = ValueMetricProducer::Interval curInterval =
@@ -1388,6 +1471,8 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
EXPECT_EQ(10, curBaseInfo.base.long_value); EXPECT_EQ(10, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(false, curInterval.hasValue);
LogEvent event2(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice // has one slice
@@ -1400,12 +1485,14 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
LogEvent event3(/*uid=*/0, /*pid=*/0); LogEvent event3(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 15); CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(15, curBaseInfo.base.long_value); EXPECT_EQ(15, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(0, curInterval.value.long_value);
LogEvent event4(/*uid=*/0, /*pid=*/0); LogEvent event4(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15); CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15);
@@ -1416,11 +1503,11 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(15, curBaseInfo.base.long_value); EXPECT_EQ(15, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(0, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs); valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs},
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); {bucketStartTimeNs}, {bucket2StartTimeNs});
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs});
} }
TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
@@ -1740,8 +1827,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
EXPECT_EQ(3, baseInfo1.base.long_value); EXPECT_EQ(3, baseInfo1.base.long_value);
EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
vector<shared_ptr<LogEvent>> allData;
vector<shared_ptr<LogEvent>> allData;
allData.clear(); allData.clear();
allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4)); allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4));
allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11)); allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
@@ -1753,7 +1840,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(8, interval1.value.long_value); EXPECT_EQ(8, interval1.value.long_value);
EXPECT_FALSE(interval1.seenNewData); EXPECT_FALSE(interval1.seenNewData);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
auto it = valueProducer->mCurrentSlicedBucket.begin(); auto it = valueProducer->mCurrentSlicedBucket.begin();
for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) { for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
@@ -1769,14 +1857,13 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
} }
EXPECT_TRUE(it != iter); EXPECT_TRUE(it != iter);
EXPECT_TRUE(itBase != iterBase); EXPECT_TRUE(itBase != iterBase);
auto& interval2 = it->second[0]; auto interval2 = it->second[0];
auto& baseInfo2 = itBase->second[0]; auto baseInfo2 = itBase->second[0];
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
EXPECT_EQ(true, baseInfo2.hasBase); EXPECT_EQ(true, baseInfo2.hasBase);
EXPECT_EQ(4, baseInfo2.base.long_value); EXPECT_EQ(4, baseInfo2.base.long_value);
EXPECT_EQ(false, interval2.hasValue); EXPECT_EQ(false, interval2.hasValue);
EXPECT_FALSE(interval2.seenNewData); EXPECT_FALSE(interval2.seenNewData);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs});
// next pull somehow did not happen, skip to end of bucket 3 // next pull somehow did not happen, skip to end of bucket 3
allData.clear(); allData.clear();
@@ -1791,7 +1878,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
EXPECT_EQ(5, baseInfo2.base.long_value); EXPECT_EQ(5, baseInfo2.base.long_value);
EXPECT_EQ(false, interval2.hasValue); EXPECT_EQ(false, interval2.hasValue);
EXPECT_FALSE(interval2.seenNewData); EXPECT_FALSE(interval2.seenNewData);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
allData.clear(); allData.clear();
allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14)); allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14));
@@ -1805,9 +1893,13 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
EXPECT_FALSE(interval2.seenNewData); EXPECT_FALSE(interval2.seenNewData);
ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
auto iterator = valueProducer->mPastBuckets.begin(); auto iterator = valueProducer->mPastBuckets.begin();
EXPECT_EQ(bucket4StartTimeNs, iterator->second[0].mBucketStartNs);
EXPECT_EQ(bucket5StartTimeNs, iterator->second[0].mBucketEndNs);
EXPECT_EQ(9, iterator->second[0].values[0].long_value); EXPECT_EQ(9, iterator->second[0].values[0].long_value);
EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
iterator++; iterator++;
EXPECT_EQ(bucketStartTimeNs, iterator->second[0].mBucketStartNs);
EXPECT_EQ(bucket2StartTimeNs, iterator->second[0].mBucketEndNs);
EXPECT_EQ(8, iterator->second[0].values[0].long_value); EXPECT_EQ(8, iterator->second[0].values[0].long_value);
EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
} }
@@ -2414,7 +2506,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
EXPECT_EQ(true, valueProducer->mHasGlobalBase); EXPECT_EQ(true, valueProducer->mHasGlobalBase);
EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1},
{bucketStartTimeNs}, {bucket2StartTimeNs});
} }
TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
@@ -2461,10 +2554,11 @@ TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
EXPECT_EQ(true, valueProducer->mHasGlobalBase); EXPECT_EQ(true, valueProducer->mHasGlobalBase);
} }
TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid) { TEST_P(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucketStartTimeNs + bucketSizeNs / 2;
EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialization. // Initialization.
.WillOnce(Invoke( .WillOnce(Invoke(
@@ -2474,23 +2568,41 @@ TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid) {
return true; return true;
})) }))
// notifyAppUpgrade. // notifyAppUpgrade.
.WillOnce(Invoke([](int tagId, const ConfigKey&, .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
vector<std::shared_ptr<LogEvent>>* data, bool) { vector<std::shared_ptr<LogEvent>>* data,
bool) {
data->clear(); data->clear();
data->push_back(CreateRepeatedValueLogEvent( data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
tagId, bucketStartTimeNs + bucketSizeNs / 2, 10));
return true; return true;
})); }));
sp<ValueMetricProducer> valueProducer = sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size()); ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
valueProducer->notifyAppUpgrade(bucketStartTimeNs + bucketSizeNs / 2, "com.foo", 10000, 1); switch (GetParam()) {
case APP_UPGRADE:
valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
break;
case BOOT_COMPLETE:
valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
break;
}
EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
EXPECT_EQ(0, valueProducer->getCurrentBucketNum());
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9},
{partialBucketSplitTimeNs - bucketStartTimeNs},
{bucketStartTimeNs}, {partialBucketSplitTimeNs});
ASSERT_EQ(1UL, valueProducer->mCurrentFullBucket.size()); ASSERT_EQ(1UL, valueProducer->mCurrentFullBucket.size());
vector<shared_ptr<LogEvent>> allData; vector<shared_ptr<LogEvent>> allData;
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 4)); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 4));
valueProducer->onDataPulled(allData, /** fails */ false, bucket3StartTimeNs + 1); valueProducer->onDataPulled(allData, /** fails */ false, bucket3StartTimeNs + 1);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9},
{partialBucketSplitTimeNs - bucketStartTimeNs},
{bucketStartTimeNs}, {partialBucketSplitTimeNs});
ASSERT_EQ(1, valueProducer->mSkippedBuckets.size());
EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs);
EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs);
ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size()); ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
} }
@@ -2537,7 +2649,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10); valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10);
// Bucket should have been completed. // Bucket should have been completed.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10},
{bucket2StartTimeNs}, {bucket3StartTimeNs});
} }
TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) { TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
@@ -2557,7 +2670,8 @@ TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// Bucket should have been completed. // Bucket should have been completed.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
} }
TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) { TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
@@ -2585,12 +2699,14 @@ TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// Bucket should have been completed. // Bucket should have been completed.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
} }
TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) { TEST_P(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialization. // Initialization.
@@ -2601,20 +2717,29 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) {
return true; return true;
})) }))
// notifyAppUpgrade. // notifyAppUpgrade.
.WillOnce(Invoke([](int tagId, const ConfigKey&, .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
vector<std::shared_ptr<LogEvent>>* data, bool) { vector<std::shared_ptr<LogEvent>>* data,
bool) {
data->clear(); data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 2, 10)); data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
return true; return true;
})); }));
sp<ValueMetricProducer> valueProducer = sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1); switch (GetParam()) {
case APP_UPGRADE:
valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
break;
case BOOT_COMPLETE:
valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
break;
}
// Bucket should have been completed. // Bucket should have been completed.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
} }
TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
@@ -2642,7 +2767,7 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); valueProducer->onConditionChanged(false, bucketStartTimeNs + 12);
EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
@@ -2654,7 +2779,8 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 10)); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 10));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2}, {bucketStartTimeNs},
{bucket2StartTimeNs});
} }
// TODO: b/145705635 fix or delete this test // TODO: b/145705635 fix or delete this test
@@ -2705,7 +2831,7 @@ TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// There was not global base available so all buckets are invalid. // There was not global base available so all buckets are invalid.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {});
} }
TEST(ValueMetricProducerTest, TestPullNeededFastDump) { TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
@@ -2849,7 +2975,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) {
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 30); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 30);
// Bucket should have been completed. // Bucket should have been completed.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
} }
TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) { TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) {
@@ -2892,7 +3019,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110)); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curBaseInfo.hasBase);
@@ -2923,7 +3051,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30)); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
ValueMetricProducer::Interval curInterval = ValueMetricProducer::Interval curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0]; valueProducer->mCurrentSlicedBucket.begin()->second[0];
ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
@@ -2946,7 +3075,7 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) {
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// Condition was always false. // Condition was always false.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {});
} }
TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) { TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
@@ -2976,7 +3105,7 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// No buckets, we had a failure. // No buckets, we had a failure.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {});
} }
/* /*

View File

@@ -42,6 +42,8 @@ using Status = ::ndk::ScopedAStatus;
const int SCREEN_STATE_ATOM_ID = util::SCREEN_STATE_CHANGED; const int SCREEN_STATE_ATOM_ID = util::SCREEN_STATE_CHANGED;
const int UID_PROCESS_STATE_ATOM_ID = util::UID_PROCESS_STATE_CHANGED; const int UID_PROCESS_STATE_ATOM_ID = util::UID_PROCESS_STATE_CHANGED;
enum BucketSplitEvent { APP_UPGRADE, BOOT_COMPLETE };
// Converts a ProtoOutputStream to a StatsLogReport proto. // Converts a ProtoOutputStream to a StatsLogReport proto.
StatsLogReport outputStreamToProto(ProtoOutputStream* proto); StatsLogReport outputStreamToProto(ProtoOutputStream* proto);

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2020 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 "utils/MultiConditionTrigger.h"
#include <gtest/gtest.h>
#include <chrono>
#include <set>
#include <thread>
#include <vector>
#ifdef __ANDROID__
using namespace std;
using std::this_thread::sleep_for;
namespace android {
namespace os {
namespace statsd {
TEST(MultiConditionTrigger, TestMultipleConditions) {
int numConditions = 5;
string t1 = "t1", t2 = "t2", t3 = "t3", t4 = "t4", t5 = "t5";
set<string> conditionNames = {t1, t2, t3, t4, t5};
mutex lock;
condition_variable cv;
bool triggerCalled = false;
// Mark done as true and notify in the done.
MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] {
{
lock_guard lg(lock);
triggerCalled = true;
}
cv.notify_all();
});
vector<thread> threads;
vector<bool> done(numConditions, false);
int i = 0;
for (const string& conditionName : conditionNames) {
threads.emplace_back([&done, &conditionName, &trigger, i] {
sleep_for(chrono::milliseconds(3));
done[i] = true;
trigger.markComplete(conditionName);
});
i++;
}
unique_lock<mutex> unique_lk(lock);
cv.wait(unique_lk, [&triggerCalled] {
return triggerCalled;
});
for (i = 0; i < numConditions; i++) {
EXPECT_EQ(done[i], 1);
}
for (i = 0; i < numConditions; i++) {
threads[i].join();
}
}
TEST(MultiConditionTrigger, TestNoConditions) {
mutex lock;
condition_variable cv;
bool triggerCalled = false;
MultiConditionTrigger trigger({}, [&lock, &cv, &triggerCalled] {
{
lock_guard lg(lock);
triggerCalled = true;
}
cv.notify_all();
});
unique_lock<mutex> unique_lk(lock);
cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
EXPECT_TRUE(triggerCalled);
// Ensure that trigger occurs immediately if no events need to be completed.
}
TEST(MultiConditionTrigger, TestMarkCompleteCalledBySameCondition) {
string t1 = "t1", t2 = "t2";
set<string> conditionNames = {t1, t2};
mutex lock;
condition_variable cv;
bool triggerCalled = false;
MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] {
{
lock_guard lg(lock);
triggerCalled = true;
}
cv.notify_all();
});
trigger.markComplete(t1);
trigger.markComplete(t1);
// Ensure that the trigger still hasn't fired.
{
lock_guard lg(lock);
EXPECT_FALSE(triggerCalled);
}
trigger.markComplete(t2);
unique_lock<mutex> unique_lk(lock);
cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
EXPECT_TRUE(triggerCalled);
}
TEST(MultiConditionTrigger, TestTriggerOnlyCalledOnce) {
string t1 = "t1";
set<string> conditionNames = {t1};
mutex lock;
condition_variable cv;
bool triggerCalled = false;
int triggerCount = 0;
MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled, &triggerCount] {
{
lock_guard lg(lock);
triggerCount++;
triggerCalled = true;
}
cv.notify_all();
});
trigger.markComplete(t1);
// Ensure that the trigger fired.
{
unique_lock<mutex> unique_lk(lock);
cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
EXPECT_TRUE(triggerCalled);
EXPECT_EQ(triggerCount, 1);
triggerCalled = false;
}
trigger.markComplete(t1);
// Ensure that the trigger does not fire again.
{
unique_lock<mutex> unique_lk(lock);
cv.wait_for(unique_lk, chrono::milliseconds(5), [&triggerCalled] { return triggerCalled; });
EXPECT_FALSE(triggerCalled);
EXPECT_EQ(triggerCount, 1);
}
}
} // namespace statsd
} // namespace os
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif

View File

@@ -1,96 +0,0 @@
/*
* Copyright (C) 2020 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 "utils/NamedLatch.h"
#include <gtest/gtest.h>
#include <chrono>
#include <set>
#include <thread>
#include <vector>
#ifdef __ANDROID__
using namespace std;
using std::this_thread::sleep_for;
namespace android {
namespace os {
namespace statsd {
TEST(NamedLatchTest, TestWait) {
int numEvents = 5;
string t1 = "t1", t2 = "t2", t3 = "t3", t4 = "t4", t5 = "t5";
set<string> eventNames = {t1, t2, t3, t4, t5};
NamedLatch latch(eventNames);
vector<thread> threads;
vector<bool> done(numEvents, false);
int i = 0;
for (const string& eventName : eventNames) {
threads.emplace_back([&done, &eventName, &latch, i] {
sleep_for(chrono::milliseconds(3));
done[i] = true;
latch.countDown(eventName);
});
i++;
}
latch.wait();
for (i = 0; i < numEvents; i++) {
EXPECT_EQ(done[i], 1);
}
for (i = 0; i < numEvents; i++) {
threads[i].join();
}
}
TEST(NamedLatchTest, TestNoWorkers) {
NamedLatch latch({});
latch.wait();
// Ensure that latch does not wait if no events need to countDown.
}
TEST(NamedLatchTest, TestCountDownCalledBySameEventName) {
string t1 = "t1", t2 = "t2";
set<string> eventNames = {t1, t2};
NamedLatch latch(eventNames);
thread waiterThread([&latch] { latch.wait(); });
latch.countDown(t1);
latch.countDown(t1);
// Ensure that the latch's remaining threads still has t2.
latch.mMutex.lock();
ASSERT_EQ(latch.mRemainingEventNames.size(), 1);
EXPECT_NE(latch.mRemainingEventNames.find(t2), latch.mRemainingEventNames.end());
latch.mMutex.unlock();
latch.countDown(t2);
waiterThread.join();
}
} // namespace statsd
} // namespace os
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif