Support slicing by chain.
BUG: b/73975181 Test: statsd test Change-Id: I913ae0f68ff21ed0703bb5da9c60d3eaa3bf5981
This commit is contained in:
@@ -54,29 +54,13 @@ static void BM_FilterValue(benchmark::State& state) {
|
||||
translateFieldMatcher(field_matcher, &matchers);
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
vector<HashableDimensionKey> 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<Matcher> matchers;
|
||||
translateFieldMatcher(field_matcher, &matchers);
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
HashableDimensionKey output;
|
||||
filterValues(matchers, event.getValues(), &output);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BM_FilterValue2);
|
||||
|
||||
} // namespace statsd
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -61,125 +61,22 @@ android::hash_t hashDimension(const HashableDimensionKey& value) {
|
||||
|
||||
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,
|
||||
|
||||
@@ -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<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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<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;
|
||||
|
||||
@@ -137,21 +137,18 @@ void StateTracker::evaluateCondition(const LogEvent& event,
|
||||
|
||||
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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<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 @@ 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<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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -53,39 +53,17 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -31,6 +31,8 @@ enum Position {
|
||||
LAST = 2;
|
||||
|
||||
ANY = 3;
|
||||
|
||||
ALL = 4;
|
||||
}
|
||||
|
||||
enum TimeUnit {
|
||||
|
||||
@@ -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<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 @@ TEST(AtomMatcherTest, TestFilter) {
|
||||
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) {
|
||||
|
||||
@@ -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<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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -180,9 +180,12 @@ void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *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){};
|
||||
|
||||
Reference in New Issue
Block a user