Merge "Init duration metric if condition existed"
This commit is contained in:
committed by
Android (Google) Code Review
commit
8837606ba6
@@ -85,16 +85,14 @@ public:
|
||||
const std::vector<sp<ConditionTracker>>& allConditions,
|
||||
const vector<Matcher>& dimensions) const override;
|
||||
|
||||
void getTrueSlicedDimensions(
|
||||
const std::vector<sp<ConditionTracker>>& allConditions,
|
||||
std::set<HashableDimensionKey>* dimensions) const override {
|
||||
const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(
|
||||
const std::vector<sp<ConditionTracker>>& allConditions) const override {
|
||||
if (mSlicedChildren.size() == 1) {
|
||||
return allConditions[mSlicedChildren.front()]->getTrueSlicedDimensions(
|
||||
allConditions, dimensions);
|
||||
return allConditions[mSlicedChildren.front()]->getSlicedDimensionMap(allConditions);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
LogicalOperation mLogicalOperation;
|
||||
|
||||
|
||||
@@ -135,9 +135,8 @@ public:
|
||||
return mProtoHash;
|
||||
}
|
||||
|
||||
virtual void getTrueSlicedDimensions(
|
||||
const std::vector<sp<ConditionTracker>>& allConditions,
|
||||
std::set<HashableDimensionKey>* dimensions) const = 0;
|
||||
virtual const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(
|
||||
const std::vector<sp<ConditionTracker>>& allConditions) const = 0;
|
||||
|
||||
virtual bool IsChangedDimensionTrackable() const = 0;
|
||||
|
||||
|
||||
@@ -53,9 +53,9 @@ public:
|
||||
ConditionState getUnSlicedPartConditionState(const int index) {
|
||||
return mAllConditions[index]->getUnSlicedPartConditionState();
|
||||
}
|
||||
void getTrueSlicedDimensions(const int index,
|
||||
std::set<HashableDimensionKey>* trueDimensions) const {
|
||||
return mAllConditions[index]->getTrueSlicedDimensions(mAllConditions, trueDimensions);
|
||||
|
||||
const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(const int index) const {
|
||||
return mAllConditions[index]->getSlicedDimensionMap(mAllConditions);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -74,14 +74,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void getTrueSlicedDimensions(
|
||||
const std::vector<sp<ConditionTracker>>& allConditions,
|
||||
std::set<HashableDimensionKey>* dimensions) const override {
|
||||
for (const auto& itr : mSlicedConditionState) {
|
||||
if (itr.second > 0) {
|
||||
dimensions->insert(itr.first);
|
||||
}
|
||||
}
|
||||
const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(
|
||||
const std::vector<sp<ConditionTracker>>& allConditions) const override {
|
||||
return &mSlicedConditionState;
|
||||
}
|
||||
|
||||
bool IsChangedDimensionTrackable() const override { return true; }
|
||||
|
||||
@@ -67,8 +67,8 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
|
||||
|
||||
DurationMetricProducer::DurationMetricProducer(
|
||||
const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
|
||||
const vector<ConditionState>& initialConditionCache, const int startIndex,
|
||||
const int stopIndex, const int stopAllIndex, const bool nesting,
|
||||
const vector<ConditionState>& initialConditionCache, const int whatIndex,
|
||||
const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting,
|
||||
const sp<ConditionWizard>& wizard, const uint64_t protoHash,
|
||||
const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
|
||||
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
|
||||
@@ -140,6 +140,8 @@ DurationMetricProducer::DurationMetricProducer(
|
||||
mCurrentBucketStartTimeNs = startTimeNs;
|
||||
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
|
||||
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
|
||||
|
||||
initTrueDimensions(whatIndex, startTimeNs);
|
||||
}
|
||||
|
||||
DurationMetricProducer::~DurationMetricProducer() {
|
||||
@@ -224,6 +226,23 @@ bool DurationMetricProducer::onConfigUpdatedLocked(
|
||||
return true;
|
||||
}
|
||||
|
||||
void DurationMetricProducer::initTrueDimensions(const int whatIndex, const int64_t startTimeNs) {
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
// Currently whatIndex will only be -1 in tests. In the future, we might want to avoid creating
|
||||
// a ConditionTracker if the condition is only used in the "what" of a duration metric. In that
|
||||
// scenario, -1 can also be passed.
|
||||
if (whatIndex == -1) {
|
||||
return;
|
||||
}
|
||||
const map<HashableDimensionKey, int>* slicedWhatMap = mWizard->getSlicedDimensionMap(whatIndex);
|
||||
for (const auto& [internalDimKey, count] : *slicedWhatMap) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Fake start events.
|
||||
handleMatchedLogEventValuesLocked(mStartIndex, internalDimKey.getValues(), startTimeNs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
|
||||
const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) {
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
@@ -330,14 +349,14 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool conditio
|
||||
// state based on the new unsliced condition state.
|
||||
if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
|
||||
(dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
|
||||
std::set<HashableDimensionKey> trueConditionDimensions;
|
||||
mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions);
|
||||
const map<HashableDimensionKey, int>* slicedConditionMap =
|
||||
mWizard->getSlicedDimensionMap(mConditionTrackerIndex);
|
||||
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
|
||||
HashableDimensionKey linkedConditionDimensionKey;
|
||||
getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
|
||||
&linkedConditionDimensionKey);
|
||||
if (trueConditionDimensions.find(linkedConditionDimensionKey) !=
|
||||
trueConditionDimensions.end()) {
|
||||
const auto& slicedConditionIt = slicedConditionMap->find(linkedConditionDimensionKey);
|
||||
if (slicedConditionIt != slicedConditionMap->end() && slicedConditionIt->second > 0) {
|
||||
whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime);
|
||||
}
|
||||
}
|
||||
@@ -595,8 +614,9 @@ bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey
|
||||
}
|
||||
|
||||
void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey,
|
||||
const ConditionKey& conditionKeys,
|
||||
bool condition, const LogEvent& event) {
|
||||
const ConditionKey& conditionKeys, bool condition,
|
||||
const int64_t eventTimeNs,
|
||||
const vector<FieldValue>& eventValues) {
|
||||
const auto& whatKey = eventKey.getDimensionKeyInWhat();
|
||||
auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
|
||||
if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
|
||||
@@ -608,20 +628,17 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey
|
||||
|
||||
auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
|
||||
if (mUseWhatDimensionAsInternalDimension) {
|
||||
it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
|
||||
it->second->noteStart(whatKey, condition, eventTimeNs, conditionKeys);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mInternalDimensions.empty()) {
|
||||
it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(),
|
||||
conditionKeys);
|
||||
it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, eventTimeNs, conditionKeys);
|
||||
} else {
|
||||
HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
|
||||
filterValues(mInternalDimensions, event.getValues(), &dimensionKey);
|
||||
it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(),
|
||||
conditionKeys);
|
||||
filterValues(mInternalDimensions, eventValues, &dimensionKey);
|
||||
it->second->noteStart(dimensionKey, condition, eventTimeNs, conditionKeys);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DurationMetricProducer::onMatchedLogEventInternalLocked(
|
||||
@@ -633,26 +650,32 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked(
|
||||
|
||||
void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
|
||||
const LogEvent& event) {
|
||||
int64_t eventTimeNs = event.GetElapsedTimestampNs();
|
||||
handleMatchedLogEventValuesLocked(matcherIndex, event.getValues(),
|
||||
event.GetElapsedTimestampNs());
|
||||
}
|
||||
|
||||
void DurationMetricProducer::handleMatchedLogEventValuesLocked(const size_t matcherIndex,
|
||||
const vector<FieldValue>& values,
|
||||
const int64_t eventTimeNs) {
|
||||
if (eventTimeNs < mTimeBaseNs) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIsActive) {
|
||||
flushIfNeededLocked(event.GetElapsedTimestampNs());
|
||||
flushIfNeededLocked(eventTimeNs);
|
||||
}
|
||||
|
||||
// Handles Stopall events.
|
||||
if ((int)matcherIndex == mStopAllIndex) {
|
||||
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
|
||||
whatIt.second->noteStopAll(event.GetElapsedTimestampNs());
|
||||
whatIt.second->noteStopAll(eventTimeNs);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY;
|
||||
if (!mDimensionsInWhat.empty()) {
|
||||
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
|
||||
filterValues(mDimensionsInWhat, values, &dimensionInWhat);
|
||||
}
|
||||
|
||||
// Stores atom id to primary key pairs for each state atom that the metric is
|
||||
@@ -663,8 +686,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
|
||||
// field values from the log event. These values will form a primary key
|
||||
// that will be used to query StateTracker for the correct state value.
|
||||
for (const auto& stateLink : mMetric2StateLinks) {
|
||||
getDimensionForState(event.getValues(), stateLink,
|
||||
&statePrimaryKeys[stateLink.stateAtomId]);
|
||||
getDimensionForState(values, stateLink, &statePrimaryKeys[stateLink.stateAtomId]);
|
||||
}
|
||||
|
||||
// For each sliced state, query StateTracker for the state value using
|
||||
@@ -695,19 +717,19 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
|
||||
if (mUseWhatDimensionAsInternalDimension) {
|
||||
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
|
||||
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
|
||||
whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
|
||||
whatIt->second->noteStop(dimensionInWhat, eventTimeNs, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY;
|
||||
if (!mInternalDimensions.empty()) {
|
||||
filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey);
|
||||
filterValues(mInternalDimensions, values, &internalDimensionKey);
|
||||
}
|
||||
|
||||
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
|
||||
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
|
||||
whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false);
|
||||
whatIt->second->noteStop(internalDimensionKey, eventTimeNs, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -716,7 +738,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
|
||||
ConditionKey conditionKey;
|
||||
if (mConditionSliced) {
|
||||
for (const auto& link : mMetric2ConditionLinks) {
|
||||
getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
|
||||
getDimensionForCondition(values, link, &conditionKey[link.conditionId]);
|
||||
}
|
||||
|
||||
auto conditionState =
|
||||
@@ -731,7 +753,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
|
||||
condition = condition && mIsActive;
|
||||
|
||||
handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition,
|
||||
event);
|
||||
eventTimeNs, values);
|
||||
}
|
||||
|
||||
size_t DurationMetricProducer::byteSizeLocked() const {
|
||||
|
||||
@@ -40,8 +40,8 @@ class DurationMetricProducer : public MetricProducer {
|
||||
public:
|
||||
DurationMetricProducer(
|
||||
const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
|
||||
const vector<ConditionState>& initialConditionCache, const int startIndex,
|
||||
const int stopIndex, const int stopAllIndex, const bool nesting,
|
||||
const vector<ConditionState>& initialConditionCache, const int whatIndex,
|
||||
const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting,
|
||||
const sp<ConditionWizard>& wizard, const uint64_t protoHash,
|
||||
const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
|
||||
const int64_t startTimeNs,
|
||||
@@ -74,8 +74,15 @@ protected:
|
||||
const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
|
||||
|
||||
private:
|
||||
// Initializes true dimensions of the 'what' predicate. Only to be called during initialization.
|
||||
void initTrueDimensions(const int whatIndex, const int64_t startTimeNs);
|
||||
|
||||
void handleMatchedLogEventValuesLocked(const size_t matcherIndex,
|
||||
const std::vector<FieldValue>& values,
|
||||
const int64_t eventTimeNs);
|
||||
void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys,
|
||||
bool condition, const LogEvent& event);
|
||||
bool condition, const int64_t eventTimeNs,
|
||||
const vector<FieldValue>& eventValues);
|
||||
|
||||
void onDumpReportLocked(const int64_t dumpTimeNs,
|
||||
const bool include_current_partial_bucket,
|
||||
|
||||
@@ -450,7 +450,8 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata(
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
const Predicate& durationWhat = config.predicate(what_it->second);
|
||||
const int whatIndex = what_it->second;
|
||||
const Predicate& durationWhat = config.predicate(whatIndex);
|
||||
if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
|
||||
ALOGE("DurationMetric's \"what\" must be a simple condition");
|
||||
return nullopt;
|
||||
@@ -536,10 +537,11 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata(
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
return {new DurationMetricProducer(
|
||||
key, metric, conditionIndex, initialConditionCache, startIndex, stopIndex, stopAllIndex,
|
||||
nesting, wizard, metricHash, internalDimensions, timeBaseNs, currentTimeNs,
|
||||
eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
|
||||
return {new DurationMetricProducer(key, metric, conditionIndex, initialConditionCache,
|
||||
whatIndex, startIndex, stopIndex, stopAllIndex, nesting,
|
||||
wizard, metricHash, internalDimensions, timeBaseNs,
|
||||
currentTimeNs, eventActivationMap, eventDeactivationMap,
|
||||
slicedStateAtoms, stateGroupMap)};
|
||||
}
|
||||
|
||||
optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <thread> // std::this_thread::sleep_for
|
||||
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "src/StatsLogProcessor.h"
|
||||
#include "src/storage/StorageManager.h"
|
||||
@@ -28,6 +26,7 @@ namespace statsd {
|
||||
#ifdef __ANDROID__
|
||||
#define STATS_DATA_DIR "/data/misc/stats-data"
|
||||
using android::base::StringPrintf;
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -298,6 +297,300 @@ TEST_P(ConfigUpdateE2eTest, TestConfigTtl) {
|
||||
&buffer);
|
||||
}
|
||||
|
||||
TEST(ConfigUpdateE2eTest, TestNewDurationExistingWhat) {
|
||||
StatsdConfig config;
|
||||
config.add_allowed_log_source("AID_ROOT");
|
||||
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
|
||||
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
|
||||
|
||||
Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
|
||||
*config.add_predicate() = holdingWakelockPredicate;
|
||||
|
||||
ConfigKey key(123, 987);
|
||||
uint64_t bucketStartTimeNs = 10000000000; // 0:10
|
||||
uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL;
|
||||
sp<StatsLogProcessor> processor =
|
||||
CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key);
|
||||
|
||||
int app1Uid = 123;
|
||||
vector<int> attributionUids1 = {app1Uid};
|
||||
vector<string> attributionTags1 = {"App1"};
|
||||
// Create a wakelock acquire, causing the condition to be true.
|
||||
unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
|
||||
attributionUids1, attributionTags1,
|
||||
"wl1"); // 0:10
|
||||
processor->OnLogEvent(event.get());
|
||||
|
||||
// Add metric.
|
||||
DurationMetric* durationMetric = config.add_duration_metric();
|
||||
durationMetric->set_id(StringToId("WakelockDuration"));
|
||||
durationMetric->set_what(holdingWakelockPredicate.id());
|
||||
durationMetric->set_aggregation_type(DurationMetric::SUM);
|
||||
durationMetric->set_bucket(FIVE_MINUTES);
|
||||
|
||||
uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00
|
||||
processor->OnConfigUpdated(updateTimeNs, key, config, /*modular*/ true);
|
||||
|
||||
event = CreateReleaseWakelockEvent(bucketStartTimeNs + 80 * NS_PER_SEC, attributionUids1,
|
||||
attributionTags1,
|
||||
"wl1"); // 1:20
|
||||
processor->OnLogEvent(event.get());
|
||||
uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30
|
||||
ConfigMetricsReportList reports;
|
||||
vector<uint8_t> buffer;
|
||||
processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer);
|
||||
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
|
||||
backfillDimensionPath(&reports);
|
||||
backfillStringInReport(&reports);
|
||||
backfillStartEndTimestamp(&reports);
|
||||
ASSERT_EQ(reports.reports_size(), 1);
|
||||
ASSERT_EQ(reports.reports(0).metrics_size(), 1);
|
||||
EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
|
||||
|
||||
StatsLogReport::DurationMetricDataWrapper metricData;
|
||||
sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData);
|
||||
ASSERT_EQ(metricData.data_size(), 1);
|
||||
DurationMetricData data = metricData.data(0);
|
||||
ASSERT_EQ(data.bucket_info_size(), 1);
|
||||
|
||||
DurationBucketInfo bucketInfo = data.bucket_info(0);
|
||||
EXPECT_EQ(bucketInfo.start_bucket_elapsed_nanos(), updateTimeNs);
|
||||
EXPECT_EQ(bucketInfo.end_bucket_elapsed_nanos(), dumpTimeNs);
|
||||
EXPECT_EQ(bucketInfo.duration_nanos(), 20 * NS_PER_SEC);
|
||||
}
|
||||
|
||||
TEST(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedCondition) {
|
||||
StatsdConfig config;
|
||||
config.add_allowed_log_source("AID_ROOT");
|
||||
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
|
||||
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
|
||||
*config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
|
||||
*config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
|
||||
|
||||
Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
|
||||
// The predicate is dimensioning by first attribution node by uid.
|
||||
*holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
|
||||
CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
|
||||
*config.add_predicate() = holdingWakelockPredicate;
|
||||
|
||||
Predicate isInBackgroundPredicate = CreateIsInBackgroundPredicate();
|
||||
*isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
|
||||
CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/});
|
||||
*config.add_predicate() = isInBackgroundPredicate;
|
||||
|
||||
ConfigKey key(123, 987);
|
||||
uint64_t bucketStartTimeNs = 10000000000; // 0:10
|
||||
uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL;
|
||||
sp<StatsLogProcessor> processor =
|
||||
CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key);
|
||||
|
||||
int app1Uid = 123, app2Uid = 456;
|
||||
vector<int> attributionUids1 = {app1Uid};
|
||||
vector<string> attributionTags1 = {"App1"};
|
||||
vector<int> attributionUids2 = {app2Uid};
|
||||
vector<string> attributionTags2 = {"App2"};
|
||||
unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
|
||||
attributionUids1, attributionTags1,
|
||||
"wl1"); // 0:10
|
||||
processor->OnLogEvent(event.get());
|
||||
event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, app1Uid); // 0:22
|
||||
processor->OnLogEvent(event.get());
|
||||
event = CreateAcquireWakelockEvent(bucketStartTimeNs + 35 * NS_PER_SEC, attributionUids2,
|
||||
attributionTags2,
|
||||
"wl1"); // 0:35
|
||||
processor->OnLogEvent(event.get());
|
||||
|
||||
// Add metric.
|
||||
DurationMetric* durationMetric = config.add_duration_metric();
|
||||
durationMetric->set_id(StringToId("WakelockDuration"));
|
||||
durationMetric->set_what(holdingWakelockPredicate.id());
|
||||
durationMetric->set_condition(isInBackgroundPredicate.id());
|
||||
durationMetric->set_aggregation_type(DurationMetric::SUM);
|
||||
// The metric is dimensioning by first attribution node and only by uid.
|
||||
*durationMetric->mutable_dimensions_in_what() =
|
||||
CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
|
||||
durationMetric->set_bucket(FIVE_MINUTES);
|
||||
// Links between wakelock state atom and condition of app is in background.
|
||||
auto links = durationMetric->add_links();
|
||||
links->set_condition(isInBackgroundPredicate.id());
|
||||
*links->mutable_fields_in_what() =
|
||||
CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
|
||||
*links->mutable_fields_in_condition() =
|
||||
CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/});
|
||||
|
||||
uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00
|
||||
processor->OnConfigUpdated(updateTimeNs, key, config, /*modular*/ true);
|
||||
|
||||
event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 73 * NS_PER_SEC, app2Uid); // 1:13
|
||||
processor->OnLogEvent(event.get());
|
||||
event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1,
|
||||
attributionTags1, "wl1"); // 1:24
|
||||
processor->OnLogEvent(event.get());
|
||||
|
||||
uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30
|
||||
ConfigMetricsReportList reports;
|
||||
vector<uint8_t> buffer;
|
||||
processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer);
|
||||
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
|
||||
backfillDimensionPath(&reports);
|
||||
backfillStringInReport(&reports);
|
||||
backfillStartEndTimestamp(&reports);
|
||||
ASSERT_EQ(reports.reports_size(), 1);
|
||||
ASSERT_EQ(reports.reports(0).metrics_size(), 1);
|
||||
EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
|
||||
|
||||
StatsLogReport::DurationMetricDataWrapper metricData;
|
||||
sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData);
|
||||
ASSERT_EQ(metricData.data_size(), 2);
|
||||
|
||||
DurationMetricData data = metricData.data(0);
|
||||
ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
|
||||
app1Uid);
|
||||
ASSERT_EQ(data.bucket_info_size(), 1);
|
||||
DurationBucketInfo bucketInfo = data.bucket_info(0);
|
||||
EXPECT_EQ(bucketInfo.duration_nanos(), 24 * NS_PER_SEC);
|
||||
|
||||
data = metricData.data(1);
|
||||
ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
|
||||
app2Uid);
|
||||
ASSERT_EQ(data.bucket_info_size(), 1);
|
||||
bucketInfo = data.bucket_info(0);
|
||||
EXPECT_EQ(bucketInfo.duration_nanos(), 17 * NS_PER_SEC);
|
||||
}
|
||||
|
||||
TEST(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedState) {
|
||||
StatsdConfig config;
|
||||
config.add_allowed_log_source("AID_ROOT");
|
||||
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
|
||||
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
|
||||
|
||||
Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
|
||||
// The predicate is dimensioning by first attribution node by uid.
|
||||
*holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
|
||||
CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
|
||||
*config.add_predicate() = holdingWakelockPredicate;
|
||||
|
||||
auto uidProcessState = CreateUidProcessState();
|
||||
*config.add_state() = uidProcessState;
|
||||
|
||||
// Count metric. We don't care about this one. Only use it so the StateTracker gets persisted.
|
||||
CountMetric* countMetric = config.add_count_metric();
|
||||
countMetric->set_id(StringToId("Tmp"));
|
||||
countMetric->set_what(config.atom_matcher(0).id());
|
||||
countMetric->add_slice_by_state(uidProcessState.id());
|
||||
// The metric is dimensioning by first attribution node and only by uid.
|
||||
*countMetric->mutable_dimensions_in_what() =
|
||||
CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
|
||||
countMetric->set_bucket(FIVE_MINUTES);
|
||||
auto stateLink = countMetric->add_state_link();
|
||||
stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED);
|
||||
*stateLink->mutable_fields_in_what() =
|
||||
CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
|
||||
*stateLink->mutable_fields_in_state() =
|
||||
CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/});
|
||||
config.add_no_report_metric(countMetric->id());
|
||||
|
||||
ConfigKey key(123, 987);
|
||||
uint64_t bucketStartTimeNs = 10000000000; // 0:10
|
||||
uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL;
|
||||
sp<StatsLogProcessor> processor =
|
||||
CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key);
|
||||
|
||||
int app1Uid = 123, app2Uid = 456;
|
||||
vector<int> attributionUids1 = {app1Uid};
|
||||
vector<string> attributionTags1 = {"App1"};
|
||||
vector<int> attributionUids2 = {app2Uid};
|
||||
vector<string> attributionTags2 = {"App2"};
|
||||
unique_ptr<LogEvent> event = CreateUidProcessStateChangedEvent(
|
||||
bucketStartTimeNs + 10 * NS_PER_SEC, app1Uid,
|
||||
android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); // 0:10
|
||||
processor->OnLogEvent(event.get());
|
||||
event = CreateAcquireWakelockEvent(bucketStartTimeNs + 22 * NS_PER_SEC, attributionUids1,
|
||||
attributionTags1,
|
||||
"wl1"); // 0:22
|
||||
processor->OnLogEvent(event.get());
|
||||
event = CreateUidProcessStateChangedEvent(
|
||||
bucketStartTimeNs + 30 * NS_PER_SEC, app2Uid,
|
||||
android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); // 0:30
|
||||
processor->OnLogEvent(event.get());
|
||||
|
||||
// Add metric.
|
||||
DurationMetric* durationMetric = config.add_duration_metric();
|
||||
durationMetric->set_id(StringToId("WakelockDuration"));
|
||||
durationMetric->set_what(holdingWakelockPredicate.id());
|
||||
durationMetric->add_slice_by_state(uidProcessState.id());
|
||||
durationMetric->set_aggregation_type(DurationMetric::SUM);
|
||||
// The metric is dimensioning by first attribution node and only by uid.
|
||||
*durationMetric->mutable_dimensions_in_what() =
|
||||
CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
|
||||
durationMetric->set_bucket(FIVE_MINUTES);
|
||||
// Links between wakelock state atom and condition of app is in background.
|
||||
stateLink = durationMetric->add_state_link();
|
||||
stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED);
|
||||
*stateLink->mutable_fields_in_what() =
|
||||
CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
|
||||
*stateLink->mutable_fields_in_state() =
|
||||
CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/});
|
||||
|
||||
uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00
|
||||
processor->OnConfigUpdated(updateTimeNs, key, config, /*modular*/ true);
|
||||
|
||||
event = CreateAcquireWakelockEvent(bucketStartTimeNs + 72 * NS_PER_SEC, attributionUids2,
|
||||
attributionTags2,
|
||||
"wl1"); // 1:13
|
||||
processor->OnLogEvent(event.get());
|
||||
event = CreateUidProcessStateChangedEvent(
|
||||
bucketStartTimeNs + 75 * NS_PER_SEC, app1Uid,
|
||||
android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); // 1:15
|
||||
processor->OnLogEvent(event.get());
|
||||
event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1,
|
||||
attributionTags1, "wl1"); // 1:24
|
||||
processor->OnLogEvent(event.get());
|
||||
|
||||
uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30
|
||||
ConfigMetricsReportList reports;
|
||||
vector<uint8_t> buffer;
|
||||
processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer);
|
||||
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
|
||||
backfillDimensionPath(&reports);
|
||||
backfillStringInReport(&reports);
|
||||
backfillStartEndTimestamp(&reports);
|
||||
ASSERT_EQ(reports.reports_size(), 1);
|
||||
ASSERT_EQ(reports.reports(0).metrics_size(), 1);
|
||||
EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
|
||||
|
||||
StatsLogReport::DurationMetricDataWrapper metricData;
|
||||
sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData);
|
||||
ASSERT_EQ(metricData.data_size(), 3);
|
||||
|
||||
DurationMetricData data = metricData.data(0);
|
||||
ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
|
||||
app1Uid);
|
||||
ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
|
||||
android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND);
|
||||
ASSERT_EQ(data.bucket_info_size(), 1);
|
||||
DurationBucketInfo bucketInfo = data.bucket_info(0);
|
||||
EXPECT_EQ(bucketInfo.duration_nanos(), 15 * NS_PER_SEC);
|
||||
|
||||
data = metricData.data(1);
|
||||
ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
|
||||
app1Uid);
|
||||
ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
|
||||
android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND);
|
||||
ASSERT_EQ(data.bucket_info_size(), 1);
|
||||
bucketInfo = data.bucket_info(0);
|
||||
EXPECT_EQ(bucketInfo.duration_nanos(), 9 * NS_PER_SEC);
|
||||
|
||||
data = metricData.data(2);
|
||||
ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
|
||||
app2Uid);
|
||||
ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
|
||||
android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND);
|
||||
ASSERT_EQ(data.bucket_info_size(), 1);
|
||||
bucketInfo = data.bucket_info(0);
|
||||
EXPECT_EQ(bucketInfo.duration_nanos(), 18 * NS_PER_SEC);
|
||||
}
|
||||
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
#endif
|
||||
|
||||
@@ -73,9 +73,9 @@ TEST(DurationMetricTrackerTest, TestFirstBucket) {
|
||||
FieldMatcher dimensions;
|
||||
|
||||
DurationMetricProducer durationProducer(
|
||||
kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */,
|
||||
3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, 5,
|
||||
600 * NS_PER_SEC + NS_PER_SEC / 2);
|
||||
kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/,
|
||||
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
|
||||
wizard, protoHash, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
|
||||
|
||||
EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs);
|
||||
EXPECT_EQ(10, durationProducer.mCurrentBucketNum);
|
||||
@@ -101,9 +101,9 @@ TEST(DurationMetricTrackerTest, TestNoCondition) {
|
||||
FieldMatcher dimensions;
|
||||
|
||||
DurationMetricProducer durationProducer(
|
||||
kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */,
|
||||
3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
|
||||
bucketStartTimeNs, bucketStartTimeNs);
|
||||
kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/,
|
||||
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
|
||||
wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
|
||||
|
||||
durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
|
||||
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
|
||||
@@ -145,8 +145,9 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) {
|
||||
|
||||
DurationMetricProducer durationProducer(
|
||||
kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
|
||||
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
|
||||
wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
|
||||
-1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
|
||||
3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
|
||||
bucketStartTimeNs, bucketStartTimeNs);
|
||||
durationProducer.mCondition = ConditionState::kFalse;
|
||||
|
||||
EXPECT_FALSE(durationProducer.mCondition);
|
||||
@@ -195,8 +196,9 @@ TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) {
|
||||
|
||||
DurationMetricProducer durationProducer(
|
||||
kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
|
||||
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
|
||||
wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
|
||||
-1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
|
||||
3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
|
||||
bucketStartTimeNs, bucketStartTimeNs);
|
||||
|
||||
EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition);
|
||||
EXPECT_FALSE(durationProducer.isConditionSliced());
|
||||
@@ -240,9 +242,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) {
|
||||
FieldMatcher dimensions;
|
||||
|
||||
DurationMetricProducer durationProducer(
|
||||
kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
|
||||
3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
|
||||
bucketStartTimeNs, bucketStartTimeNs);
|
||||
kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
|
||||
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
|
||||
wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
|
||||
|
||||
int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
|
||||
LogEvent event1(/*uid=*/0, /*pid=*/0);
|
||||
@@ -303,9 +305,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollo
|
||||
FieldMatcher dimensions;
|
||||
|
||||
DurationMetricProducer durationProducer(
|
||||
kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
|
||||
3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
|
||||
bucketStartTimeNs, bucketStartTimeNs);
|
||||
kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
|
||||
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
|
||||
wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
|
||||
|
||||
int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
|
||||
LogEvent event1(/*uid=*/0, /*pid=*/0);
|
||||
@@ -367,9 +369,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) {
|
||||
FieldMatcher dimensions;
|
||||
|
||||
DurationMetricProducer durationProducer(
|
||||
kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
|
||||
3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
|
||||
bucketStartTimeNs, bucketStartTimeNs);
|
||||
kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
|
||||
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
|
||||
wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
|
||||
|
||||
sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
|
||||
EXPECT_TRUE(anomalyTracker != nullptr);
|
||||
@@ -413,9 +415,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) {
|
||||
FieldMatcher dimensions;
|
||||
|
||||
DurationMetricProducer durationProducer(
|
||||
kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
|
||||
3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
|
||||
bucketStartTimeNs, bucketStartTimeNs);
|
||||
kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
|
||||
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
|
||||
wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
|
||||
|
||||
int64_t startTimeNs = bucketStartTimeNs + 1;
|
||||
LogEvent event1(/*uid=*/0, /*pid=*/0);
|
||||
@@ -467,9 +469,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextB
|
||||
FieldMatcher dimensions;
|
||||
|
||||
DurationMetricProducer durationProducer(
|
||||
kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
|
||||
3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
|
||||
bucketStartTimeNs, bucketStartTimeNs);
|
||||
kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
|
||||
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
|
||||
wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
|
||||
|
||||
int64_t startTimeNs = bucketStartTimeNs + 1;
|
||||
LogEvent event1(/*uid=*/0, /*pid=*/0);
|
||||
|
||||
@@ -875,8 +875,9 @@ TEST(MetricsManagerTest, TestCreateAnomalyTrackerDurationTooLong) {
|
||||
FieldMatcher dimensions;
|
||||
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
|
||||
vector<sp<MetricProducer>> metricProducers({new DurationMetricProducer(
|
||||
kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */,
|
||||
3 /* stop_all index */, false /*nesting*/, wizard, 0x0123456789, dimensions, 0, 0)});
|
||||
kConfigKey, metric, -1 /*no condition*/, {}, -1 /* what index not needed*/,
|
||||
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
|
||||
wizard, 0x0123456789, dimensions, 0, 0)});
|
||||
sp<AlarmMonitor> anomalyAlarmMonitor;
|
||||
EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt);
|
||||
}
|
||||
|
||||
@@ -1091,6 +1091,21 @@ void ValidateAttributionUidAndTagDimension(
|
||||
.value_tuple().dimensions_value(1).value_str(), tag);
|
||||
}
|
||||
|
||||
void ValidateStateValue(const google::protobuf::RepeatedPtrField<StateValue>& stateValues,
|
||||
int atomId, int64_t value) {
|
||||
ASSERT_EQ(stateValues.size(), 1);
|
||||
ASSERT_EQ(stateValues[0].atom_id(), atomId);
|
||||
switch (stateValues[0].contents_case()) {
|
||||
case StateValue::ContentsCase::kValue:
|
||||
EXPECT_EQ(stateValues[0].value(), (int32_t)value);
|
||||
break;
|
||||
case StateValue::ContentsCase::kGroupId:
|
||||
EXPECT_EQ(stateValues[0].group_id(), value);
|
||||
break;
|
||||
default:
|
||||
FAIL() << "State value should have either a value or a group id";
|
||||
}
|
||||
}
|
||||
bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) {
|
||||
if (s1.field() != s2.field()) {
|
||||
return false;
|
||||
|
||||
@@ -347,6 +347,8 @@ void ValidateAttributionUidAndTagDimension(
|
||||
const DimensionsValue& value, int atomId, int uid, const std::string& tag);
|
||||
void ValidateAttributionUidAndTagDimension(
|
||||
const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag);
|
||||
void ValidateStateValue(const google::protobuf::RepeatedPtrField<StateValue>& stateValues,
|
||||
int atomId, int64_t value);
|
||||
|
||||
struct DimensionsPair {
|
||||
DimensionsPair(DimensionsValue m1, google::protobuf::RepeatedPtrField<StateValue> m2)
|
||||
|
||||
Reference in New Issue
Block a user