Support slicing by chain.
BUG: b/73975181
Test: statsd test
Change-Id: I913ae0f68ff21ed0703bb5da9c60d3eaa3bf5981
diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp
index 66c4def..cfe477d 100644
--- a/cmds/statsd/benchmark/filter_value_benchmark.cpp
+++ b/cmds/statsd/benchmark/filter_value_benchmark.cpp
@@ -54,28 +54,12 @@
translateFieldMatcher(field_matcher, &matchers);
while (state.KeepRunning()) {
- vector<HashableDimensionKey> output;
- filterValues(matchers, event.getValues(), &output);
- }
-}
-
-BENCHMARK(BM_FilterValue);
-
-static void BM_FilterValue2(benchmark::State& state) {
- LogEvent event(1, 100000);
- FieldMatcher field_matcher;
- createLogEventAndMatcher(&event, &field_matcher);
-
- std::vector<Matcher> matchers;
- translateFieldMatcher(field_matcher, &matchers);
-
- while (state.KeepRunning()) {
HashableDimensionKey output;
filterValues(matchers, event.getValues(), &output);
}
}
-BENCHMARK(BM_FilterValue2);
+BENCHMARK(BM_FilterValue);
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 0c9b701..dfd8705 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -48,6 +48,11 @@
return true;
}
+ if (matcher.hasAllPositionMatcher() &&
+ (mField & (matcher.mMask & kClearAllPositionMatcherMask)) == matcher.mMatcher.getField()) {
+ return true;
+ }
+
return false;
}
@@ -67,6 +72,10 @@
return;
}
switch (matcher.position()) {
+ case Position::ALL:
+ pos[depth] = 0x00;
+ mask[depth] = 0x7f;
+ break;
case Position::ANY:
pos[depth] = 0;
mask[depth] = 0;
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 0e3ae06..f7ce23b 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -30,6 +30,7 @@
const int32_t kMaxLogDepth = 2;
const int32_t kLastBitMask = 0x80;
const int32_t kClearLastBitDeco = 0x7f;
+const int32_t kClearAllPositionMatcherMask = 0xffff00ff;
enum Type { UNKNOWN, INT, LONG, FLOAT, STRING };
@@ -205,6 +206,7 @@
* First: [Matcher Field] 0x02010101 [Mask]0xff7f7f7f
* Last: [Matcher Field] 0x02018001 [Mask]0xff7f807f
* Any: [Matcher Field] 0x02010001 [Mask]0xff7f007f
+ * All: [Matcher Field] 0x02010001 [Mask]0xff7f7f7f
*
* [To match a log Field with a Matcher] we apply the bit mask to the log Field and check if
* the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are
@@ -226,9 +228,21 @@
return mMask;
}
+ inline int32_t getRawMaskAtDepth(int32_t depth) const {
+ int32_t field = (mMask & 0x00ffffff);
+ int32_t shift = 8 * (kMaxLogDepth - depth);
+ int32_t mask = 0xff << shift;
+
+ return (field & mask) >> shift;
+ }
+
+ bool hasAllPositionMatcher() const {
+ return mMatcher.getDepth() == 2 && getRawMaskAtDepth(1) == 0x7f;
+ }
+
bool hasAnyPositionMatcher(int* prefix) const {
- if (mMatcher.getDepth() == 2 && mMatcher.getRawPosAtDepth(2) == 0) {
- (*prefix) = mMatcher.getPrefix(2);
+ if (mMatcher.getDepth() == 2 && mMatcher.getRawPosAtDepth(1) == 0) {
+ (*prefix) = mMatcher.getPrefix(1);
return true;
}
return false;
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index d0c8311..7103034 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -61,125 +61,22 @@
bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values,
HashableDimensionKey* output) {
- for (size_t i = 0; i < matcherFields.size(); ++i) {
- const auto& matcher = matcherFields[i];
- bool found = false;
- for (const auto& value : values) {
+ size_t num_matches = 0;
+ for (const auto& value : values) {
+ for (size_t i = 0; i < matcherFields.size(); ++i) {
+ const auto& matcher = matcherFields[i];
// TODO: potential optimization here to break early because all fields are naturally
// sorted.
if (value.mField.matches(matcher)) {
output->addValue(value);
- output->mutableValue(i)->mField.setTag(value.mField.getTag());
- output->mutableValue(i)->mField.setField(value.mField.getField() & matcher.mMask);
- found = true;
- break;
- }
- }
-
- if (!found) {
- VLOG("We can't find a dimension value for matcher (%d)%#x.", matcher.mMatcher.getTag(),
- matcher.mMatcher.getField());
- return false;
- }
- }
-
- return true;
-}
-
-// Filter fields using the matchers and output the results as a HashableDimensionKey.
-// Note: HashableDimensionKey is just a wrapper for vector<FieldValue>
-bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values,
- vector<HashableDimensionKey>* output) {
- output->push_back(HashableDimensionKey());
- // Top level is only tag id. Now take the real child matchers
- int prevAnyMatcherPrefix = 0;
- size_t prevPrevFanout = 0;
- size_t prevFanout = 0;
-
- // For each matcher get matched results.
- vector<FieldValue> matchedResults(2);
- for (const auto& matcher : matcherFields) {
- size_t num_matches = 0;
- for (const auto& value : values) {
- // TODO: potential optimization here to break early because all fields are naturally
- // sorted.
- if (value.mField.matches(matcher)) {
- if (num_matches >= matchedResults.size()) {
- matchedResults.resize(num_matches * 2);
- }
- matchedResults[num_matches].mField.setTag(value.mField.getTag());
- matchedResults[num_matches].mField.setField(value.mField.getField() & matcher.mMask);
- matchedResults[num_matches].mValue = value.mValue;
+ output->mutableValue(num_matches)->mField.setTag(value.mField.getTag());
+ output->mutableValue(num_matches)->mField.setField(
+ value.mField.getField() & matcher.mMask);
num_matches++;
}
}
-
- if (num_matches == 0) {
- VLOG("We can't find a dimension value for matcher (%d)%#x.", matcher.mMatcher.getTag(),
- matcher.mMatcher.getField());
- continue;
- }
-
- if (num_matches == 1) {
- for (auto& dimension : *output) {
- dimension.addValue(matchedResults[0]);
- }
- prevAnyMatcherPrefix = 0;
- prevFanout = 0;
- continue;
- }
-
- // All the complexity below is because we support ANY in dimension.
- bool createFanout = true;
- // createFanout is true when the matcher doesn't need to follow the prev matcher's
- // order.
- // e.g., get (uid, tag) from any position in attribution. because we have translated
- // it as 2 matchers, they need to follow the same ordering, we can't create a cross
- // product of all uid and tags.
- // However, if the 2 matchers have different prefix, they will create a cross product
- // e.g., [any uid] [any some other repeated field], we will create a cross product for them
- if (prevAnyMatcherPrefix != 0) {
- int anyMatcherPrefix = 0;
- bool isAnyMatcher = matcher.hasAnyPositionMatcher(&anyMatcherPrefix);
- if (isAnyMatcher && anyMatcherPrefix == prevAnyMatcherPrefix) {
- createFanout = false;
- } else {
- prevAnyMatcherPrefix = anyMatcherPrefix;
- }
- }
-
- // Each matcher should match exact one field, unless position is ANY
- // When x number of fields matches a matcher, the returned dimension
- // size is multiplied by x.
- int oldSize;
- if (createFanout) {
- // First create fanout (fanout size is matchedResults.Size which could be one,
- // which means we do nothing here)
- oldSize = output->size();
- for (size_t i = 1; i < num_matches; i++) {
- output->insert(output->end(), output->begin(), output->begin() + oldSize);
- }
- prevPrevFanout = oldSize;
- prevFanout = num_matches;
- } else {
- // If we should not create fanout, e.g., uid tag from same position should be remain
- // together.
- oldSize = prevPrevFanout;
- if (prevFanout != num_matches) {
- // sanity check.
- ALOGE("2 Any matcher result in different output");
- return false;
- }
- }
- // now add the matched field value to output
- for (size_t i = 0; i < num_matches; i++) {
- for (int j = 0; j < oldSize; j++) {
- (*output)[i * oldSize + j].addValue(matchedResults[i]);
- }
- }
}
-
- return output->size() > 0 && (*output)[0].getValues().size() > 0;
+ return num_matches > 0;
}
void filterGaugeValues(const std::vector<Matcher>& matcherFields,
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 4cfed88..6f4941f 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -122,17 +122,14 @@
/**
* Creating HashableDimensionKeys from FieldValues using matcher.
*
- * This function may make modifications to the Field if the matcher has Position=LAST or ANY in
- * it. This is because: for example, when we create dimension from last uid in attribution chain,
+ * This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
+ * in it. This is because: for example, when we create dimension from last uid in attribution chain,
* In one event, uid 1000 is at position 5 and it's the last
* In another event, uid 1000 is at position 6, and it's the last
* these 2 events should be mapped to the same dimension. So we will remove the original position
* from the dimension key for the uid field (by applying 0x80 bit mask).
*/
bool filterValues(const std::vector<Matcher>& matcherFields, const std::vector<FieldValue>& values,
- std::vector<HashableDimensionKey>* output);
-// This function is used when there is at most one output dimension key. (no ANY matcher)
-bool filterValues(const std::vector<Matcher>& matcherFields, const std::vector<FieldValue>& values,
HashableDimensionKey* output);
/**
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 8db8200..8b42146 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -121,7 +121,8 @@
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3);
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1);
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2);
- FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
+ FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
+ FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain);
FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
@@ -138,6 +139,7 @@
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition);
+
};
} // namespace statsd
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 4913aef..73efb39 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -327,25 +327,7 @@
// have both sliced and unsliced version of a predicate.
handleConditionEvent(outputValue, matchedState == 1, &overallState, &overallChanged);
} else {
- std::vector<HashableDimensionKey> outputValues;
- filterValues(mOutputDimensions, event.getValues(), &outputValues);
-
- // If this event has multiple nodes in the attribution chain, this log event probably will
- // generate multiple dimensions. If so, we will find if the condition changes for any
- // dimension and ask the corresponding metric producer to verify whether the actual sliced
- // condition has changed or not.
- // A high level assumption is that a predicate is either sliced or unsliced. We will never
- // have both sliced and unsliced version of a predicate.
- for (const HashableDimensionKey& outputValue : outputValues) {
- ConditionState tempState;
- bool tempChanged = false;
- handleConditionEvent(outputValue, matchedState == 1, &tempState, &tempChanged);
- if (tempChanged) {
- overallChanged = true;
- }
- // ConditionState's | operator is overridden
- overallState = overallState | tempState;
- }
+ ALOGE("The condition tracker should not be sliced by ANY position matcher.");
}
conditionCache[mIndex] = overallState;
conditionChangedCache[mIndex] = overallChanged;
diff --git a/cmds/statsd/src/condition/StateTracker.cpp b/cmds/statsd/src/condition/StateTracker.cpp
index c68875c..fe1740b 100644
--- a/cmds/statsd/src/condition/StateTracker.cpp
+++ b/cmds/statsd/src/condition/StateTracker.cpp
@@ -137,21 +137,18 @@
VLOG("StateTracker evaluate event %s", event.ToString().c_str());
- vector<HashableDimensionKey> keys;
- vector<HashableDimensionKey> outputs;
- filterValues(mPrimaryKeys, event.getValues(), &keys);
- filterValues(mOutputDimensions, event.getValues(), &outputs);
- if (keys.size() != 1 || outputs.size() != 1) {
- ALOGE("More than 1 states in the event?? panic now!");
+ // Primary key can exclusive fields must be simple fields. so there won't be more than
+ // one keys matched.
+ HashableDimensionKey primaryKey;
+ HashableDimensionKey state;
+ if (!filterValues(mPrimaryKeys, event.getValues(), &primaryKey) ||
+ !filterValues(mOutputDimensions, event.getValues(), &state)) {
+ ALOGE("Failed to filter fields in the event?? panic now!");
conditionCache[mIndex] =
mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse;
conditionChangedCache[mIndex] = false;
return;
}
- // Primary key can exclusive fields must be simple fields. so there won't be more than
- // one keys matched.
- const auto& primaryKey = keys[0];
- const auto& state = outputs[0];
hitGuardRail(primaryKey);
VLOG("StateTracker: key %s state %s", primaryKey.toString().c_str(), state.toString().c_str());
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 364d4e9..b7105b3 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -185,6 +185,9 @@
ranges.push_back(std::make_pair(newStart, end));
break;
}
+ case Position::ALL:
+ ALOGE("Not supported: field matcher with ALL position.");
+ break;
case Position::POSITION_UNKNOWN:
break;
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index bc09683..c6b9405 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -88,6 +88,12 @@
translateFieldMatcher(internalDimensions, &mInternalDimensions);
mContainANYPositionInInternalDimensions = HasPositionANY(internalDimensions);
}
+ if (mContainANYPositionInInternalDimensions) {
+ ALOGE("Position ANY in internal dimension not supported.");
+ }
+ if (mContainANYPositionInDimensionsInWhat) {
+ ALOGE("Position ANY in dimension_in_what not supported.");
+ }
if (metric.has_dimensions_in_condition()) {
translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
@@ -589,18 +595,10 @@
it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
event.GetElapsedTimestampNs(), conditionKeys);
} else {
- if (mContainANYPositionInInternalDimensions) {
- std::vector<HashableDimensionKey> dimensionKeys;
- filterValues(mInternalDimensions, event.getValues(), &dimensionKeys);
- for (const auto& key : dimensionKeys) {
- it->second->noteStart(key, condition, event.GetElapsedTimestampNs(), conditionKeys);
- }
- } else {
- HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
- filterValues(mInternalDimensions, event.getValues(), &dimensionKey);
- it->second->noteStart(
- dimensionKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
- }
+ HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
+ filterValues(mInternalDimensions, event.getValues(), &dimensionKey);
+ it->second->noteStart(
+ dimensionKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
}
}
@@ -612,8 +610,8 @@
ALOGW("Not used in duration tracker.");
}
-void DurationMetricProducer::onMatchedLogEventLocked_simple(const size_t matcherIndex,
- const LogEvent& event) {
+void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
+ const LogEvent& event) {
uint64_t eventTimeNs = event.GetElapsedTimestampNs();
if (eventTimeNs < mStartTimeNs) {
return;
@@ -712,117 +710,6 @@
}
}
-void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
- const LogEvent& event) {
- if (!mContainANYPositionInDimensionsInWhat) {
- onMatchedLogEventLocked_simple(matcherIndex, event);
- return;
- }
-
- uint64_t eventTimeNs = event.GetElapsedTimestampNs();
- if (eventTimeNs < mStartTimeNs) {
- return;
- }
-
- flushIfNeededLocked(event.GetElapsedTimestampNs());
-
- // Handles Stopall events.
- if (matcherIndex == mStopAllIndex) {
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->noteStopAll(event.GetElapsedTimestampNs());
- }
- }
- return;
- }
-
- vector<HashableDimensionKey> dimensionInWhatValues;
- if (!mDimensionsInWhat.empty()) {
- filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues);
- } else {
- dimensionInWhatValues.push_back(DEFAULT_DIMENSION_KEY);
- }
-
- // Handles Stop events.
- if (matcherIndex == mStopIndex) {
- if (mUseWhatDimensionAsInternalDimension) {
- for (const HashableDimensionKey& whatKey : dimensionInWhatValues) {
- auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
- if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- for (const auto& condIt : whatIt->second) {
- condIt.second->noteStop(whatKey, event.GetElapsedTimestampNs(), false);
- }
- }
- }
- return;
- }
-
- HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY;
- if (!mInternalDimensions.empty()) {
- filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey);
- }
-
- for (const HashableDimensionKey& whatDimension : dimensionInWhatValues) {
- auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatDimension);
- if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- for (const auto& condIt : whatIt->second) {
- condIt.second->noteStop(
- internalDimensionKey, event.GetElapsedTimestampNs(), false);
- }
- }
- }
- return;
- }
-
- bool condition;
- ConditionKey conditionKey;
- std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
- if (mConditionSliced) {
- for (const auto& link : mMetric2ConditionLinks) {
- getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
- }
-
- auto conditionState =
- mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
- !mSameConditionDimensionsInTracker,
- !mHasLinksToAllConditionDimensionsInTracker,
- &dimensionKeysInCondition);
- condition = (conditionState == ConditionState::kTrue);
- if (mDimensionsInCondition.empty() && condition) {
- dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
- }
- } else {
- condition = mCondition;
- if (condition) {
- dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
- }
- }
-
- for (const auto& whatDimension : dimensionInWhatValues) {
- if (dimensionKeysInCondition.empty()) {
- handleStartEvent(MetricDimensionKey(whatDimension, DEFAULT_DIMENSION_KEY),
- conditionKey, condition, event);
- } else {
- auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatDimension);
- // If the what dimension is already there, we should update all the trackers even
- // the condition is false.
- if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- for (const auto& condIt : whatIt->second) {
- const bool cond = dimensionKeysInCondition.find(condIt.first) !=
- dimensionKeysInCondition.end();
- handleStartEvent(MetricDimensionKey(whatDimension, condIt.first),
- conditionKey, cond, event);
- dimensionKeysInCondition.erase(condIt.first);
- }
- }
- for (const auto& conditionDimension : dimensionKeysInCondition) {
- handleStartEvent(MetricDimensionKey(whatDimension, conditionDimension),
- conditionKey, condition, event);
- }
- }
- }
-}
-
size_t DurationMetricProducer::byteSizeLocked() const {
size_t totalSize = 0;
for (const auto& pair : mPastBuckets) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 6746e11..985749d 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -52,8 +52,6 @@
protected:
void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override;
- void onMatchedLogEventLocked_simple(const size_t matcherIndex, const LogEvent& event);
-
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKeys, bool condition,
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 6c90b03..f4495a1 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -53,39 +53,17 @@
dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
}
- if (mContainANYPositionInDimensionsInWhat) {
- vector<HashableDimensionKey> dimensionInWhatValues;
- if (!mDimensionsInWhat.empty()) {
- filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues);
- } else {
- dimensionInWhatValues.push_back(DEFAULT_DIMENSION_KEY);
- }
-
- for (const auto& whatDimension : dimensionInWhatValues) {
- for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
- onMatchedLogEventInternalLocked(
- matcherIndex, MetricDimensionKey(whatDimension, conditionDimensionKey),
- conditionKey, condition, event);
- }
- if (dimensionKeysInCondition.empty()) {
- onMatchedLogEventInternalLocked(
- matcherIndex, MetricDimensionKey(whatDimension, DEFAULT_DIMENSION_KEY),
- conditionKey, condition, event);
- }
- }
- } else {
- HashableDimensionKey dimensionInWhat;
- filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
- MetricDimensionKey metricKey(dimensionInWhat, DEFAULT_DIMENSION_KEY);
- for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
- metricKey.setDimensionKeyInCondition(conditionDimensionKey);
- onMatchedLogEventInternalLocked(
- matcherIndex, metricKey, conditionKey, condition, event);
- }
- if (dimensionKeysInCondition.empty()) {
- onMatchedLogEventInternalLocked(
- matcherIndex, metricKey, conditionKey, condition, event);
- }
+ HashableDimensionKey dimensionInWhat;
+ filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
+ MetricDimensionKey metricKey(dimensionInWhat, DEFAULT_DIMENSION_KEY);
+ for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
+ metricKey.setDimensionKeyInCondition(conditionDimensionKey);
+ onMatchedLogEventInternalLocked(
+ matcherIndex, metricKey, conditionKey, condition, event);
+ }
+ if (dimensionKeysInCondition.empty()) {
+ onMatchedLogEventInternalLocked(
+ matcherIndex, metricKey, conditionKey, condition, event);
}
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index dbab814..8726c3c 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -158,7 +158,8 @@
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
- FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
+ FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
+ FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain);
FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 1c99e2a..2c70191 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -31,6 +31,8 @@
LAST = 2;
ANY = 3;
+
+ ALL = 4;
}
enum TimeUnit {
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index 5846761..73e7c44 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -45,20 +45,41 @@
const auto& matcher12 = output[0];
EXPECT_EQ((int32_t)10, matcher12.mMatcher.getTag());
- EXPECT_EQ((int32_t)0x2010001, matcher12.mMatcher.getField());
+ EXPECT_EQ((int32_t)0x02010001, matcher12.mMatcher.getField());
EXPECT_EQ((int32_t)0xff7f007f, matcher12.mMask);
}
-TEST(AtomMatcherTest, TestFilter) {
+TEST(AtomMatcherTest, TestFieldTranslation_ALL) {
FieldMatcher matcher1;
matcher1.set_field(10);
FieldMatcher* child = matcher1.add_child();
child->set_field(1);
- child->set_position(Position::ANY);
+ child->set_position(Position::ALL);
child = child->add_child();
child->set_field(1);
+ vector<Matcher> output;
+ translateFieldMatcher(matcher1, &output);
+
+ EXPECT_EQ((size_t)1, output.size());
+
+ const auto& matcher12 = output[0];
+ EXPECT_EQ((int32_t)10, matcher12.mMatcher.getTag());
+ EXPECT_EQ((int32_t)0x02010001, matcher12.mMatcher.getField());
+ EXPECT_EQ((int32_t)0xff7f7f7f, matcher12.mMask);
+}
+
+TEST(AtomMatcherTest, TestFilter_ALL) {
+ FieldMatcher matcher1;
+ matcher1.set_field(10);
+ FieldMatcher* child = matcher1.add_child();
+ child->set_field(1);
+ child->set_position(Position::ALL);
+
+ child->add_child()->set_field(1);
+ child->add_child()->set_field(2);
+
child = matcher1.add_child();
child->set_field(2);
@@ -85,32 +106,28 @@
event.write("some value");
// Convert to a LogEvent
event.init();
- vector<HashableDimensionKey> output;
+ HashableDimensionKey output;
filterValues(matchers, event.getValues(), &output);
- EXPECT_EQ((size_t)(3), output.size());
+ EXPECT_EQ((size_t)7, output.getValues().size());
+ EXPECT_EQ((int32_t)0x02010101, output.getValues()[0].mField.getField());
+ EXPECT_EQ((int32_t)1111, output.getValues()[0].mValue.int_value);
+ EXPECT_EQ((int32_t)0x02010102, output.getValues()[1].mField.getField());
+ EXPECT_EQ("location1", output.getValues()[1].mValue.str_value);
- const auto& key1 = output[0];
- EXPECT_EQ((size_t)2, key1.getValues().size());
- EXPECT_EQ((int32_t)0x02010001, key1.getValues()[0].mField.getField());
- EXPECT_EQ((int32_t)1111, key1.getValues()[0].mValue.int_value);
- EXPECT_EQ((int32_t)0x00020000, key1.getValues()[1].mField.getField());
- EXPECT_EQ("some value", key1.getValues()[1].mValue.str_value);
+ EXPECT_EQ((int32_t)0x02010201, output.getValues()[2].mField.getField());
+ EXPECT_EQ((int32_t)2222, output.getValues()[2].mValue.int_value);
+ EXPECT_EQ((int32_t)0x02010202, output.getValues()[3].mField.getField());
+ EXPECT_EQ("location2", output.getValues()[3].mValue.str_value);
- const auto& key2 = output[1];
- EXPECT_EQ((size_t)2, key2.getValues().size());
- EXPECT_EQ((int32_t)0x02010001, key2.getValues()[0].mField.getField());
- EXPECT_EQ((int32_t)2222, key2.getValues()[0].mValue.int_value);
- EXPECT_EQ((int32_t)0x00020000, key2.getValues()[1].mField.getField());
- EXPECT_EQ("some value", key2.getValues()[1].mValue.str_value);
+ EXPECT_EQ((int32_t)0x02010301, output.getValues()[4].mField.getField());
+ EXPECT_EQ((int32_t)3333, output.getValues()[4].mValue.int_value);
+ EXPECT_EQ((int32_t)0x02010302, output.getValues()[5].mField.getField());
+ EXPECT_EQ("location3", output.getValues()[5].mValue.str_value);
- const auto& key3 = output[2];
- EXPECT_EQ((size_t)2, key3.getValues().size());
- EXPECT_EQ((int32_t)0x02010001, key3.getValues()[0].mField.getField());
- EXPECT_EQ((int32_t)3333, key3.getValues()[0].mValue.int_value);
- EXPECT_EQ((int32_t)0x00020000, key3.getValues()[1].mField.getField());
- EXPECT_EQ("some value", key3.getValues()[1].mValue.str_value);
+ EXPECT_EQ((int32_t)0x00020000, output.getValues()[6].mField.getField());
+ EXPECT_EQ("some value", output.getValues()[6].mValue.str_value);
}
TEST(AtomMatcherTest, TestSubDimension) {
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index 7a7e000..2574ba7 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -28,7 +28,7 @@
namespace {
-StatsdConfig CreateStatsdConfig() {
+StatsdConfig CreateStatsdConfig(const Position position) {
StatsdConfig config;
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
auto attributionNodeMatcher =
@@ -46,15 +46,15 @@
countMetric->set_what(wakelockAcquireMatcher.id());
*countMetric->mutable_dimensions_in_what() =
CreateAttributionUidAndTagDimensions(
- android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ android::util::WAKELOCK_STATE_CHANGED, {position});
countMetric->set_bucket(FIVE_MINUTES);
return config;
}
} // namespace
-TEST(AttributionE2eTest, TestAttributionMatchAndSlice) {
- auto config = CreateStatsdConfig();
+TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid) {
+ auto config = CreateStatsdConfig(Position::FIRST);
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
@@ -199,6 +199,215 @@
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
}
+TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain) {
+ auto config = CreateStatsdConfig(Position::ALL);
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ // Here it assumes that GMS core has two uids.
+ processor->getUidMap()->updateApp(
+ android::String16("com.android.gmscore"), 222 /* uid */, 1 /* version code*/);
+ processor->getUidMap()->updateApp(
+ android::String16("com.android.gmscore"), 444 /* uid */, 1 /* version code*/);
+ processor->getUidMap()->updateApp(
+ android::String16("app1"), 111 /* uid */, 2 /* version code*/);
+ processor->getUidMap()->updateApp(
+ android::String16("APP3"), 333 /* uid */, 2 /* version code*/);
+
+ // GMS core node is in the middle.
+ std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
+ CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(333, "App3")};
+
+ // GMS core node is the last one.
+ std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App1"),
+ CreateAttribution(333, "App3"),
+ CreateAttribution(222, "GMSCoreModule1")};
+
+ // GMS core node is the first one.
+ std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(333, "App3")};
+
+ // Single GMS core node.
+ std::vector<AttributionNodeInternal> attributions4 = {CreateAttribution(222, "GMSCoreModule1")};
+
+ // GMS core has another uid.
+ std::vector<AttributionNodeInternal> attributions5 = {CreateAttribution(111, "App1"),
+ CreateAttribution(444, "GMSCoreModule2"),
+ CreateAttribution(333, "App3")};
+
+ // Multiple GMS core nodes.
+ std::vector<AttributionNodeInternal> attributions6 = {CreateAttribution(444, "GMSCoreModule2"),
+ CreateAttribution(222, "GMSCoreModule1")};
+
+ // No GMS core nodes.
+ std::vector<AttributionNodeInternal> attributions7 = {CreateAttribution(111, "App1"),
+ CreateAttribution(333, "App3")};
+ std::vector<AttributionNodeInternal> attributions8 = {CreateAttribution(111, "App1")};
+
+ // GMS core node with isolated uid.
+ const int isolatedUid = 666;
+ std::vector<AttributionNodeInternal> attributions9 = {
+ CreateAttribution(isolatedUid, "GMSCoreModule1")};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ // Events 1~4 are in the 1st bucket.
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 2));
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions2, "wl1", bucketStartTimeNs + 200));
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions3, "wl1", bucketStartTimeNs + bucketSizeNs - 1));
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions4, "wl1", bucketStartTimeNs + bucketSizeNs));
+
+ // Events 5~8 are in the 3rd bucket.
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions5, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1));
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions6, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 100));
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions7, "wl2", bucketStartTimeNs + 3 * bucketSizeNs - 2));
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions8, "wl2", bucketStartTimeNs + 3 * bucketSizeNs));
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 1));
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 100));
+ events.push_back(CreateIsolatedUidChangedEvent(
+ isolatedUid, 222, true/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs - 1));
+ events.push_back(CreateIsolatedUidChangedEvent(
+ isolatedUid, 222, false/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs + 10));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ EXPECT_EQ(countMetrics.data_size(), 6);
+
+ auto data = countMetrics.data(0);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
+ EXPECT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(1, data.bucket_info(1).count());
+ EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
+ data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs,
+ data.bucket_info(1).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(1);
+ ValidateUidDimension(
+ data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
+ ValidateUidDimension(
+ data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+ data = countMetrics.data(2);
+ ValidateUidDimension(
+ data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2");
+ ValidateUidDimension(
+ data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(3);
+ ValidateUidDimension(
+ data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
+ ValidateUidDimension(
+ data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
+ ValidateUidDimension(
+ data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(bucketStartTimeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(4);
+ ValidateUidDimension(
+ data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
+ ValidateUidDimension(
+ data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
+ ValidateUidDimension(
+ data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(bucketStartTimeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(5);
+ ValidateUidDimension(
+ data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
+ ValidateUidDimension(
+ data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2");
+ ValidateUidDimension(
+ data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 2678c8a..0f785df 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -500,12 +500,42 @@
.value_tuple().dimensions_value(0).value_int(), uid);
}
+void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid) {
+ EXPECT_EQ(value.field(), atomId);
+ EXPECT_GT(value.value_tuple().dimensions_value_size(), node_idx);
+ // Attribution field.
+ EXPECT_EQ(value.value_tuple().dimensions_value(node_idx).field(), 1);
+ EXPECT_EQ(value.value_tuple().dimensions_value(node_idx)
+ .value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(value.value_tuple().dimensions_value(node_idx)
+ .value_tuple().dimensions_value(0).value_int(), uid);
+}
+
+void ValidateAttributionUidAndTagDimension(
+ const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag) {
+ EXPECT_EQ(value.field(), atomId);
+ EXPECT_GT(value.value_tuple().dimensions_value_size(), node_idx);
+ // Attribution field.
+ EXPECT_EQ(1, value.value_tuple().dimensions_value(node_idx).field());
+ // Uid only.
+ EXPECT_EQ(2, value.value_tuple().dimensions_value(node_idx)
+ .value_tuple().dimensions_value_size());
+ EXPECT_EQ(1, value.value_tuple().dimensions_value(node_idx)
+ .value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(uid, value.value_tuple().dimensions_value(node_idx)
+ .value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(2, value.value_tuple().dimensions_value(node_idx)
+ .value_tuple().dimensions_value(1).field());
+ EXPECT_EQ(tag, value.value_tuple().dimensions_value(node_idx)
+ .value_tuple().dimensions_value(1).value_str());
+}
+
void ValidateAttributionUidAndTagDimension(
const DimensionsValue& value, int atomId, int uid, const std::string& tag) {
EXPECT_EQ(value.field(), atomId);
- EXPECT_EQ(value.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(1, value.value_tuple().dimensions_value_size());
// Attribution field.
- EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(1, value.value_tuple().dimensions_value(0).field());
// Uid only.
EXPECT_EQ(value.value_tuple().dimensions_value(0)
.value_tuple().dimensions_value_size(), 2);
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 14eba1f..1ac630c 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -180,9 +180,12 @@
int64_t StringToId(const string& str);
+void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid);
void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid);
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);
struct DimensionsPair {
DimensionsPair(DimensionsValue m1, DimensionsValue m2) : dimInWhat(m1), dimInCondition(m2){};