Merge "Split buckets on boot complete" into rvc-dev am: affc08a340 am: 3463279428

Change-Id: Ida11915b0de672940ae6354abcc0ae98636ac072
This commit is contained in:
Tej Singh
2020-04-22 01:48:40 +00:00
committed by Automerger Merge Worker
23 changed files with 917 additions and 449 deletions

View File

@@ -104,7 +104,7 @@ cc_defaults {
"src/subscriber/IncidentdReporter.cpp",
"src/subscriber/SubscriberReporter.cpp",
"src/uid_data.proto",
"src/utils/NamedLatch.cpp",
"src/utils/MultiConditionTrigger.cpp",
],
local_include_dirs: [
@@ -366,7 +366,7 @@ cc_test {
"tests/StatsService_test.cpp",
"tests/storage/StorageManager_test.cpp",
"tests/UidMap_test.cpp",
"tests/utils/NamedLatch_test.cpp",
"tests/utils/MultiConditionTrigger_test.cpp",
],
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,
const int uid, const int64_t version) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
ALOGW("Received app upgrade");
for (auto it : mMetricsManagers) {
VLOG("Received app upgrade");
for (const auto& it : mMetricsManagers) {
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,
const int uid) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
ALOGW("Received app removed");
for (auto it : mMetricsManagers) {
VLOG("Received app removed");
for (const auto& it : mMetricsManagers) {
it.second->notifyAppRemoved(eventTimeNs, apk, uid);
}
}
void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
ALOGW("Received uid map");
for (auto it : mMetricsManagers) {
VLOG("Received uid map");
for (const auto& it : mMetricsManagers) {
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) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
mOnDiskDataConfigs.insert(key);

View File

@@ -120,6 +120,11 @@ public:
/* Notify all MetricsManagers of uid map snapshots received */
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.
void resetConfigs();

View File

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

View File

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

View File

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

View File

@@ -154,12 +154,14 @@ private:
FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition);
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(DurationMetricTrackerTest, TestFirstBucket);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestSumDuration);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket,
TestSumDurationWithSplitInFollowingBucket);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket);
};
} // namespace statsd

View File

