From e06cfd777a501eca17f8dd796b32ad61002c5875 Mon Sep 17 00:00:00 2001 From: Yangster-mac Date: Sat, 10 Mar 2018 23:22:59 -0800 Subject: [PATCH] Support slicing by chain. BUG: b/73975181 Test: statsd test Change-Id: I913ae0f68ff21ed0703bb5da9c60d3eaa3bf5981 --- .../benchmark/filter_value_benchmark.cpp | 18 +- cmds/statsd/src/FieldValue.cpp | 9 + cmds/statsd/src/FieldValue.h | 18 +- cmds/statsd/src/HashableDimensionKey.cpp | 119 +--------- cmds/statsd/src/HashableDimensionKey.h | 7 +- cmds/statsd/src/StatsLogProcessor.h | 4 +- .../src/condition/SimpleConditionTracker.cpp | 20 +- cmds/statsd/src/condition/StateTracker.cpp | 17 +- cmds/statsd/src/matchers/matcher_util.cpp | 3 + .../src/metrics/DurationMetricProducer.cpp | 137 +---------- .../src/metrics/DurationMetricProducer.h | 2 - cmds/statsd/src/metrics/MetricProducer.cpp | 44 +--- cmds/statsd/src/metrics/MetricsManager.h | 3 +- cmds/statsd/src/statsd_config.proto | 2 + cmds/statsd/tests/FieldValue_test.cpp | 63 +++-- .../statsd/tests/e2e/Attribution_e2e_test.cpp | 217 +++++++++++++++++- cmds/statsd/tests/statsd_test_util.cpp | 34 ++- cmds/statsd/tests/statsd_test_util.h | 3 + 18 files changed, 365 insertions(+), 355 deletions(-) diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp index 66c4defe7adb8..cfe477d7ca8f9 100644 --- a/cmds/statsd/benchmark/filter_value_benchmark.cpp +++ b/cmds/statsd/benchmark/filter_value_benchmark.cpp @@ -54,29 +54,13 @@ static void BM_FilterValue(benchmark::State& state) { translateFieldMatcher(field_matcher, &matchers); while (state.KeepRunning()) { - vector output; + 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 matchers; - translateFieldMatcher(field_matcher, &matchers); - - while (state.KeepRunning()) { - HashableDimensionKey output; - filterValues(matchers, event.getValues(), &output); - } -} - -BENCHMARK(BM_FilterValue2); - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp index 0c9b7016eaff1..dfd8705f83aa3 100644 --- a/cmds/statsd/src/FieldValue.cpp +++ b/cmds/statsd/src/FieldValue.cpp @@ -48,6 +48,11 @@ bool Field::matches(const Matcher& matcher) const { return true; } + if (matcher.hasAllPositionMatcher() && + (mField & (matcher.mMask & kClearAllPositionMatcherMask)) == matcher.mMatcher.getField()) { + return true; + } + return false; } @@ -67,6 +72,10 @@ void translateFieldMatcher(int tag, const FieldMatcher& matcher, int depth, int* 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 0e3ae06033e75..f7ce23b04339b 100644 --- a/cmds/statsd/src/FieldValue.h +++ b/cmds/statsd/src/FieldValue.h @@ -30,6 +30,7 @@ const int32_t kAttributionField = 1; 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 @@ public: * 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 @@ struct Matcher { 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 d0c83119dc43e..71030345b0aa4 100644 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ b/cmds/statsd/src/HashableDimensionKey.cpp @@ -61,125 +61,22 @@ android::hash_t hashDimension(const HashableDimensionKey& value) { bool filterValues(const vector& matcherFields, const vector& 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 -bool filterValues(const vector& matcherFields, const vector& values, - vector* 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 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& matcherFields, diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h index 4cfed883ec075..6f4941f717eed 100644 --- a/cmds/statsd/src/HashableDimensionKey.h +++ b/cmds/statsd/src/HashableDimensionKey.h @@ -122,16 +122,13 @@ android::hash_t hashDimension(const HashableDimensionKey& key); /** * 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& matcherFields, const std::vector& values, - std::vector* output); -// This function is used when there is at most one output dimension key. (no ANY matcher) bool filterValues(const std::vector& matcherFields, const std::vector& values, HashableDimensionKey* output); diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 8db82006d082f..8b42146c66214 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -121,7 +121,8 @@ private: 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 @@ private: 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 4913aef3347ff..73efb39ac1195 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -327,25 +327,7 @@ void SimpleConditionTracker::evaluateCondition( // have both sliced and unsliced version of a predicate. handleConditionEvent(outputValue, matchedState == 1, &overallState, &overallChanged); } else { - std::vector 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 c68875c581622..fe1740b24dd43 100644 --- a/cmds/statsd/src/condition/StateTracker.cpp +++ b/cmds/statsd/src/condition/StateTracker.cpp @@ -137,21 +137,18 @@ void StateTracker::evaluateCondition(const LogEvent& event, VLOG("StateTracker evaluate event %s", event.ToString().c_str()); - vector keys; - vector 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 364d4e9d5c944..b7105b351a105 100644 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -185,6 +185,9 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, 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 bc09683d79ade..c6b9405e424d3 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -88,6 +88,12 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat 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 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(), conditionKeys); } else { - if (mContainANYPositionInInternalDimensions) { - std::vector 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 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( 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_simple(const size_t matcher } } -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 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 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 6746e116333f0..985749df63ddf 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -52,8 +52,6 @@ public: 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 6c90b031a9cce..f4495a19d7001 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -53,39 +53,17 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY); } - if (mContainANYPositionInDimensionsInWhat) { - vector 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 dbab81431743b..8726c3cc4e527 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -158,7 +158,8 @@ private: 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 1c99e2ad03bcf..2c701915f4298 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -31,6 +31,8 @@ enum Position { 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 5846761cb8e98..73e7c44dc3da0 100644 --- a/cmds/statsd/tests/FieldValue_test.cpp +++ b/cmds/statsd/tests/FieldValue_test.cpp @@ -45,20 +45,41 @@ TEST(AtomMatcherTest, TestFieldTranslation) { 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 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 @@ TEST(AtomMatcherTest, TestFilter) { event.write("some value"); // Convert to a LogEvent event.init(); - vector 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 7a7e000fb98ff..2574ba79f9d3b 100644 --- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp @@ -28,7 +28,7 @@ namespace statsd { namespace { -StatsdConfig CreateStatsdConfig() { +StatsdConfig CreateStatsdConfig(const Position position) { StatsdConfig config; auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); auto attributionNodeMatcher = @@ -46,15 +46,15 @@ StatsdConfig CreateStatsdConfig() { 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 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { 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 attributions1 = {CreateAttribution(111, "App1"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(333, "App3")}; + + // GMS core node is the last one. + std::vector attributions2 = {CreateAttribution(111, "App1"), + CreateAttribution(333, "App3"), + CreateAttribution(222, "GMSCoreModule1")}; + + // GMS core node is the first one. + std::vector attributions3 = {CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(333, "App3")}; + + // Single GMS core node. + std::vector attributions4 = {CreateAttribution(222, "GMSCoreModule1")}; + + // GMS core has another uid. + std::vector attributions5 = {CreateAttribution(111, "App1"), + CreateAttribution(444, "GMSCoreModule2"), + CreateAttribution(333, "App3")}; + + // Multiple GMS core nodes. + std::vector attributions6 = {CreateAttribution(444, "GMSCoreModule2"), + CreateAttribution(222, "GMSCoreModule1")}; + + // No GMS core nodes. + std::vector attributions7 = {CreateAttribution(111, "App1"), + CreateAttribution(333, "App3")}; + std::vector attributions8 = {CreateAttribution(111, "App1")}; + + // GMS core node with isolated uid. + const int isolatedUid = 666; + std::vector attributions9 = { + CreateAttribution(isolatedUid, "GMSCoreModule1")}; + + std::vector> 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 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 2678c8a93463c..0f785dff8e812 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -500,12 +500,42 @@ void ValidateUidDimension(const DimensionsValue& value, int atomId, int uid) { .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 14eba1f67d9d1..1ac630ccff964 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -180,9 +180,12 @@ void sortLogEventsByTimestamp(std::vector> *events); 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){};