Merge "Slice by state in ValueMetricProducer"
This commit is contained in:
committed by
Android (Google) Code Review
commit
7897c567fd
@@ -152,6 +152,10 @@ bool LessThan(const vector<FieldValue>& s1, const vector<FieldValue>& s2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HashableDimensionKey::operator!=(const HashableDimensionKey& that) const {
|
||||
return !((*this) == that);
|
||||
}
|
||||
|
||||
bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
|
||||
if (mValues.size() != that.getValues().size()) {
|
||||
return false;
|
||||
|
||||
@@ -71,6 +71,8 @@ public:
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
bool operator!=(const HashableDimensionKey& that) const;
|
||||
|
||||
bool operator==(const HashableDimensionKey& that) const;
|
||||
|
||||
bool operator<(const HashableDimensionKey& that) const;
|
||||
|
||||
@@ -284,6 +284,10 @@ private:
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
|
||||
|
||||
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
|
||||
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
|
||||
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
|
||||
};
|
||||
|
||||
} // namespace statsd
|
||||
|
||||
@@ -277,8 +277,8 @@ bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
|
||||
|
||||
void CountMetricProducer::onMatchedLogEventInternalLocked(
|
||||
const size_t matcherIndex, const MetricDimensionKey& eventKey,
|
||||
const ConditionKey& conditionKey, bool condition,
|
||||
const LogEvent& event) {
|
||||
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
|
||||
const map<int, HashableDimensionKey>& statePrimaryKeys) {
|
||||
int64_t eventTimeNs = event.GetElapsedTimestampNs();
|
||||
flushIfNeededLocked(eventTimeNs);
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ public:
|
||||
protected:
|
||||
void onMatchedLogEventInternalLocked(
|
||||
const size_t matcherIndex, const MetricDimensionKey& eventKey,
|
||||
const ConditionKey& conditionKey, bool condition,
|
||||
const LogEvent& event) override;
|
||||
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
|
||||
const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -541,8 +541,8 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey
|
||||
|
||||
void DurationMetricProducer::onMatchedLogEventInternalLocked(
|
||||
const size_t matcherIndex, const MetricDimensionKey& eventKey,
|
||||
const ConditionKey& conditionKeys, bool condition,
|
||||
const LogEvent& event) {
|
||||
const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
|
||||
const map<int, HashableDimensionKey>& statePrimaryKeys) {
|
||||
ALOGW("Not used in duration tracker.");
|
||||
}
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ protected:
|
||||
|
||||
void onMatchedLogEventInternalLocked(
|
||||
const size_t matcherIndex, const MetricDimensionKey& eventKey,
|
||||
const ConditionKey& conditionKeys, bool condition,
|
||||
const LogEvent& event) override;
|
||||
const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
|
||||
const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
|
||||
|
||||
private:
|
||||
void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys,
|
||||
|
||||
@@ -143,8 +143,8 @@ void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
|
||||
|
||||
void EventMetricProducer::onMatchedLogEventInternalLocked(
|
||||
const size_t matcherIndex, const MetricDimensionKey& eventKey,
|
||||
const ConditionKey& conditionKey, bool condition,
|
||||
const LogEvent& event) {
|
||||
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
|
||||
const map<int, HashableDimensionKey>& statePrimaryKeys) {
|
||||
if (!condition) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ public:
|
||||
private:
|
||||
void onMatchedLogEventInternalLocked(
|
||||
const size_t matcherIndex, const MetricDimensionKey& eventKey,
|
||||
const ConditionKey& conditionKey, bool condition,
|
||||
const LogEvent& event) override;
|
||||
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
|
||||
const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
|
||||
|
||||
void onDumpReportLocked(const int64_t dumpTimeNs,
|
||||
const bool include_current_partial_bucket,
|
||||
|
||||
@@ -449,8 +449,8 @@ bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
|
||||
|
||||
void GaugeMetricProducer::onMatchedLogEventInternalLocked(
|
||||
const size_t matcherIndex, const MetricDimensionKey& eventKey,
|
||||
const ConditionKey& conditionKey, bool condition,
|
||||
const LogEvent& event) {
|
||||
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
|
||||
const map<int, HashableDimensionKey>& statePrimaryKeys) {
|
||||
if (condition == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -95,8 +95,8 @@ public:
|
||||
protected:
|
||||
void onMatchedLogEventInternalLocked(
|
||||
const size_t matcherIndex, const MetricDimensionKey& eventKey,
|
||||
const ConditionKey& conditionKey, bool condition,
|
||||
const LogEvent& event) override;
|
||||
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
|
||||
const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
|
||||
|
||||
private:
|
||||
void onDumpReportLocked(const int64_t dumpTimeNs,
|
||||
|
||||
@@ -133,8 +133,8 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo
|
||||
HashableDimensionKey dimensionInWhat;
|
||||
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
|
||||
MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey);
|
||||
onMatchedLogEventInternalLocked(
|
||||
matcherIndex, metricKey, conditionKey, condition, event);
|
||||
onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event,
|
||||
statePrimaryKeys);
|
||||
}
|
||||
|
||||
bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
|
||||
@@ -269,6 +269,7 @@ void MetricProducer::getMappedStateValue(const int32_t atomId, const HashableDim
|
||||
FieldValue* value) {
|
||||
if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
|
||||
value->mValue = Value(StateTracker::kStateUnknown);
|
||||
value->mField.setTag(atomId);
|
||||
ALOGW("StateTracker not found for state atom %d", atomId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -330,8 +330,8 @@ protected:
|
||||
*/
|
||||
virtual void onMatchedLogEventInternalLocked(
|
||||
const size_t matcherIndex, const MetricDimensionKey& eventKey,
|
||||
const ConditionKey& conditionKey, bool condition,
|
||||
const LogEvent& event) = 0;
|
||||
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
|
||||
const map<int, HashableDimensionKey>& statePrimaryKeys) = 0;
|
||||
|
||||
// Consume the parsed stats log entry that already matched the "what" of the metric.
|
||||
virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
|
||||
@@ -475,6 +475,10 @@ protected:
|
||||
FRIEND_TEST(StatsLogProcessorTest,
|
||||
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
|
||||
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
|
||||
|
||||
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
|
||||
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
|
||||
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
|
||||
};
|
||||
|
||||
} // namespace statsd
|
||||
|
||||
@@ -289,6 +289,10 @@ private:
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
|
||||
|
||||
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
|
||||
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
|
||||
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
|
||||
};
|
||||
|
||||
} // namespace statsd
|
||||
|
||||
@@ -66,6 +66,7 @@ const int FIELD_ID_DROP_TIME = 2;
|
||||
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
|
||||
const int FIELD_ID_BUCKET_INFO = 3;
|
||||
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
|
||||
const int FIELD_ID_SLICE_BY_STATE = 6;
|
||||
// for ValueBucketInfo
|
||||
const int FIELD_ID_VALUE_INDEX = 1;
|
||||
const int FIELD_ID_VALUE_LONG = 2;
|
||||
@@ -146,6 +147,14 @@ ValueMetricProducer::ValueMetricProducer(
|
||||
mConditionSliced = true;
|
||||
}
|
||||
|
||||
for (const auto& stateLink : metric.state_link()) {
|
||||
Metric2State ms;
|
||||
ms.stateAtomId = stateLink.state_atom_id();
|
||||
translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
|
||||
translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
|
||||
mMetric2StateLinks.push_back(ms);
|
||||
}
|
||||
|
||||
int64_t numBucketsForward = calcBucketsForwardCount(startTimeNs);
|
||||
mCurrentBucketNum += numBucketsForward;
|
||||
|
||||
@@ -181,6 +190,33 @@ ValueMetricProducer::~ValueMetricProducer() {
|
||||
}
|
||||
}
|
||||
|
||||
void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId,
|
||||
const HashableDimensionKey& primaryKey, int oldState,
|
||||
int newState) {
|
||||
VLOG("ValueMetric %lld onStateChanged time %lld, State %d, key %s, %d -> %d",
|
||||
(long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(),
|
||||
oldState, newState);
|
||||
// If condition is not true, we do not need to pull for this state change.
|
||||
if (mCondition != ConditionState::kTrue) {
|
||||
return;
|
||||
}
|
||||
bool isEventLate = eventTimeNs < mCurrentBucketStartTimeNs;
|
||||
if (isEventLate) {
|
||||
VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
|
||||
(long long)mCurrentBucketStartTimeNs);
|
||||
invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
|
||||
return;
|
||||
}
|
||||
mStateChangePrimaryKey.first = atomId;
|
||||
mStateChangePrimaryKey.second = primaryKey;
|
||||
if (mIsPulled) {
|
||||
pullAndMatchEventsLocked(eventTimeNs);
|
||||
}
|
||||
mStateChangePrimaryKey.first = 0;
|
||||
mStateChangePrimaryKey.second = DEFAULT_DIMENSION_KEY;
|
||||
flushIfNeededLocked(eventTimeNs);
|
||||
}
|
||||
|
||||
void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
|
||||
const int64_t eventTime) {
|
||||
VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
|
||||
@@ -281,6 +317,14 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
|
||||
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
|
||||
}
|
||||
|
||||
// Then fill slice_by_state.
|
||||
for (auto state : dimensionKey.getStateValuesKey().getValues()) {
|
||||
uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
|
||||
FIELD_ID_SLICE_BY_STATE);
|
||||
writeStateToProto(state, protoOutput);
|
||||
protoOutput->end(stateToken);
|
||||
}
|
||||
|
||||
// Then fill bucket_info (ValueBucketInfo).
|
||||
for (const auto& bucket : pair.second) {
|
||||
uint64_t bucketInfoToken = protoOutput->start(
|
||||
@@ -300,7 +344,7 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
|
||||
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
|
||||
(long long)bucket.mConditionTrueNs);
|
||||
}
|
||||
for (int i = 0; i < (int)bucket.valueIndex.size(); i ++) {
|
||||
for (int i = 0; i < (int)bucket.valueIndex.size(); i++) {
|
||||
int index = bucket.valueIndex[i];
|
||||
const Value& value = bucket.values[i];
|
||||
uint64_t valueToken = protoOutput->start(
|
||||
@@ -358,9 +402,10 @@ void ValueMetricProducer::invalidateCurrentBucket(const int64_t dropTimeNs,
|
||||
}
|
||||
|
||||
void ValueMetricProducer::resetBase() {
|
||||
for (auto& slice : mCurrentSlicedBucket) {
|
||||
for (auto& interval : slice.second) {
|
||||
interval.hasBase = false;
|
||||
for (auto& slice : mCurrentBaseInfo) {
|
||||
for (auto& baseInfo : slice.second) {
|
||||
baseInfo.hasBase = false;
|
||||
baseInfo.hasCurrentState = false;
|
||||
}
|
||||
}
|
||||
mHasGlobalBase = false;
|
||||
@@ -558,14 +603,20 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log
|
||||
onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
|
||||
}
|
||||
}
|
||||
// If the new pulled data does not contains some keys we track in our intervals, we need to
|
||||
// reset the base.
|
||||
// If a key that is:
|
||||
// 1. Tracked in mCurrentSlicedBucket and
|
||||
// 2. A superset of the current mStateChangePrimaryKey
|
||||
// was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys)
|
||||
// then we need to reset the base.
|
||||
for (auto& slice : mCurrentSlicedBucket) {
|
||||
bool presentInPulledData = mMatchedMetricDimensionKeys.find(slice.first)
|
||||
!= mMatchedMetricDimensionKeys.end();
|
||||
if (!presentInPulledData) {
|
||||
for (auto& interval : slice.second) {
|
||||
interval.hasBase = false;
|
||||
const auto& whatKey = slice.first.getDimensionKeyInWhat();
|
||||
bool presentInPulledData =
|
||||
mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end();
|
||||
if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) {
|
||||
auto it = mCurrentBaseInfo.find(whatKey);
|
||||
for (auto& baseInfo : it->second) {
|
||||
baseInfo.hasBase = false;
|
||||
baseInfo.hasCurrentState = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -674,17 +725,30 @@ bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret)
|
||||
return false;
|
||||
}
|
||||
|
||||
void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIndex,
|
||||
const MetricDimensionKey& eventKey,
|
||||
const ConditionKey& conditionKey,
|
||||
bool condition, const LogEvent& event) {
|
||||
void ValueMetricProducer::onMatchedLogEventInternalLocked(
|
||||
const size_t matcherIndex, const MetricDimensionKey& eventKey,
|
||||
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
|
||||
const map<int, HashableDimensionKey>& statePrimaryKeys) {
|
||||
auto whatKey = eventKey.getDimensionKeyInWhat();
|
||||
auto stateKey = eventKey.getStateValuesKey();
|
||||
|
||||
// Skip this event if a state changed occurred for a different primary key.
|
||||
auto it = statePrimaryKeys.find(mStateChangePrimaryKey.first);
|
||||
// Check that both the atom id and the primary key are equal.
|
||||
if (it != statePrimaryKeys.end() && it->second != mStateChangePrimaryKey.second) {
|
||||
VLOG("ValueMetric skip event with primary key %s because state change primary key "
|
||||
"is %s",
|
||||
it->second.toString().c_str(), mStateChangePrimaryKey.second.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t eventTimeNs = event.GetElapsedTimestampNs();
|
||||
if (eventTimeNs < mCurrentBucketStartTimeNs) {
|
||||
VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
|
||||
(long long)mCurrentBucketStartTimeNs);
|
||||
return;
|
||||
}
|
||||
mMatchedMetricDimensionKeys.insert(eventKey);
|
||||
mMatchedMetricDimensionKeys.insert(whatKey);
|
||||
|
||||
if (!mIsPulled) {
|
||||
// We cannot flush without doing a pull first.
|
||||
@@ -709,10 +773,26 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn
|
||||
if (hitGuardRailLocked(eventKey)) {
|
||||
return;
|
||||
}
|
||||
vector<Interval>& multiIntervals = mCurrentSlicedBucket[eventKey];
|
||||
if (multiIntervals.size() < mFieldMatchers.size()) {
|
||||
vector<BaseInfo>& baseInfos = mCurrentBaseInfo[whatKey];
|
||||
if (baseInfos.size() < mFieldMatchers.size()) {
|
||||
VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
|
||||
multiIntervals.resize(mFieldMatchers.size());
|
||||
baseInfos.resize(mFieldMatchers.size());
|
||||
}
|
||||
|
||||
for (auto baseInfo : baseInfos) {
|
||||
if (!baseInfo.hasCurrentState) {
|
||||
baseInfo.currentState = DEFAULT_DIMENSION_KEY;
|
||||
baseInfo.hasCurrentState = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We need to get the intervals stored with the previous state key so we can
|
||||
// close these value intervals.
|
||||
const auto oldStateKey = baseInfos[0].currentState;
|
||||
vector<Interval>& intervals = mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)];
|
||||
if (intervals.size() < mFieldMatchers.size()) {
|
||||
VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
|
||||
intervals.resize(mFieldMatchers.size());
|
||||
}
|
||||
|
||||
// We only use anomaly detection under certain cases.
|
||||
@@ -725,7 +805,8 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn
|
||||
|
||||
for (int i = 0; i < (int)mFieldMatchers.size(); i++) {
|
||||
const Matcher& matcher = mFieldMatchers[i];
|
||||
Interval& interval = multiIntervals[i];
|
||||
BaseInfo& baseInfo = baseInfos[i];
|
||||
Interval& interval = intervals[i];
|
||||
interval.valueIndex = i;
|
||||
Value value;
|
||||
if (!getDoubleOrLong(event, matcher, value)) {
|
||||
@@ -736,60 +817,61 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn
|
||||
interval.seenNewData = true;
|
||||
|
||||
if (mUseDiff) {
|
||||
if (!interval.hasBase) {
|
||||
if (!baseInfo.hasBase) {
|
||||
if (mHasGlobalBase && mUseZeroDefaultBase) {
|
||||
// The bucket has global base. This key does not.
|
||||
// Optionally use zero as base.
|
||||
interval.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE);
|
||||
interval.hasBase = true;
|
||||
baseInfo.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE);
|
||||
baseInfo.hasBase = true;
|
||||
} else {
|
||||
// no base. just update base and return.
|
||||
interval.base = value;
|
||||
interval.hasBase = true;
|
||||
baseInfo.base = value;
|
||||
baseInfo.hasBase = true;
|
||||
// If we're missing a base, do not use anomaly detection on incomplete data
|
||||
useAnomalyDetection = false;
|
||||
// Continue (instead of return) here in order to set interval.base and
|
||||
// interval.hasBase for other intervals
|
||||
// Continue (instead of return) here in order to set baseInfo.base and
|
||||
// baseInfo.hasBase for other baseInfos
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Value diff;
|
||||
switch (mValueDirection) {
|
||||
case ValueMetric::INCREASING:
|
||||
if (value >= interval.base) {
|
||||
diff = value - interval.base;
|
||||
if (value >= baseInfo.base) {
|
||||
diff = value - baseInfo.base;
|
||||
} else if (mUseAbsoluteValueOnReset) {
|
||||
diff = value;
|
||||
} else {
|
||||
VLOG("Unexpected decreasing value");
|
||||
StatsdStats::getInstance().notePullDataError(mPullTagId);
|
||||
interval.base = value;
|
||||
baseInfo.base = value;
|
||||
// If we've got bad data, do not use anomaly detection
|
||||
useAnomalyDetection = false;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case ValueMetric::DECREASING:
|
||||
if (interval.base >= value) {
|
||||
diff = interval.base - value;
|
||||
if (baseInfo.base >= value) {
|
||||
diff = baseInfo.base - value;
|
||||
} else if (mUseAbsoluteValueOnReset) {
|
||||
diff = value;
|
||||
} else {
|
||||
VLOG("Unexpected increasing value");
|
||||
StatsdStats::getInstance().notePullDataError(mPullTagId);
|
||||
interval.base = value;
|
||||
baseInfo.base = value;
|
||||
// If we've got bad data, do not use anomaly detection
|
||||
useAnomalyDetection = false;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case ValueMetric::ANY:
|
||||
diff = value - interval.base;
|
||||
diff = value - baseInfo.base;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
interval.base = value;
|
||||
baseInfo.base = value;
|
||||
value = diff;
|
||||
}
|
||||
|
||||
@@ -814,12 +896,13 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn
|
||||
interval.hasValue = true;
|
||||
}
|
||||
interval.sampleSize += 1;
|
||||
baseInfo.currentState = stateKey;
|
||||
}
|
||||
|
||||
// Only trigger the tracker if all intervals are correct
|
||||
if (useAnomalyDetection) {
|
||||
// TODO: propgate proper values down stream when anomaly support doubles
|
||||
long wholeBucketVal = multiIntervals[0].value.long_value;
|
||||
long wholeBucketVal = intervals[0].value.long_value;
|
||||
auto prev = mCurrentFullBucket.find(eventKey);
|
||||
if (prev != mCurrentFullBucket.end()) {
|
||||
wholeBucketVal += prev->second;
|
||||
@@ -953,6 +1036,7 @@ void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs)
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
// TODO: remove mCurrentBaseInfo entries when obsolete
|
||||
}
|
||||
|
||||
mCurrentBucketIsInvalid = false;
|
||||
|
||||
@@ -83,11 +83,14 @@ public:
|
||||
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
|
||||
};
|
||||
|
||||
void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey,
|
||||
int oldState, int newState) override;
|
||||
|
||||
protected:
|
||||
void onMatchedLogEventInternalLocked(
|
||||
const size_t matcherIndex, const MetricDimensionKey& eventKey,
|
||||
const ConditionKey& conditionKey, bool condition,
|
||||
const LogEvent& event) override;
|
||||
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
|
||||
const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
|
||||
|
||||
private:
|
||||
void onDumpReportLocked(const int64_t dumpTimeNs,
|
||||
@@ -144,7 +147,10 @@ private:
|
||||
std::vector<Matcher> mFieldMatchers;
|
||||
|
||||
// Value fields for matching.
|
||||
std::set<MetricDimensionKey> mMatchedMetricDimensionKeys;
|
||||
std::set<HashableDimensionKey> mMatchedMetricDimensionKeys;
|
||||
|
||||
// Holds the atom id, primary key pair from a state change.
|
||||
pair<int32_t, HashableDimensionKey> mStateChangePrimaryKey;
|
||||
|
||||
// tagId for pulled data. -1 if this is not pulled
|
||||
const int mPullTagId;
|
||||
@@ -156,10 +162,6 @@ private:
|
||||
typedef struct {
|
||||
// Index in multi value aggregation.
|
||||
int valueIndex;
|
||||
// Holds current base value of the dimension. Take diff and update if necessary.
|
||||
Value base;
|
||||
// Whether there is a base to diff to.
|
||||
bool hasBase;
|
||||
// Current value, depending on the aggregation type.
|
||||
Value value;
|
||||
// Number of samples collected.
|
||||
@@ -171,8 +173,21 @@ private:
|
||||
bool seenNewData = false;
|
||||
} Interval;
|
||||
|
||||
typedef struct {
|
||||
// Holds current base value of the dimension. Take diff and update if necessary.
|
||||
Value base;
|
||||
// Whether there is a base to diff to.
|
||||
bool hasBase;
|
||||
// Last seen state value(s).
|
||||
HashableDimensionKey currentState;
|
||||
// Whether this dimensions in what key has a current state key.
|
||||
bool hasCurrentState;
|
||||
} BaseInfo;
|
||||
|
||||
std::unordered_map<MetricDimensionKey, std::vector<Interval>> mCurrentSlicedBucket;
|
||||
|
||||
std::unordered_map<HashableDimensionKey, std::vector<BaseInfo>> mCurrentBaseInfo;
|
||||
|
||||
std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
|
||||
|
||||
// Save the past buckets and we can clear when the StatsLogReport is dumped.
|
||||
@@ -285,6 +300,9 @@ private:
|
||||
FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
|
||||
FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
|
||||
FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue);
|
||||
FRIEND_TEST(ValueMetricProducerTest, TestSlicedState);
|
||||
FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMap);
|
||||
FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions);
|
||||
FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
|
||||
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
|
||||
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
|
||||
|
||||
@@ -18,11 +18,12 @@
|
||||
#include "Log.h"
|
||||
|
||||
#include "metrics_manager_util.h"
|
||||
#include "MetricProducer.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "atoms_info.h"
|
||||
#include "FieldValue.h"
|
||||
#include "MetricProducer.h"
|
||||
#include "condition/CombinationConditionTracker.h"
|
||||
#include "condition/SimpleConditionTracker.h"
|
||||
#include "condition/StateConditionTracker.h"
|
||||
@@ -173,6 +174,14 @@ bool handleMetricWithStates(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handleMetricWithStateLink(const FieldMatcher& stateMatcher,
|
||||
const vector<Matcher>& dimensionsInWhat) {
|
||||
vector<Matcher> stateMatchers;
|
||||
translateFieldMatcher(stateMatcher, &stateMatchers);
|
||||
|
||||
return subsetDimensions(stateMatchers, dimensionsInWhat);
|
||||
}
|
||||
|
||||
// Validates a metricActivation and populates state.
|
||||
// EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
|
||||
// to provide the producer with state about its activators and deactivators.
|
||||
@@ -669,18 +678,41 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> slicedStateAtoms;
|
||||
unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
|
||||
if (metric.slice_by_state_size() > 0) {
|
||||
if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
|
||||
allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (metric.state_link_size() > 0) {
|
||||
ALOGW("ValueMetric has a MetricStateLink but doesn't have a sliced state");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all metric state links are a subset of dimensions_in_what fields.
|
||||
std::vector<Matcher> dimensionsInWhat;
|
||||
translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
|
||||
for (const auto& stateLink : metric.state_link()) {
|
||||
if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unordered_map<int, shared_ptr<Activation>> eventActivationMap;
|
||||
unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
|
||||
bool success = handleMetricActivation(config, metric.id(), metricIndex,
|
||||
metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
|
||||
deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
|
||||
eventDeactivationMap);
|
||||
bool success = handleMetricActivation(
|
||||
config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap,
|
||||
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
|
||||
metricsWithActivation, eventActivationMap, eventDeactivationMap);
|
||||
if (!success) return false;
|
||||
|
||||
sp<MetricProducer> valueProducer = new ValueMetricProducer(
|
||||
key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId,
|
||||
timeBaseTimeNs, currentTimeNs, pullerManager, eventActivationMap,
|
||||
eventDeactivationMap);
|
||||
eventDeactivationMap, slicedStateAtoms, stateGroupMap);
|
||||
allMetricProducers.push_back(valueProducer);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,13 +28,17 @@ StateManager& StateManager::getInstance() {
|
||||
return sStateManager;
|
||||
}
|
||||
|
||||
void StateManager::clear() {
|
||||
mStateTrackers.clear();
|
||||
}
|
||||
|
||||
void StateManager::onLogEvent(const LogEvent& event) {
|
||||
if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) {
|
||||
mStateTrackers[event.GetTagId()]->onLogEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
bool StateManager::registerListener(int32_t atomId, wp<StateListener> listener) {
|
||||
bool StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
|
||||
// Check if state tracker already exists.
|
||||
if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
|
||||
// Create a new state tracker iff atom is a state atom.
|
||||
@@ -50,7 +54,7 @@ bool StateManager::registerListener(int32_t atomId, wp<StateListener> listener)
|
||||
return true;
|
||||
}
|
||||
|
||||
void StateManager::unregisterListener(int32_t atomId, wp<StateListener> listener) {
|
||||
void StateManager::unregisterListener(const int32_t atomId, wp<StateListener> listener) {
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
|
||||
// Hold the sp<> until the lock is released so that ~StateTracker() is
|
||||
@@ -74,7 +78,7 @@ void StateManager::unregisterListener(int32_t atomId, wp<StateListener> listener
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
bool StateManager::getStateValue(int32_t atomId, const HashableDimensionKey& key,
|
||||
bool StateManager::getStateValue(const int32_t atomId, const HashableDimensionKey& key,
|
||||
FieldValue* output) const {
|
||||
auto it = mStateTrackers.find(atomId);
|
||||
if (it != mStateTrackers.end()) {
|
||||
|
||||
@@ -40,30 +40,33 @@ public:
|
||||
// Returns a pointer to the single, shared StateManager object.
|
||||
static StateManager& getInstance();
|
||||
|
||||
// Unregisters all listeners and removes all trackers from StateManager.
|
||||
void clear();
|
||||
|
||||
// Notifies the correct StateTracker of an event.
|
||||
void onLogEvent(const LogEvent& event);
|
||||
|
||||
// Returns true if atomId is being tracked and is associated with a state
|
||||
// atom. StateManager notifies the correct StateTracker to register listener.
|
||||
// If the correct StateTracker does not exist, a new StateTracker is created.
|
||||
bool registerListener(int32_t atomId, wp<StateListener> listener);
|
||||
bool registerListener(const int32_t atomId, wp<StateListener> listener);
|
||||
|
||||
// Notifies the correct StateTracker to unregister a listener
|
||||
// and removes the tracker if it no longer has any listeners.
|
||||
void unregisterListener(int32_t atomId, wp<StateListener> listener);
|
||||
void unregisterListener(const int32_t atomId, wp<StateListener> listener);
|
||||
|
||||
// Returns true if the StateTracker exists and queries for the
|
||||
// original state value mapped to the given query key. The state value is
|
||||
// stored and output in a FieldValue class.
|
||||
// Returns false if the StateTracker doesn't exist.
|
||||
bool getStateValue(int32_t atomId, const HashableDimensionKey& queryKey,
|
||||
bool getStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
|
||||
FieldValue* output) const;
|
||||
|
||||
inline int getStateTrackersCount() const {
|
||||
return mStateTrackers.size();
|
||||
}
|
||||
|
||||
inline int getListenersCount(int32_t atomId) const {
|
||||
inline int getListenersCount(const int32_t atomId) const {
|
||||
auto it = mStateTrackers.find(atomId);
|
||||
if (it != mStateTrackers.end()) {
|
||||
return it->second->getListenersCount();
|
||||
|
||||
@@ -147,12 +147,14 @@ message ValueBucketInfo {
|
||||
message ValueMetricData {
|
||||
optional DimensionsValue dimensions_in_what = 1;
|
||||
|
||||
optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
|
||||
repeated StateValue slice_by_state = 6;
|
||||
|
||||
repeated ValueBucketInfo bucket_info = 3;
|
||||
|
||||
repeated DimensionsValue dimension_leaf_values_in_what = 4;
|
||||
|
||||
optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
|
||||
|
||||
repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
|
||||
}
|
||||
|
||||
|
||||
@@ -290,12 +290,14 @@ message ValueMetric {
|
||||
|
||||
optional FieldMatcher dimensions_in_what = 5;
|
||||
|
||||
optional FieldMatcher dimensions_in_condition = 9 [deprecated = true];
|
||||
repeated int64 slice_by_state = 18;
|
||||
|
||||
optional TimeUnit bucket = 6;
|
||||
|
||||
repeated MetricConditionLink links = 7;
|
||||
|
||||
repeated MetricStateLink state_link = 19;
|
||||
|
||||
enum AggregationType {
|
||||
SUM = 1;
|
||||
MIN = 2;
|
||||
@@ -325,6 +327,8 @@ message ValueMetric {
|
||||
optional int32 max_pull_delay_sec = 16 [default = 10];
|
||||
|
||||
optional bool split_bucket_for_app_upgrade = 17 [default = true];
|
||||
|
||||
optional FieldMatcher dimensions_in_condition = 9 [deprecated = true];
|
||||
}
|
||||
|
||||
message Alert {
|
||||
|
||||
@@ -27,9 +27,6 @@ namespace statsd {
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
const int SCREEN_STATE_ATOM_ID = android::util::SCREEN_STATE_CHANGED;
|
||||
const int UID_PROCESS_STATE_ATOM_ID = android::util::UID_PROCESS_STATE_CHANGED;
|
||||
|
||||
/**
|
||||
* Test a count metric that has one slice_by_state with no primary fields.
|
||||
*
|
||||
|
||||
@@ -369,6 +369,168 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) {
|
||||
EXPECT_EQ(1, bucketInfo.values_size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test initialization of a simple value metric that is sliced by a state.
|
||||
*
|
||||
* ValueCpuUserTimePerScreenState
|
||||
*/
|
||||
TEST(ValueMetricE2eTest, TestInitWithSlicedState) {
|
||||
// Create config.
|
||||
StatsdConfig config;
|
||||
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
|
||||
|
||||
auto pulledAtomMatcher =
|
||||
CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
|
||||
*config.add_atom_matcher() = pulledAtomMatcher;
|
||||
|
||||
auto screenState = CreateScreenState();
|
||||
*config.add_state() = screenState;
|
||||
|
||||
// Create value metric that slices by screen state without a map.
|
||||
int64_t metricId = 123456;
|
||||
auto valueMetric = config.add_value_metric();
|
||||
valueMetric->set_id(metricId);
|
||||
valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
|
||||
valueMetric->set_what(pulledAtomMatcher.id());
|
||||
*valueMetric->mutable_value_field() =
|
||||
CreateDimensions(android::util::CPU_TIME_PER_UID, {2 /* user_time_micros */});
|
||||
valueMetric->add_slice_by_state(screenState.id());
|
||||
valueMetric->set_max_pull_delay_sec(INT_MAX);
|
||||
|
||||
// Initialize StatsLogProcessor.
|
||||
const uint64_t bucketStartTimeNs = 10000000000; // 0:10
|
||||
const uint64_t bucketSizeNs =
|
||||
TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000LL;
|
||||
int uid = 12345;
|
||||
int64_t cfgId = 98765;
|
||||
ConfigKey cfgKey(uid, cfgId);
|
||||
|
||||
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
|
||||
|
||||
// Check that StateTrackers were initialized correctly.
|
||||
EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
|
||||
EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
|
||||
|
||||
// Check that ValueMetricProducer was initialized correctly.
|
||||
EXPECT_EQ(1U, processor->mMetricsManagers.size());
|
||||
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
|
||||
EXPECT_TRUE(metricsManager->isConfigValid());
|
||||
EXPECT_EQ(1, metricsManager->mAllMetricProducers.size());
|
||||
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
|
||||
EXPECT_EQ(1, metricProducer->mSlicedStateAtoms.size());
|
||||
EXPECT_EQ(SCREEN_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0));
|
||||
EXPECT_EQ(0, metricProducer->mStateGroupMap.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test initialization of a value metric that is sliced by state and has
|
||||
* dimensions_in_what.
|
||||
*
|
||||
* ValueCpuUserTimePerUidPerUidProcessState
|
||||
*/
|
||||
TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions) {
|
||||
// Create config.
|
||||
StatsdConfig config;
|
||||
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
|
||||
|
||||
auto cpuTimePerUidMatcher =
|
||||
CreateSimpleAtomMatcher("CpuTimePerUidMatcher", android::util::CPU_TIME_PER_UID);
|
||||
*config.add_atom_matcher() = cpuTimePerUidMatcher;
|
||||
|
||||
auto uidProcessState = CreateUidProcessState();
|
||||
*config.add_state() = uidProcessState;
|
||||
|
||||
// Create value metric that slices by screen state with a complete map.
|
||||
int64_t metricId = 123456;
|
||||
auto valueMetric = config.add_value_metric();
|
||||
valueMetric->set_id(metricId);
|
||||
valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
|
||||
valueMetric->set_what(cpuTimePerUidMatcher.id());
|
||||
*valueMetric->mutable_value_field() =
|
||||
CreateDimensions(android::util::CPU_TIME_PER_UID, {2 /* user_time_micros */});
|
||||
*valueMetric->mutable_dimensions_in_what() =
|
||||
CreateDimensions(android::util::CPU_TIME_PER_UID, {1 /* uid */});
|
||||
valueMetric->add_slice_by_state(uidProcessState.id());
|
||||
MetricStateLink* stateLink = valueMetric->add_state_link();
|
||||
stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
|
||||
auto fieldsInWhat = stateLink->mutable_fields_in_what();
|
||||
*fieldsInWhat = CreateDimensions(android::util::CPU_TIME_PER_UID, {1 /* uid */});
|
||||
auto fieldsInState = stateLink->mutable_fields_in_state();
|
||||
*fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
|
||||
valueMetric->set_max_pull_delay_sec(INT_MAX);
|
||||
|
||||
// Initialize StatsLogProcessor.
|
||||
const uint64_t bucketStartTimeNs = 10000000000; // 0:10
|
||||
int uid = 12345;
|
||||
int64_t cfgId = 98765;
|
||||
ConfigKey cfgKey(uid, cfgId);
|
||||
|
||||
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
|
||||
|
||||
// Check that StateTrackers were initialized correctly.
|
||||
EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
|
||||
EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
|
||||
|
||||
// Check that ValueMetricProducer was initialized correctly.
|
||||
EXPECT_EQ(1U, processor->mMetricsManagers.size());
|
||||
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
|
||||
EXPECT_TRUE(metricsManager->isConfigValid());
|
||||
EXPECT_EQ(1, metricsManager->mAllMetricProducers.size());
|
||||
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
|
||||
EXPECT_EQ(1, metricProducer->mSlicedStateAtoms.size());
|
||||
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0));
|
||||
EXPECT_EQ(0, metricProducer->mStateGroupMap.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test initialization of a value metric that is sliced by state and has
|
||||
* dimensions_in_what.
|
||||
*
|
||||
* ValueCpuUserTimePerUidPerUidProcessState
|
||||
*/
|
||||
TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions) {
|
||||
// Create config.
|
||||
StatsdConfig config;
|
||||
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
|
||||
|
||||
auto cpuTimePerUidMatcher =
|
||||
CreateSimpleAtomMatcher("CpuTimePerUidMatcher", android::util::CPU_TIME_PER_UID);
|
||||
*config.add_atom_matcher() = cpuTimePerUidMatcher;
|
||||
|
||||
auto uidProcessState = CreateUidProcessState();
|
||||
*config.add_state() = uidProcessState;
|
||||
|
||||
// Create value metric that slices by screen state with a complete map.
|
||||
int64_t metricId = 123456;
|
||||
auto valueMetric = config.add_value_metric();
|
||||
valueMetric->set_id(metricId);
|
||||
valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
|
||||
valueMetric->set_what(cpuTimePerUidMatcher.id());
|
||||
*valueMetric->mutable_value_field() =
|
||||
CreateDimensions(android::util::CPU_TIME_PER_UID, {2 /* user_time_micros */});
|
||||
valueMetric->add_slice_by_state(uidProcessState.id());
|
||||
MetricStateLink* stateLink = valueMetric->add_state_link();
|
||||
stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
|
||||
auto fieldsInWhat = stateLink->mutable_fields_in_what();
|
||||
*fieldsInWhat = CreateDimensions(android::util::CPU_TIME_PER_UID, {1 /* uid */});
|
||||
auto fieldsInState = stateLink->mutable_fields_in_state();
|
||||
*fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
|
||||
valueMetric->set_max_pull_delay_sec(INT_MAX);
|
||||
|
||||
// Initialize StatsLogProcessor.
|
||||
const uint64_t bucketStartTimeNs = 10000000000; // 0:10
|
||||
int uid = 12345;
|
||||
int64_t cfgId = 98765;
|
||||
ConfigKey cfgKey(uid, cfgId);
|
||||
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
|
||||
|
||||
// No StateTrackers are initialized.
|
||||
EXPECT_EQ(0, StateManager::getInstance().getStateTrackersCount());
|
||||
|
||||
// Config initialization fails.
|
||||
EXPECT_EQ(0, processor->mMetricsManagers.size());
|
||||
}
|
||||
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -146,6 +146,7 @@ TEST(StateListenerTest, TestStateListenerWeakPointer) {
|
||||
TEST(StateManagerTest, TestStateManagerGetInstance) {
|
||||
sp<TestStateListener> listener1 = new TestStateListener();
|
||||
StateManager& mgr = StateManager::getInstance();
|
||||
mgr.clear();
|
||||
|
||||
mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
|
||||
EXPECT_EQ(1, mgr.getStateTrackersCount());
|
||||
|
||||
@@ -30,6 +30,9 @@ namespace statsd {
|
||||
using android::util::ProtoReader;
|
||||
using google::protobuf::RepeatedPtrField;
|
||||
|
||||
const int SCREEN_STATE_ATOM_ID = android::util::SCREEN_STATE_CHANGED;
|
||||
const int UID_PROCESS_STATE_ATOM_ID = android::util::UID_PROCESS_STATE_CHANGED;
|
||||
|
||||
// Converts a ProtoOutputStream to a StatsLogReport proto.
|
||||
StatsLogReport outputStreamToProto(ProtoOutputStream* proto);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user