@@ -73,18 +73,23 @@ public:
bool pullSuccess, int64_t originalPullTimeNs) override;
// 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,
const int64_t version) override {
void notifyAppUpgrade(const int64_t& eventTimeNs) override {
std::lock_guard<std::mutex> lock(mMutex);
if (!mSplitBucketForAppUpgrade) {
return;
}
if (eventTimeNs > getCurrentBucketEndTimeNs()) {
// Flush full buckets on the normal path up to the latest bucket boundary.
flushIfNeededLocked(eventTimeNs);
flushLocked(eventTimeNs);
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
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) {
pullAndMatchEventsLocked(eventTimeNs);
}
@@ -190,13 +195,14 @@ private:
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection);
FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket);
FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger);
FRIEND_TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput);
FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents);
FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled);
};
} // 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
* 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.
* Force a partial bucket split on app upgrade
*/
virtual void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
const int64_t version) {
virtual void notifyAppUpgrade(const int64_t& eventTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
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.
flushLocked(eventTimeNs);
};
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.
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.
void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
std::lock_guard<std::mutex> lock(mMutex);
@@ -292,8 +287,7 @@ public:
// End: getters/setters
protected:
/**
* Flushes the current bucket if the eventTime is after the current bucket's end time. This will
also flush the current partial bucket in memory.
* Flushes the current bucket if the eventTime is after the current bucket's end time.
*/
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,
const int64_t version) {
// Inform all metric producers.
for (auto it : mAllMetricProducers) {
it->notifyAppUpgrade(eventTimeNs, apk, uid, version);
for (const auto& it : mAllMetricProducers) {
it->notifyAppUpgrade(eventTimeNs);
}
// check if we care this package
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,
const int uid) {
// Inform all metric producers.
for (auto it : mAllMetricProducers) {
it->notifyAppRemoved(eventTimeNs, apk, uid);
for (const auto& it : mAllMetricProducers) {
it->notifyAppRemoved(eventTimeNs);
}
// check if we care this package
if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
@@ -282,6 +282,13 @@ void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) {
initLogSourceWhiteList();
}
void MetricsManager::onStatsdInitCompleted(const int64_t& eventTimeNs) {
// Inform all metric producers.
for (const auto& it : mAllMetricProducers) {
it->onStatsdInitCompleted(eventTimeNs);
}
}
void MetricsManager::init() {
for (const auto& producer : mAllMetricProducers) {
producer->prepareFirstBucket();

View File

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

View File

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

View File

@@ -15,7 +15,9 @@
*/
#define DEBUG false // STOPSHIP if true
#include "NamedLatch.h"
#include "MultiConditionTrigger.h"
#include <thread>
using namespace std;
@@ -23,26 +25,33 @@ namespace android {
namespace os {
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) {
bool notify = false;
void MultiConditionTrigger::markComplete(const string& conditionName) {
bool doTrigger = false;
{
lock_guard<mutex> lg(mMutex);
mRemainingEventNames.erase(eventName);
notify = mRemainingEventNames.empty();
if (mCompleted) {
return;
}
mRemainingConditionNames.erase(conditionName);
mCompleted = mRemainingConditionNames.empty();
doTrigger = mCompleted;
}
if (notify) {
mConditionVariable.notify_all();
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 os
} // 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_min_bucket_size_nanos(minTime);
valueMetric->set_use_absolute_value_on_reset(true);
valueMetric->set_skip_zero_diff_output(false);
return config;
}
@@ -217,6 +218,35 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
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) {
shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
service->mPullerManager->RegisterPullAtomCallback(
@@ -229,13 +259,22 @@ TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
// initialized with.
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,
String16("v2"), String16(""));
int64_t appUpgradeTimeNs = 5 * 60 * NS_PER_SEC + start + 2 * NS_PER_SEC;
service->mUidMap->updateApp(appUpgradeTimeNs, String16(kApp1.c_str()), 1, 2, String16("v2"),
String16(""));
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(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) {
@@ -249,13 +288,13 @@ TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// 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->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
String16(""));
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);
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.
EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
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) {
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.
service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
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,
String16("v2"), String16(""));
ConfigMetricsReport report =
GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
EXPECT_EQ(1, report.metrics_size());
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());
EXPECT_EQ(2, report.metrics(0).gauge_metrics().data(0).bucket_info_size());
}
TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
// Partial buckets don't occur when app is first installed.
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 */));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
@@ -298,7 +382,7 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
String16(""));
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);
ASSERT_EQ(1, report.metrics_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_EQ(MillisToNano(NanoToMillis(endSkipped)),
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

View File

@@ -38,9 +38,9 @@ namespace android {
namespace os {
namespace statsd {
const ConfigKey kConfigKey(0, 12345);
namespace {
const ConfigKey kConfigKey(0, 12345);
void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
@@ -61,6 +61,13 @@ void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string ui
} // 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) {
CountMetric metric;
metric.set_id(1);
@@ -237,11 +244,11 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
EXPECT_EQ(1LL, bucketInfo.mCount);
}
TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket) {
sp<AlarmMonitor> alarmMonitor;
int64_t bucketStartTimeNs = 10000000000;
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 conditionTagId = 2;
@@ -260,22 +267,30 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr);
// Bucket is flushed yet.
// Bucket is not flushed yet.
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
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.
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((long long)bucketStartTimeNs,
EXPECT_EQ(bucketStartTimeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
EXPECT_EQ((long long)eventUpgradeTimeNs,
EXPECT_EQ(eventTimeNs,
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.
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");
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
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));
// Third event in following bucket.
@@ -294,13 +310,14 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(1, countProducer.getCurrentBucketNum());
EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
}
TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) {
TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket) {
int64_t bucketStartTimeNs = 10000000000;
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 conditionTagId = 2;
@@ -319,15 +336,23 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) {
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
// App upgrade forces bucket flush.
// Check that there's a past bucket and the bucket end is not adjusted.
countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
// App upgrade or boot complete forces bucket flush.
// Check that there's a past bucket and the bucket end is not adjusted since the upgrade
// 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((int64_t)bucketStartTimeNs,
EXPECT_EQ(bucketStartTimeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
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.
LogEvent event2(/*uid=*/0, /*pid=*/0);
@@ -340,7 +365,7 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) {
makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
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);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs);

View File

@@ -41,10 +41,10 @@ namespace android {
namespace os {
namespace statsd {
const ConfigKey kConfigKey(0, 12345);
namespace {
const ConfigKey kConfigKey(0, 12345);
void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
@@ -55,6 +55,13 @@ void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
} // 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) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
DurationMetric metric;
@@ -205,7 +212,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) {
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),
* 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 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;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
DurationMetric metric;
metric.set_id(1);
@@ -238,32 +237,47 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) {
3 /* stop_all index */, false /*nesting*/, wizard,
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);
EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
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());
std::vector<DurationBucket> buckets =
durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs);
EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration);
EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketEndNs);
EXPECT_EQ(partialBucketSplitTimeNs - startTimeNs, buckets[0].mDuration);
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.
int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
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 - eventUpgradeTimeNs, buckets[1].mDuration);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - partialBucketSplitTimeNs, buckets[1].mDuration);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
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):
* - [10,70]: 59 secs
@@ -272,15 +286,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
*/
int64_t bucketStartTimeNs = 10000000000;
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;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
DurationMetric metric;
metric.set_id(1);
@@ -293,11 +299,22 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
3 /* stop_all index */, false /*nesting*/, wizard,
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);
EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
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());
std::vector<DurationBucket> buckets =
durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
@@ -305,32 +322,29 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs);
EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration);
EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketEndNs);
EXPECT_EQ(partialBucketSplitTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration);
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.
int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
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 - eventUpgradeTimeNs, buckets[2].mDuration);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - partialBucketSplitTimeNs,
buckets[2].mDuration);
}
TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) {
TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) {
sp<AlarmMonitor> alarmMonitor;
int64_t bucketStartTimeNs = 10000000000;
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;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
// Setup metric with alert.
DurationMetric metric;
@@ -351,27 +365,35 @@ TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) {
sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
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.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.
int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs,
anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
}
TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) {
TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) {
int64_t bucketStartTimeNs = 10000000000;
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;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
DurationMetric metric;
metric.set_id(1);
@@ -385,15 +407,30 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) {
3 /* stop_all index */, false /*nesting*/, wizard,
dimensions, bucketStartTimeNs, bucketStartTimeNs);
int64_t startTimeNs = bucketStartTimeNs + 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
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(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.
int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
@@ -406,18 +443,10 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) {
EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
}
TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) {
TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket) {
int64_t bucketStartTimeNs = 10000000000;
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;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
DurationMetric metric;
metric.set_id(1);
@@ -431,24 +460,39 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) {
3 /* stop_all index */, false /*nesting*/, wizard,
dimensions, bucketStartTimeNs, bucketStartTimeNs);
int64_t startTimeNs = bucketStartTimeNs + 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, startTimeNs, tagId);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
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(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.
int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC;
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event2, endTimeNs, tagId);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
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);
std::vector<DurationBucket> buckets =
durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
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(endTimeNs - startTimeNs, buckets[0].mDuration);
}

View File

@@ -42,6 +42,8 @@ namespace android {
namespace os {
namespace statsd {
namespace {
const ConfigKey kConfigKey(0, 12345);
const int tagId = 1;
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 bucket3StartTimeNs = bucketStartTimeNs + 2 * 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,
int32_t value2) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
@@ -71,6 +72,13 @@ shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t v
}
} // 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
*/
@@ -194,7 +202,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) {
EXPECT_EQ(25L, it->mValue.int_value);
}
TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
TEST_P(GaugeMetricProducerTest_PartialBucket, TestPushedEvents) {
sp<AlarmMonitor> alarmMonitor;
GaugeMetric metric;
metric.set_id(metricId);
@@ -230,11 +238,22 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
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(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(eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
// Partial buckets are not sent to anomaly tracker.
EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
@@ -244,7 +263,11 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
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.
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));
}
TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) {
TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) {
GaugeMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
@@ -293,7 +316,8 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) {
.WillOnce(Invoke(
[](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventUpgradeTimeNs, 2));
data->push_back(
CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 2));
return true;
}));
@@ -311,10 +335,21 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) {
.mFields->begin()
->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(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((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
@@ -370,7 +405,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
.mFields->begin()
->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(0L, gaugeProducer.mCurrentBucketNum);
EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);

View File

@@ -41,6 +41,8 @@ namespace android {
namespace os {
namespace statsd {
namespace {
const ConfigKey kConfigKey(0, 12345);
const int tagId = 1;
const int64_t metricId = 123;
@@ -58,10 +60,18 @@ double epsilon = 0.001;
static void assertPastBucketValuesSingleKey(
const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets,
const std::initializer_list<int>& expectedValuesList,
const std::initializer_list<int64_t>& expectedDurationNsList) {
std::vector<int> expectedValues(expectedValuesList);
std::vector<int64_t> expectedDurationNs(expectedDurationNsList);
const std::initializer_list<int64_t>& expectedDurationNsList,
const std::initializer_list<int64_t>& expectedStartTimeNsList,
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(), expectedStartTimeNs.size());
ASSERT_EQ(expectedValues.size(), expectedEndTimeNs.size());
if (expectedValues.size() == 0) {
ASSERT_EQ(0, mPastBuckets.size());
return;
@@ -70,15 +80,21 @@ static void assertPastBucketValuesSingleKey(
ASSERT_EQ(1, mPastBuckets.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++) {
EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value)
<< "Values differ at index " << i;
EXPECT_EQ(expectedDurationNs[i], buckets[i].mConditionTrueNs)
<< "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 {
public:
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
*/
@@ -325,9 +348,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs);
}
TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
TEST_P(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialize bucket.
.WillOnce(Invoke([](int tagId, const ConfigKey&,
@@ -337,10 +361,12 @@ TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
return true;
}))
// Partial bucket.
.WillOnce(Invoke([](int tagId, const ConfigKey&,
vector<std::shared_ptr<LogEvent>>* data, bool) {
.WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
vector<std::shared_ptr<LogEvent>>* data,
bool) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5));
data->push_back(
CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs + 8, 5));
return true;
}));
@@ -354,19 +380,21 @@ TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs);
// 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.
EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
vector<ValueBucket> buckets = valueProducer->mPastBuckets.begin()->second;
EXPECT_EQ(2UL, buckets.size());
// 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);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1, 3},
{bucketSizeNs, partialBucketSplitTimeNs - bucket2StartTimeNs},
{bucketStartTimeNs, bucket2StartTimeNs},
{bucket2StartTimeNs, partialBucketSplitTimeNs});
}
/*
@@ -613,7 +641,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8});
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
// has one slice
EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
@@ -625,7 +654,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
EXPECT_EQ(10, curInterval.value.long_value);
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8});
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
// has one slice
EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
@@ -636,10 +666,12 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
EXPECT_EQ(false, curBaseInfo.hasBase);
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();
UidMap uidMap;
@@ -660,25 +692,46 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
valueProducer.notifyAppUpgrade(bucketStartTimeNs + 150, "ANY.APP", 1, 1);
EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 150;
switch (GetParam()) {
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);
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);
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.
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);
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(1, valueProducer.getCurrentBucketNum());
}
TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValue) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
UidMap uidMap;
@@ -689,14 +742,16 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150;
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Return(true))
.WillOnce(Invoke([](int tagId, const ConfigKey&,
vector<std::shared_ptr<LogEvent>>* data, bool) {
.WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
vector<std::shared_ptr<LogEvent>>* data,
bool) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 149, 120));
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 120));
return true;
}));
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
@@ -711,20 +766,27 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150});
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());
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150}, {bucket2StartTimeNs},
{partialBucketSplitTimeNs});
allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 150));
valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(20L,
valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30},
{150, bucketSizeNs - 150});
EXPECT_EQ(2, valueProducer.getCurrentBucketNum());
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30}, {150, bucketSizeNs - 150},
{bucket2StartTimeNs, partialBucketSplitTimeNs},
{partialBucketSplitTimeNs, bucket3StartTimeNs});
}
TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
@@ -754,12 +816,12 @@ TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
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(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
}
TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -784,14 +846,21 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100);
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_EQ(bucket2StartTimeNs - 50, valueProducer->mCurrentBucketStartTimeNs);
EXPECT_EQ(1UL, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucketStartTimeNs,
valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
EXPECT_EQ(0, valueProducer->getCurrentBucketNum());
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20},
{(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)});
{(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)},
{bucketStartTimeNs}, {partialBucketSplitTimeNs});
EXPECT_FALSE(valueProducer->mCondition);
}
TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
@@ -834,7 +903,8 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
EXPECT_EQ(30, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs});
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
@@ -895,7 +965,8 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
EXPECT_EQ(50, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20});
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20}, {bucketStartTimeNs},
{bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestAnomalyDetection) {
@@ -1012,7 +1083,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(23, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs});
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs},
{bucket2StartTimeNs}, {bucket3StartTimeNs});
// pull 3 come late.
// 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(36, curBaseInfo.base.long_value);
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);
curInterval = valueProducer->mCurrentSlicedBucket.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);
// Now the alarm is delivered.
@@ -1082,7 +1163,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110));
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];
curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
EXPECT_EQ(false, curBaseInfo.hasBase);
@@ -1090,10 +1172,9 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
}
/*
* Test pulled event with non sliced condition. The pull on boundary come late, after the
condition
* change to false, and then true again. This is due to alarm delivered late.
*/
* Test pulled event with non sliced condition. The pull on boundary come late, after the condition
* change to false, and then true again. This is due to alarm delivered late.
*/
TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
@@ -1139,7 +1220,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
// pull on bucket boundary come late, condition change happens before it
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());
curInterval = valueProducer->mCurrentSlicedBucket.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
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];
curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -1167,13 +1250,15 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
EXPECT_EQ(140, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(10, curInterval.value.long_value);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
allData.clear();
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 160));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30},
{bucketSizeNs - 8, bucketSizeNs - 24});
assertPastBucketValuesSingleKey(
valueProducer->mPastBuckets, {20, 30}, {bucketSizeNs - 8, bucketSizeNs - 24},
{bucketStartTimeNs, bucket2StartTimeNs}, {bucket2StartTimeNs, bucket3StartTimeNs});
}
TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
@@ -1216,7 +1301,8 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
EXPECT_EQ(10, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs});
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
@@ -1239,9 +1325,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
LogEvent event1(/*uid=*/0, /*pid=*/0);
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);
// has one slice
@@ -1251,6 +1334,8 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
LogEvent event2(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice
@@ -1258,10 +1343,9 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(20, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
/* EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); */
/* EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); */
/* EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); */
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
@@ -1351,7 +1435,8 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
EXPECT_EQ(25, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs});
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
@@ -1375,10 +1460,8 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
LogEvent event1(/*uid=*/0, /*pid=*/0);
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);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
@@ -1388,6 +1471,8 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
EXPECT_EQ(10, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
LogEvent event2(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice
@@ -1400,12 +1485,14 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
LogEvent event3(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(15, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(0, curInterval.value.long_value);
LogEvent event4(/*uid=*/0, /*pid=*/0);
CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15);
@@ -1416,11 +1503,11 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(15, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(0, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs});
assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
@@ -1740,8 +1827,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
EXPECT_EQ(3, baseInfo1.base.long_value);
EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
vector<shared_ptr<LogEvent>> allData;
vector<shared_ptr<LogEvent>> allData;
allData.clear();
allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4));
allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
@@ -1753,7 +1840,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(8, interval1.value.long_value);
EXPECT_FALSE(interval1.seenNewData);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs});
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
auto it = valueProducer->mCurrentSlicedBucket.begin();
for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
@@ -1769,14 +1857,13 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
}
EXPECT_TRUE(it != iter);
EXPECT_TRUE(itBase != iterBase);
auto& interval2 = it->second[0];
auto& baseInfo2 = itBase->second[0];
auto interval2 = it->second[0];
auto baseInfo2 = itBase->second[0];
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
EXPECT_EQ(true, baseInfo2.hasBase);
EXPECT_EQ(4, baseInfo2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
EXPECT_FALSE(interval2.seenNewData);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs});
// next pull somehow did not happen, skip to end of bucket 3
allData.clear();
@@ -1791,7 +1878,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
EXPECT_EQ(5, baseInfo2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
EXPECT_FALSE(interval2.seenNewData);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs});
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
allData.clear();
allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14));
@@ -1805,9 +1893,13 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
EXPECT_FALSE(interval2.seenNewData);
ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
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(bucketSizeNs, iterator->second[0].mConditionTrueNs);
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(bucketSizeNs, iterator->second[0].mConditionTrueNs);
}
@@ -2414,7 +2506,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
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) {
@@ -2461,10 +2554,11 @@ TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
}
TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid) {
TEST_P(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucketStartTimeNs + bucketSizeNs / 2;
EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialization.
.WillOnce(Invoke(
@@ -2474,23 +2568,41 @@ TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid) {
return true;
}))
// notifyAppUpgrade.
.WillOnce(Invoke([](int tagId, const ConfigKey&,
vector<std::shared_ptr<LogEvent>>* data, bool) {
.WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
vector<std::shared_ptr<LogEvent>>* data,
bool) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(
tagId, bucketStartTimeNs + bucketSizeNs / 2, 10));
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
return true;
}));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
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());
vector<shared_ptr<LogEvent>> allData;
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 4));
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());
}
@@ -2537,7 +2649,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10);
// Bucket should have been completed.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10});
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10},
{bucket2StartTimeNs}, {bucket3StartTimeNs});
}
TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
@@ -2557,7 +2670,8 @@ TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// Bucket should have been completed.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs});
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
@@ -2585,12 +2699,14 @@ TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// 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();
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialization.
@@ -2601,20 +2717,29 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) {
return true;
}))
// notifyAppUpgrade.
.WillOnce(Invoke([](int tagId, const ConfigKey&,
vector<std::shared_ptr<LogEvent>>* data, bool) {
.WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
vector<std::shared_ptr<LogEvent>>* data,
bool) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 2, 10));
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
return true;
}));
sp<ValueMetricProducer> valueProducer =
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.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs});
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
@@ -2642,7 +2767,7 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
valueProducer->onConditionChanged(false, bucketStartTimeNs + 12);
EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
@@ -2654,7 +2779,8 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 10));
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
@@ -2705,7 +2831,7 @@ TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// There was not global base available so all buckets are invalid.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {});
}
TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
@@ -2849,7 +2975,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) {
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 30);
// Bucket should have been completed.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs});
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs},
{bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) {
@@ -2892,7 +3019,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110));
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];
curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
EXPECT_EQ(false, curBaseInfo.hasBase);
@@ -2923,7 +3051,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) {
allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8});
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8},
{bucketStartTimeNs}, {bucket2StartTimeNs});
ValueMetricProducer::Interval curInterval =
valueProducer->mCurrentSlicedBucket.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);
// Condition was always false.
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {});
}
TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
@@ -2976,7 +3105,7 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// 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 UID_PROCESS_STATE_ATOM_ID = util::UID_PROCESS_STATE_CHANGED;
enum BucketSplitEvent { APP_UPGRADE, BOOT_COMPLETE };
// Converts a ProtoOutputStream to a StatsLogReport 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