Merge "Slice by state in DurationMetricProducer" into rvc-dev am: 8ad7c0ec42
Change-Id: I7c77e56f533b42dbffb4a55138523d2340a19554
This commit is contained in:
@@ -337,6 +337,7 @@ cc_test {
|
||||
"tests/external/StatsPullerManager_test.cpp",
|
||||
"tests/FieldValue_test.cpp",
|
||||
"tests/guardrail/StatsdStats_test.cpp",
|
||||
"tests/HashableDimensionKey_test.cpp",
|
||||
"tests/indexed_priority_queue_test.cpp",
|
||||
"tests/log_event/LogEventQueue_test.cpp",
|
||||
"tests/LogEntryMatcher_test.cpp",
|
||||
|
||||
@@ -230,6 +230,47 @@ void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metr
|
||||
}
|
||||
}
|
||||
|
||||
bool containsLinkedStateValues(const HashableDimensionKey& whatKey,
|
||||
const HashableDimensionKey& primaryKey,
|
||||
const vector<Metric2State>& stateLinks, const int32_t stateAtomId) {
|
||||
if (whatKey.getValues().size() < primaryKey.getValues().size()) {
|
||||
ALOGE("Contains linked values false: whatKey is too small");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& primaryValue : primaryKey.getValues()) {
|
||||
bool found = false;
|
||||
for (const auto& whatValue : whatKey.getValues()) {
|
||||
if (linked(stateLinks, stateAtomId, primaryValue.mField, whatValue.mField) &&
|
||||
primaryValue.mValue == whatValue.mValue) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool linked(const vector<Metric2State>& stateLinks, const int32_t stateAtomId,
|
||||
const Field& stateField, const Field& metricField) {
|
||||
for (auto stateLink : stateLinks) {
|
||||
if (stateLink.stateAtomId != stateAtomId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < stateLink.stateFields.size(); i++) {
|
||||
if (stateLink.stateFields[i].mMatcher == stateField &&
|
||||
stateLink.metricFields[i].mMatcher == metricField) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LessThan(const vector<FieldValue>& s1, const vector<FieldValue>& s2) {
|
||||
if (s1.size() != s2.size()) {
|
||||
return s1.size() < s2.size();
|
||||
|
||||
@@ -110,6 +110,10 @@ public:
|
||||
return mStateValuesKey;
|
||||
}
|
||||
|
||||
inline HashableDimensionKey* getMutableStateValuesKey() {
|
||||
return &mStateValuesKey;
|
||||
}
|
||||
|
||||
inline void setStateValuesKey(const HashableDimensionKey& key) {
|
||||
mStateValuesKey = key;
|
||||
}
|
||||
@@ -169,6 +173,32 @@ void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
|
||||
void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metric2State& link,
|
||||
HashableDimensionKey* statePrimaryKey);
|
||||
|
||||
/**
|
||||
* Returns true if the primaryKey values are a subset of the whatKey values.
|
||||
* The values from the primaryKey come from the state atom, so we need to
|
||||
* check that a link exists between the state atom field and what atom field.
|
||||
*
|
||||
* Example:
|
||||
* whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}]
|
||||
* statePrimaryKey = [Atom: 27, {uid: 1005}]
|
||||
* Returns true IF one of the Metric2State links Atom 10's uid to Atom 27's uid
|
||||
*
|
||||
* Example:
|
||||
* whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}]
|
||||
* statePrimaryKey = [Atom: 59, {uid: 1005, package_name: "system"}]
|
||||
* Returns false
|
||||
*/
|
||||
bool containsLinkedStateValues(const HashableDimensionKey& whatKey,
|
||||
const HashableDimensionKey& primaryKey,
|
||||
const std::vector<Metric2State>& stateLinks,
|
||||
const int32_t stateAtomId);
|
||||
|
||||
/**
|
||||
* Returns true if there is a Metric2State link that links the stateField and
|
||||
* the metricField (they are equal fields from different atoms).
|
||||
*/
|
||||
bool linked(const std::vector<Metric2State>& stateLinks, const int32_t stateAtomId,
|
||||
const Field& stateField, const Field& metricField);
|
||||
} // namespace statsd
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
|
||||
@@ -334,6 +334,11 @@ private:
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
|
||||
|
||||
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
|
||||
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
|
||||
|
||||
@@ -123,7 +123,8 @@ message Atom {
|
||||
BatteryLevelChanged battery_level_changed =
|
||||
30 [(module) = "framework", (module) = "statsdtest"];
|
||||
ChargingStateChanged charging_state_changed = 31 [(module) = "framework"];
|
||||
PluggedStateChanged plugged_state_changed = 32 [(module) = "framework"];
|
||||
PluggedStateChanged plugged_state_changed = 32
|
||||
[(module) = "framework", (module) = "statsdtest"];
|
||||
InteractiveStateChanged interactive_state_changed = 33 [(module) = "framework"];
|
||||
TouchEventReported touch_event_reported = 34;
|
||||
WakeupAlarmOccurred wakeup_alarm_occurred = 35 [(module) = "framework"];
|
||||
|
||||
@@ -55,6 +55,7 @@ const int FIELD_ID_DATA = 1;
|
||||
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 DurationBucketInfo
|
||||
const int FIELD_ID_DURATION = 3;
|
||||
const int FIELD_ID_BUCKET_NUM = 4;
|
||||
@@ -115,6 +116,14 @@ DurationMetricProducer::DurationMetricProducer(
|
||||
}
|
||||
mUnSlicedPartCondition = ConditionState::kUnknown;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions);
|
||||
if (mWizard != nullptr && mConditionTrackerIndex >= 0 &&
|
||||
mMetric2ConditionLinks.size() == 1) {
|
||||
@@ -150,21 +159,49 @@ sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
|
||||
return anomalyTracker;
|
||||
}
|
||||
|
||||
void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
|
||||
const HashableDimensionKey& primaryKey,
|
||||
const int32_t oldState, const int32_t newState) {
|
||||
// Create a FieldValue object to hold the new state.
|
||||
FieldValue value;
|
||||
value.mValue.setInt(newState);
|
||||
// Check if this metric has a StateMap. If so, map the new state value to
|
||||
// the correct state group id.
|
||||
mapStateValue(atomId, &value);
|
||||
|
||||
flushIfNeededLocked(eventTimeNs);
|
||||
|
||||
// Each duration tracker is mapped to a different whatKey (a set of values from the
|
||||
// dimensionsInWhat fields). We notify all trackers iff the primaryKey field values from the
|
||||
// state change event are a subset of the tracker's whatKey field values.
|
||||
//
|
||||
// Ex. For a duration metric dimensioned on uid and tag:
|
||||
// DurationTracker1 whatKey = uid: 1001, tag: 1
|
||||
// DurationTracker2 whatKey = uid: 1002, tag 1
|
||||
//
|
||||
// If the state change primaryKey = uid: 1001, we only notify DurationTracker1 of a state
|
||||
// change.
|
||||
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
|
||||
if (!containsLinkedStateValues(whatIt.first, primaryKey, mMetric2StateLinks, atomId)) {
|
||||
continue;
|
||||
}
|
||||
whatIt.second->onStateChanged(eventTimeNs, atomId, value);
|
||||
}
|
||||
}
|
||||
|
||||
unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
|
||||
const MetricDimensionKey& eventKey) const {
|
||||
switch (mAggregationType) {
|
||||
case DurationMetric_AggregationType_SUM:
|
||||
return make_unique<OringDurationTracker>(
|
||||
mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
|
||||
mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
|
||||
mTimeBaseNs, mBucketSizeNs, mConditionSliced,
|
||||
mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
|
||||
mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
|
||||
mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
|
||||
mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
|
||||
case DurationMetric_AggregationType_MAX_SPARSE:
|
||||
return make_unique<MaxDurationTracker>(
|
||||
mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
|
||||
mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
|
||||
mTimeBaseNs, mBucketSizeNs, mConditionSliced,
|
||||
mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
|
||||
mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
|
||||
mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
|
||||
mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,6 +401,13 @@ void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
|
||||
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
|
||||
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 (DurationBucketInfo).
|
||||
for (const auto& bucket : pair.second) {
|
||||
uint64_t bucketInfoToken = protoOutput->start(
|
||||
@@ -460,7 +504,6 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey
|
||||
const ConditionKey& conditionKeys,
|
||||
bool condition, const LogEvent& event) {
|
||||
const auto& whatKey = eventKey.getDimensionKeyInWhat();
|
||||
|
||||
auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
|
||||
if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
|
||||
if (hitGuardRailLocked(eventKey)) {
|
||||
@@ -471,19 +514,18 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey
|
||||
|
||||
auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
|
||||
if (mUseWhatDimensionAsInternalDimension) {
|
||||
it->second->noteStart(whatKey, condition,
|
||||
event.GetElapsedTimestampNs(), conditionKeys);
|
||||
it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mInternalDimensions.empty()) {
|
||||
it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
|
||||
event.GetElapsedTimestampNs(), conditionKeys);
|
||||
it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(),
|
||||
conditionKeys);
|
||||
} else {
|
||||
HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
|
||||
filterValues(mInternalDimensions, event.getValues(), &dimensionKey);
|
||||
it->second->noteStart(
|
||||
dimensionKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
|
||||
it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(),
|
||||
conditionKeys);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -519,6 +561,41 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
|
||||
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
|
||||
}
|
||||
|
||||
// Stores atom id to primary key pairs for each state atom that the metric is
|
||||
// sliced by.
|
||||
std::map<int, HashableDimensionKey> statePrimaryKeys;
|
||||
|
||||
// For states with primary fields, use MetricStateLinks to get the primary
|
||||
// field values from the log event. These values will form a primary key
|
||||
// that will be used to query StateTracker for the correct state value.
|
||||
for (const auto& stateLink : mMetric2StateLinks) {
|
||||
getDimensionForState(event.getValues(), stateLink,
|
||||
&statePrimaryKeys[stateLink.stateAtomId]);
|
||||
}
|
||||
|
||||
// For each sliced state, query StateTracker for the state value using
|
||||
// either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
|
||||
//
|
||||
// Expected functionality: for any case where the MetricStateLinks are
|
||||
// initialized incorrectly (ex. # of state links != # of primary fields, no
|
||||
// links are provided for a state with primary fields, links are provided
|
||||
// in the wrong order, etc.), StateTracker will simply return kStateUnknown
|
||||
// when queried using an incorrect key.
|
||||
HashableDimensionKey stateValuesKey = DEFAULT_DIMENSION_KEY;
|
||||
for (auto atomId : mSlicedStateAtoms) {
|
||||
FieldValue value;
|
||||
if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
|
||||
// found a primary key for this state, query using the key
|
||||
queryStateValue(atomId, statePrimaryKeys[atomId], &value);
|
||||
} else {
|
||||
// if no MetricStateLinks exist for this state atom,
|
||||
// query using the default dimension key (empty HashableDimensionKey)
|
||||
queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
|
||||
}
|
||||
mapStateValue(atomId, &value);
|
||||
stateValuesKey.addValue(value);
|
||||
}
|
||||
|
||||
// Handles Stop events.
|
||||
if (matcherIndex == mStopIndex) {
|
||||
if (mUseWhatDimensionAsInternalDimension) {
|
||||
@@ -559,8 +636,8 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
|
||||
|
||||
condition = condition && mIsActive;
|
||||
|
||||
handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY), conditionKey,
|
||||
condition, event);
|
||||
handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition,
|
||||
event);
|
||||
}
|
||||
|
||||
size_t DurationMetricProducer::byteSizeLocked() const {
|
||||
|
||||
@@ -54,6 +54,10 @@ public:
|
||||
sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
|
||||
const sp<AlarmMonitor>& anomalyAlarmMonitor) override;
|
||||
|
||||
void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
|
||||
const HashableDimensionKey& primaryKey, const int32_t oldState,
|
||||
const int32_t newState) override;
|
||||
|
||||
protected:
|
||||
void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override;
|
||||
|
||||
@@ -137,7 +141,7 @@ private:
|
||||
|
||||
// Helper function to create a duration tracker given the metric aggregation type.
|
||||
std::unique_ptr<DurationTracker> createDurationTracker(
|
||||
const MetricDimensionKey& eventKey) const;
|
||||
const MetricDimensionKey& eventKey) const;
|
||||
|
||||
// This hides the base class's std::vector<sp<AnomalyTracker>> mAnomalyTrackers
|
||||
std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
|
||||
|
||||
@@ -120,12 +120,13 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo
|
||||
FieldValue value;
|
||||
if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
|
||||
// found a primary key for this state, query using the key
|
||||
getMappedStateValue(atomId, statePrimaryKeys[atomId], &value);
|
||||
queryStateValue(atomId, statePrimaryKeys[atomId], &value);
|
||||
} else {
|
||||
// if no MetricStateLinks exist for this state atom,
|
||||
// query using the default dimension key (empty HashableDimensionKey)
|
||||
getMappedStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
|
||||
queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
|
||||
}
|
||||
mapStateValue(atomId, &value);
|
||||
stateValuesKey.addValue(value);
|
||||
}
|
||||
|
||||
@@ -264,15 +265,17 @@ void MetricProducer::writeActiveMetricToProtoOutputStream(
|
||||
}
|
||||
}
|
||||
|
||||
void MetricProducer::getMappedStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
|
||||
FieldValue* value) {
|
||||
void MetricProducer::queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void MetricProducer::mapStateValue(const int32_t atomId, FieldValue* value) {
|
||||
// check if there is a state map for this atom
|
||||
auto atomIt = mStateGroupMap.find(atomId);
|
||||
if (atomIt == mStateGroupMap.end()) {
|
||||
|
||||
@@ -187,7 +187,8 @@ public:
|
||||
};
|
||||
|
||||
void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
|
||||
const HashableDimensionKey& primaryKey, int oldState, int newState){};
|
||||
const HashableDimensionKey& primaryKey, const int32_t oldState,
|
||||
const int32_t newState){};
|
||||
|
||||
// Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp.
|
||||
// This method clears all the past buckets.
|
||||
@@ -379,11 +380,15 @@ protected:
|
||||
return (endNs - mTimeBaseNs) / mBucketSizeNs - 1;
|
||||
}
|
||||
|
||||
// Query StateManager for original state value.
|
||||
// If no state map exists for this atom, return the original value.
|
||||
// Otherwise, return the group_id mapped to the atom and original value.
|
||||
void getMappedStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
|
||||
FieldValue* value);
|
||||
// Query StateManager for original state value using the queryKey.
|
||||
// The field and value are output.
|
||||
void queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
|
||||
FieldValue* value);
|
||||
|
||||
// If a state map exists for the given atom, replace the original state
|
||||
// value with the group id mapped to the value.
|
||||
// If no state map exists, keep the original state value.
|
||||
void mapStateValue(const int32_t atomId, FieldValue* value);
|
||||
|
||||
DropEvent buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason);
|
||||
|
||||
@@ -467,6 +472,11 @@ protected:
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
|
||||
|
||||
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
|
||||
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
|
||||
|
||||
@@ -329,6 +329,11 @@ private:
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSuperset);
|
||||
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
|
||||
|
||||
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
|
||||
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
|
||||
|
||||
@@ -56,11 +56,19 @@ struct DurationBucket {
|
||||
int64_t mDuration;
|
||||
};
|
||||
|
||||
struct DurationValues {
|
||||
// Recorded duration for current partial bucket.
|
||||
int64_t mDuration;
|
||||
|
||||
// Sum of past partial bucket durations in current full bucket.
|
||||
// Used for anomaly detection.
|
||||
int64_t mDurationFullBucket;
|
||||
};
|
||||
|
||||
class DurationTracker {
|
||||
public:
|
||||
DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
|
||||
sp<ConditionWizard> wizard, int conditionIndex,
|
||||
bool nesting,
|
||||
sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
|
||||
int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
|
||||
int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
|
||||
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
|
||||
@@ -73,7 +81,6 @@ public:
|
||||
mNested(nesting),
|
||||
mCurrentBucketStartTimeNs(currentBucketStartNs),
|
||||
mDuration(0),
|
||||
mDurationFullBucket(0),
|
||||
mCurrentBucketNum(currentBucketNum),
|
||||
mStartTimeNs(startTimeNs),
|
||||
mConditionSliced(conditionSliced),
|
||||
@@ -82,8 +89,8 @@ public:
|
||||
|
||||
virtual ~DurationTracker(){};
|
||||
|
||||
virtual void noteStart(const HashableDimensionKey& key, bool condition,
|
||||
const int64_t eventTime, const ConditionKey& conditionKey) = 0;
|
||||
virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
|
||||
const ConditionKey& conditionKey) = 0;
|
||||
virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
|
||||
const bool stopAll) = 0;
|
||||
virtual void noteStopAll(const int64_t eventTime) = 0;
|
||||
@@ -91,6 +98,9 @@ public:
|
||||
virtual void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) = 0;
|
||||
virtual void onConditionChanged(bool condition, const int64_t timestamp) = 0;
|
||||
|
||||
virtual void onStateChanged(const int64_t timestamp, const int32_t atomId,
|
||||
const FieldValue& newState) = 0;
|
||||
|
||||
// Flush stale buckets if needed, and return true if the tracker has no on-going duration
|
||||
// events, so that the owner can safely remove the tracker.
|
||||
virtual bool flushIfNeeded(
|
||||
@@ -109,9 +119,12 @@ public:
|
||||
// Dump internal states for debugging
|
||||
virtual void dumpStates(FILE* out, bool verbose) const = 0;
|
||||
|
||||
void setEventKey(const MetricDimensionKey& eventKey) {
|
||||
mEventKey = eventKey;
|
||||
}
|
||||
virtual int64_t getCurrentStateKeyDuration() const = 0;
|
||||
|
||||
virtual int64_t getCurrentStateKeyFullBucketDuration() const = 0;
|
||||
|
||||
// Replace old value with new value for the given state atom.
|
||||
virtual void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) = 0;
|
||||
|
||||
protected:
|
||||
int64_t getCurrentBucketEndTimeNs() const {
|
||||
@@ -140,10 +153,11 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void addPastBucketToAnomalyTrackers(const int64_t& bucketValue, const int64_t& bucketNum) {
|
||||
void addPastBucketToAnomalyTrackers(const MetricDimensionKey eventKey,
|
||||
const int64_t& bucketValue, const int64_t& bucketNum) {
|
||||
for (auto& anomalyTracker : mAnomalyTrackers) {
|
||||
if (anomalyTracker != nullptr) {
|
||||
anomalyTracker->addPastBucket(mEventKey, bucketValue, bucketNum);
|
||||
anomalyTracker->addPastBucket(eventKey, bucketValue, bucketNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,6 +178,10 @@ protected:
|
||||
return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
|
||||
}
|
||||
|
||||
void setEventKey(const MetricDimensionKey& eventKey) {
|
||||
mEventKey = eventKey;
|
||||
}
|
||||
|
||||
// A reference to the DurationMetricProducer's config key.
|
||||
const ConfigKey& mConfigKey;
|
||||
|
||||
@@ -183,7 +201,8 @@ protected:
|
||||
|
||||
int64_t mDuration; // current recorded duration result (for partial bucket)
|
||||
|
||||
int64_t mDurationFullBucket; // Sum of past partial buckets in current full bucket.
|
||||
// Recorded duration results for each state key in the current partial bucket.
|
||||
std::unordered_map<HashableDimensionKey, DurationValues> mStateKeyDurationMap;
|
||||
|
||||
int64_t mCurrentBucketNum;
|
||||
|
||||
|
||||
@@ -26,15 +26,14 @@ namespace statsd {
|
||||
|
||||
MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
|
||||
const MetricDimensionKey& eventKey,
|
||||
sp<ConditionWizard> wizard, int conditionIndex,
|
||||
bool nesting,
|
||||
sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
|
||||
int64_t currentBucketStartNs, int64_t currentBucketNum,
|
||||
int64_t startTimeNs, int64_t bucketSizeNs,
|
||||
bool conditionSliced, bool fullLink,
|
||||
const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
|
||||
: DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting,
|
||||
currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs,
|
||||
conditionSliced, fullLink, anomalyTrackers) {
|
||||
: DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
|
||||
currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
|
||||
anomalyTrackers) {
|
||||
}
|
||||
|
||||
bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
|
||||
@@ -91,7 +90,6 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const int64_t eventTime,
|
||||
bool forceStop) {
|
||||
VLOG("MaxDuration: key %s stop", key.toString().c_str());
|
||||
@@ -240,6 +238,11 @@ void MaxDurationTracker::onSlicedConditionMayChange(bool overallCondition,
|
||||
}
|
||||
}
|
||||
|
||||
void MaxDurationTracker::onStateChanged(const int64_t timestamp, const int32_t atomId,
|
||||
const FieldValue& newState) {
|
||||
ALOGE("MaxDurationTracker does not handle sliced state changes.");
|
||||
}
|
||||
|
||||
void MaxDurationTracker::onConditionChanged(bool condition, const int64_t timestamp) {
|
||||
for (auto& pair : mInfos) {
|
||||
noteConditionChanged(pair.first, condition, timestamp);
|
||||
@@ -309,6 +312,20 @@ void MaxDurationTracker::dumpStates(FILE* out, bool verbose) const {
|
||||
fprintf(out, "\t\t current duration %lld\n", (long long)mDuration);
|
||||
}
|
||||
|
||||
int64_t MaxDurationTracker::getCurrentStateKeyDuration() const {
|
||||
ALOGE("MaxDurationTracker does not handle sliced state changes.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t MaxDurationTracker::getCurrentStateKeyFullBucketDuration() const {
|
||||
ALOGE("MaxDurationTracker does not handle sliced state changes.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void MaxDurationTracker::updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) {
|
||||
ALOGE("MaxDurationTracker does not handle sliced state changes.");
|
||||
}
|
||||
|
||||
} // namespace statsd
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
|
||||
@@ -54,10 +54,19 @@ public:
|
||||
void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) override;
|
||||
void onConditionChanged(bool condition, const int64_t timestamp) override;
|
||||
|
||||
void onStateChanged(const int64_t timestamp, const int32_t atomId,
|
||||
const FieldValue& newState) override;
|
||||
|
||||
int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
|
||||
const int64_t currentTimestamp) const override;
|
||||
void dumpStates(FILE* out, bool verbose) const override;
|
||||
|
||||
int64_t getCurrentStateKeyDuration() const override;
|
||||
|
||||
int64_t getCurrentStateKeyFullBucketDuration() const override;
|
||||
|
||||
void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState);
|
||||
|
||||
private:
|
||||
// Returns true if at least one of the mInfos is started.
|
||||
bool anyStarted();
|
||||
|
||||
@@ -26,13 +26,12 @@ using std::pair;
|
||||
|
||||
OringDurationTracker::OringDurationTracker(
|
||||
const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
|
||||
sp<ConditionWizard> wizard, int conditionIndex,
|
||||
bool nesting, int64_t currentBucketStartNs, int64_t currentBucketNum,
|
||||
int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
|
||||
const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
|
||||
: DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting,
|
||||
currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs,
|
||||
conditionSliced, fullLink, anomalyTrackers),
|
||||
sp<ConditionWizard> wizard, int conditionIndex, bool nesting, int64_t currentBucketStartNs,
|
||||
int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
|
||||
bool fullLink, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
|
||||
: DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
|
||||
currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
|
||||
anomalyTrackers),
|
||||
mStarted(),
|
||||
mPaused() {
|
||||
mLastStartTime = 0;
|
||||
@@ -90,10 +89,14 @@ void OringDurationTracker::noteStop(const HashableDimensionKey& key, const int64
|
||||
mConditionKeyMap.erase(key);
|
||||
}
|
||||
if (mStarted.empty()) {
|
||||
mDuration += (timestamp - mLastStartTime);
|
||||
detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
|
||||
VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime,
|
||||
(long long)mDuration);
|
||||
mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
|
||||
(timestamp - mLastStartTime);
|
||||
detectAndDeclareAnomaly(
|
||||
timestamp, mCurrentBucketNum,
|
||||
getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
|
||||
VLOG("record duration %lld, total duration %lld for state key %s",
|
||||
(long long)timestamp - mLastStartTime, (long long)getCurrentStateKeyDuration(),
|
||||
mEventKey.getStateValuesKey().toString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,10 +115,14 @@ void OringDurationTracker::noteStop(const HashableDimensionKey& key, const int64
|
||||
|
||||
void OringDurationTracker::noteStopAll(const int64_t timestamp) {
|
||||
if (!mStarted.empty()) {
|
||||
mDuration += (timestamp - mLastStartTime);
|
||||
VLOG("Oring Stop all: record duration %lld %lld ", (long long)timestamp - mLastStartTime,
|
||||
(long long)mDuration);
|
||||
detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
|
||||
mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
|
||||
(timestamp - mLastStartTime);
|
||||
VLOG("Oring Stop all: record duration %lld, total duration %lld for state key %s",
|
||||
(long long)timestamp - mLastStartTime, (long long)getCurrentStateKeyDuration(),
|
||||
mEventKey.getStateValuesKey().toString().c_str());
|
||||
detectAndDeclareAnomaly(
|
||||
timestamp, mCurrentBucketNum,
|
||||
getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
|
||||
}
|
||||
|
||||
stopAnomalyAlarm(timestamp);
|
||||
@@ -146,21 +153,36 @@ bool OringDurationTracker::flushCurrentBucket(
|
||||
|
||||
// Process the current bucket.
|
||||
if (mStarted.size() > 0) {
|
||||
mDuration += (currentBucketEndTimeNs - mLastStartTime);
|
||||
// Calculate the duration for the current state key.
|
||||
mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
|
||||
(currentBucketEndTimeNs - mLastStartTime);
|
||||
}
|
||||
if (mDuration > 0) {
|
||||
DurationBucket current_info;
|
||||
current_info.mBucketStartNs = mCurrentBucketStartTimeNs;
|
||||
current_info.mBucketEndNs = currentBucketEndTimeNs;
|
||||
current_info.mDuration = mDuration;
|
||||
(*output)[mEventKey].push_back(current_info);
|
||||
mDurationFullBucket += mDuration;
|
||||
VLOG(" duration: %lld", (long long)current_info.mDuration);
|
||||
}
|
||||
if (eventTimeNs > fullBucketEnd) {
|
||||
// End of full bucket, can send to anomaly tracker now.
|
||||
addPastBucketToAnomalyTrackers(mDurationFullBucket, mCurrentBucketNum);
|
||||
mDurationFullBucket = 0;
|
||||
// Store DurationBucket info for each whatKey, stateKey pair.
|
||||
// Note: The whatKey stored in mEventKey is constant for each DurationTracker, while the
|
||||
// stateKey stored in mEventKey is only the current stateKey. mStateKeyDurationMap is used to
|
||||
// store durations for each stateKey, so we need to flush the bucket by creating a
|
||||
// DurationBucket for each stateKey.
|
||||
for (auto& durationIt : mStateKeyDurationMap) {
|
||||
if (durationIt.second.mDuration > 0) {
|
||||
DurationBucket current_info;
|
||||
current_info.mBucketStartNs = mCurrentBucketStartTimeNs;
|
||||
current_info.mBucketEndNs = currentBucketEndTimeNs;
|
||||
current_info.mDuration = durationIt.second.mDuration;
|
||||
(*output)[MetricDimensionKey(mEventKey.getDimensionKeyInWhat(), durationIt.first)]
|
||||
.push_back(current_info);
|
||||
|
||||
durationIt.second.mDurationFullBucket += durationIt.second.mDuration;
|
||||
VLOG(" duration: %lld", (long long)current_info.mDuration);
|
||||
}
|
||||
|
||||
if (eventTimeNs > fullBucketEnd) {
|
||||
// End of full bucket, can send to anomaly tracker now.
|
||||
addPastBucketToAnomalyTrackers(
|
||||
MetricDimensionKey(mEventKey.getDimensionKeyInWhat(), durationIt.first),
|
||||
getCurrentStateKeyFullBucketDuration(), mCurrentBucketNum);
|
||||
durationIt.second.mDurationFullBucket = 0;
|
||||
}
|
||||
durationIt.second.mDuration = 0;
|
||||
}
|
||||
|
||||
if (mStarted.size() > 0) {
|
||||
@@ -169,20 +191,19 @@ bool OringDurationTracker::flushCurrentBucket(
|
||||
info.mBucketStartNs = fullBucketEnd + mBucketSizeNs * (i - 1);
|
||||
info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs;
|
||||
info.mDuration = mBucketSizeNs;
|
||||
// Full duration buckets are attributed to the current stateKey.
|
||||
(*output)[mEventKey].push_back(info);
|
||||
// Safe to send these buckets to anomaly tracker since they must be full buckets.
|
||||
// If it's a partial bucket, numBucketsForward would be 0.
|
||||
addPastBucketToAnomalyTrackers(info.mDuration, mCurrentBucketNum + i);
|
||||
addPastBucketToAnomalyTrackers(mEventKey, info.mDuration, mCurrentBucketNum + i);
|
||||
VLOG(" add filling bucket with duration %lld", (long long)info.mDuration);
|
||||
}
|
||||
} else {
|
||||
if (numBucketsForward >= 2) {
|
||||
addPastBucketToAnomalyTrackers(0, mCurrentBucketNum + numBucketsForward - 1);
|
||||
addPastBucketToAnomalyTrackers(mEventKey, 0, mCurrentBucketNum + numBucketsForward - 1);
|
||||
}
|
||||
}
|
||||
|
||||
mDuration = 0;
|
||||
|
||||
if (numBucketsForward > 0) {
|
||||
mCurrentBucketStartTimeNs = fullBucketEnd + (numBucketsForward - 1) * mBucketSizeNs;
|
||||
mCurrentBucketNum += numBucketsForward;
|
||||
@@ -229,10 +250,14 @@ void OringDurationTracker::onSlicedConditionMayChange(bool overallCondition,
|
||||
}
|
||||
|
||||
if (mStarted.empty()) {
|
||||
mDuration += (timestamp - mLastStartTime);
|
||||
VLOG("Duration add %lld , to %lld ", (long long)(timestamp - mLastStartTime),
|
||||
(long long)mDuration);
|
||||
detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
|
||||
mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
|
||||
(timestamp - mLastStartTime);
|
||||
VLOG("record duration %lld, total duration %lld for state key %s",
|
||||
(long long)(timestamp - mLastStartTime), (long long)getCurrentStateKeyDuration(),
|
||||
mEventKey.getStateValuesKey().toString().c_str());
|
||||
detectAndDeclareAnomaly(
|
||||
timestamp, mCurrentBucketNum,
|
||||
getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,10 +313,13 @@ void OringDurationTracker::onConditionChanged(bool condition, const int64_t time
|
||||
} else {
|
||||
if (!mStarted.empty()) {
|
||||
VLOG("Condition false, all paused");
|
||||
mDuration += (timestamp - mLastStartTime);
|
||||
mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
|
||||
(timestamp - mLastStartTime);
|
||||
mPaused.insert(mStarted.begin(), mStarted.end());
|
||||
mStarted.clear();
|
||||
detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
|
||||
detectAndDeclareAnomaly(
|
||||
timestamp, mCurrentBucketNum,
|
||||
getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
|
||||
}
|
||||
}
|
||||
if (mStarted.empty()) {
|
||||
@@ -299,6 +327,20 @@ void OringDurationTracker::onConditionChanged(bool condition, const int64_t time
|
||||
}
|
||||
}
|
||||
|
||||
void OringDurationTracker::onStateChanged(const int64_t timestamp, const int32_t atomId,
|
||||
const FieldValue& newState) {
|
||||
// If no keys are being tracked, update the current state key and return.
|
||||
if (mStarted.empty()) {
|
||||
updateCurrentStateKey(atomId, newState);
|
||||
return;
|
||||
}
|
||||
// Add the current duration length to the previous state key and then update
|
||||
// the last start time and current state key.
|
||||
mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += (timestamp - mLastStartTime);
|
||||
mLastStartTime = timestamp;
|
||||
updateCurrentStateKey(atomId, newState);
|
||||
}
|
||||
|
||||
int64_t OringDurationTracker::predictAnomalyTimestampNs(
|
||||
const DurationAnomalyTracker& anomalyTracker, const int64_t eventTimestampNs) const {
|
||||
|
||||
@@ -308,12 +350,13 @@ int64_t OringDurationTracker::predictAnomalyTimestampNs(
|
||||
// The timestamp of the current bucket end.
|
||||
const int64_t currentBucketEndNs = getCurrentBucketEndTimeNs();
|
||||
|
||||
// The past duration ns for the current bucket.
|
||||
int64_t currentBucketPastNs = mDuration + mDurationFullBucket;
|
||||
// The past duration ns for the current bucket of the current stateKey.
|
||||
int64_t currentStateBucketPastNs =
|
||||
getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration();
|
||||
|
||||
// As we move into the future, old buckets get overwritten (so their old data is erased).
|
||||
// Sum of past durations. Will change as we overwrite old buckets.
|
||||
int64_t pastNs = currentBucketPastNs + anomalyTracker.getSumOverPastBuckets(mEventKey);
|
||||
int64_t pastNs = currentStateBucketPastNs + anomalyTracker.getSumOverPastBuckets(mEventKey);
|
||||
|
||||
// The refractory period end timestamp for dimension mEventKey.
|
||||
const int64_t refractoryPeriodEndNs =
|
||||
@@ -372,7 +415,7 @@ int64_t OringDurationTracker::predictAnomalyTimestampNs(
|
||||
mEventKey,
|
||||
mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets() + futureBucketIdx);
|
||||
} else if (futureBucketIdx == anomalyTracker.getNumOfPastBuckets()) {
|
||||
pastNs -= (currentBucketPastNs + (currentBucketEndNs - eventTimestampNs));
|
||||
pastNs -= (currentStateBucketPastNs + (currentBucketEndNs - eventTimestampNs));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,7 +425,34 @@ int64_t OringDurationTracker::predictAnomalyTimestampNs(
|
||||
void OringDurationTracker::dumpStates(FILE* out, bool verbose) const {
|
||||
fprintf(out, "\t\t started count %lu\n", (unsigned long)mStarted.size());
|
||||
fprintf(out, "\t\t paused count %lu\n", (unsigned long)mPaused.size());
|
||||
fprintf(out, "\t\t current duration %lld\n", (long long)mDuration);
|
||||
fprintf(out, "\t\t current duration %lld\n", (long long)getCurrentStateKeyDuration());
|
||||
}
|
||||
|
||||
int64_t OringDurationTracker::getCurrentStateKeyDuration() const {
|
||||
auto it = mStateKeyDurationMap.find(mEventKey.getStateValuesKey());
|
||||
if (it == mStateKeyDurationMap.end()) {
|
||||
return 0;
|
||||
} else {
|
||||
return it->second.mDuration;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t OringDurationTracker::getCurrentStateKeyFullBucketDuration() const {
|
||||
auto it = mStateKeyDurationMap.find(mEventKey.getStateValuesKey());
|
||||
if (it == mStateKeyDurationMap.end()) {
|
||||
return 0;
|
||||
} else {
|
||||
return it->second.mDurationFullBucket;
|
||||
}
|
||||
}
|
||||
|
||||
void OringDurationTracker::updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) {
|
||||
HashableDimensionKey* stateValuesKey = mEventKey.getMutableStateValuesKey();
|
||||
for (size_t i = 0; i < stateValuesKey->getValues().size(); i++) {
|
||||
if (stateValuesKey->getValues()[i].mField.getTag() == atomId) {
|
||||
stateValuesKey->mutableValue(i)->mValue = newState.mValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace statsd
|
||||
|
||||
@@ -28,10 +28,9 @@ class OringDurationTracker : public DurationTracker {
|
||||
public:
|
||||
OringDurationTracker(const ConfigKey& key, const int64_t& id,
|
||||
const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
|
||||
int conditionIndex,
|
||||
bool nesting, int64_t currentBucketStartNs, int64_t currentBucketNum,
|
||||
int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
|
||||
bool fullLink,
|
||||
int conditionIndex, bool nesting, int64_t currentBucketStartNs,
|
||||
int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs,
|
||||
bool conditionSliced, bool fullLink,
|
||||
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
|
||||
|
||||
OringDurationTracker(const OringDurationTracker& tracker) = default;
|
||||
@@ -45,6 +44,9 @@ public:
|
||||
void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) override;
|
||||
void onConditionChanged(bool condition, const int64_t timestamp) override;
|
||||
|
||||
void onStateChanged(const int64_t timestamp, const int32_t atomId,
|
||||
const FieldValue& newState) override;
|
||||
|
||||
bool flushCurrentBucket(
|
||||
const int64_t& eventTimeNs,
|
||||
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
|
||||
@@ -56,6 +58,12 @@ public:
|
||||
const int64_t currentTimestamp) const override;
|
||||
void dumpStates(FILE* out, bool verbose) const override;
|
||||
|
||||
int64_t getCurrentStateKeyDuration() const override;
|
||||
|
||||
int64_t getCurrentStateKeyFullBucketDuration() const override;
|
||||
|
||||
void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState);
|
||||
|
||||
private:
|
||||
// We don't need to keep track of individual durations. The information that's needed is:
|
||||
// 1) which keys are started. We record the first start time.
|
||||
|
||||
@@ -564,6 +564,33 @@ 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 (metric.aggregation_type() == DurationMetric::MAX_SPARSE) {
|
||||
ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state");
|
||||
return false;
|
||||
}
|
||||
if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
|
||||
allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (metric.state_link_size() > 0) {
|
||||
ALOGW("DurationMetric 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,
|
||||
@@ -575,7 +602,8 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
|
||||
sp<MetricProducer> durationMetric = new DurationMetricProducer(
|
||||
key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
|
||||
trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs,
|
||||
currentTimeNs, eventActivationMap, eventDeactivationMap);
|
||||
currentTimeNs, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
|
||||
stateGroupMap);
|
||||
|
||||
allMetricProducers.push_back(durationMetric);
|
||||
}
|
||||
|
||||
@@ -103,12 +103,14 @@ message DurationBucketInfo {
|
||||
message DurationMetricData {
|
||||
optional DimensionsValue dimensions_in_what = 1;
|
||||
|
||||
optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
|
||||
repeated StateValue slice_by_state = 6;
|
||||
|
||||
repeated DurationBucketInfo 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];
|
||||
}
|
||||
|
||||
|
||||
@@ -227,8 +227,12 @@ message DurationMetric {
|
||||
|
||||
optional int64 condition = 3;
|
||||
|
||||
repeated int64 slice_by_state = 9;
|
||||
|
||||
repeated MetricConditionLink links = 4;
|
||||
|
||||
repeated MetricStateLink state_link = 10;
|
||||
|
||||
enum AggregationType {
|
||||
SUM = 1;
|
||||
|
||||
@@ -238,9 +242,9 @@ message DurationMetric {
|
||||
|
||||
optional FieldMatcher dimensions_in_what = 6;
|
||||
|
||||
optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
|
||||
|
||||
optional TimeUnit bucket = 7;
|
||||
|
||||
optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
|
||||
}
|
||||
|
||||
message GaugeMetric {
|
||||
|
||||
137
cmds/statsd/tests/HashableDimensionKey_test.cpp
Normal file
137
cmds/statsd/tests/HashableDimensionKey_test.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "src/HashableDimensionKey.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
|
||||
#include "statsd_test_util.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
using android::util::ProtoReader;
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
/**
|
||||
* Test that #containsLinkedStateValues returns false when the whatKey is
|
||||
* smaller than the primaryKey.
|
||||
*/
|
||||
TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_WhatKeyTooSmall) {
|
||||
std::vector<Metric2State> mMetric2StateLinks;
|
||||
|
||||
int32_t uid1 = 1000;
|
||||
HashableDimensionKey whatKey = DEFAULT_DIMENSION_KEY;
|
||||
HashableDimensionKey primaryKey;
|
||||
getUidProcessKey(uid1, &primaryKey);
|
||||
|
||||
EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks,
|
||||
UID_PROCESS_STATE_ATOM_ID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that #containsLinkedStateValues returns false when the linked values
|
||||
* are not equal.
|
||||
*/
|
||||
TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_UnequalLinkedValues) {
|
||||
int stateAtomId = UID_PROCESS_STATE_ATOM_ID;
|
||||
|
||||
FieldMatcher whatMatcher;
|
||||
whatMatcher.set_field(util::OVERLAY_STATE_CHANGED);
|
||||
FieldMatcher* child11 = whatMatcher.add_child();
|
||||
child11->set_field(1);
|
||||
|
||||
FieldMatcher stateMatcher;
|
||||
stateMatcher.set_field(stateAtomId);
|
||||
FieldMatcher* child21 = stateMatcher.add_child();
|
||||
child21->set_field(1);
|
||||
|
||||
std::vector<Metric2State> mMetric2StateLinks;
|
||||
Metric2State ms;
|
||||
ms.stateAtomId = stateAtomId;
|
||||
translateFieldMatcher(whatMatcher, &ms.metricFields);
|
||||
translateFieldMatcher(stateMatcher, &ms.stateFields);
|
||||
mMetric2StateLinks.push_back(ms);
|
||||
|
||||
int32_t uid1 = 1000;
|
||||
int32_t uid2 = 1001;
|
||||
HashableDimensionKey whatKey;
|
||||
getOverlayKey(uid2, "package", &whatKey);
|
||||
HashableDimensionKey primaryKey;
|
||||
getUidProcessKey(uid1, &primaryKey);
|
||||
|
||||
EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that #containsLinkedStateValues returns false when there is no link
|
||||
* between the key values.
|
||||
*/
|
||||
TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_MissingMetric2StateLinks) {
|
||||
int stateAtomId = UID_PROCESS_STATE_ATOM_ID;
|
||||
|
||||
std::vector<Metric2State> mMetric2StateLinks;
|
||||
|
||||
int32_t uid1 = 1000;
|
||||
HashableDimensionKey whatKey;
|
||||
getOverlayKey(uid1, "package", &whatKey);
|
||||
HashableDimensionKey primaryKey;
|
||||
getUidProcessKey(uid1, &primaryKey);
|
||||
|
||||
EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that #containsLinkedStateValues returns true when the key values are
|
||||
* linked and equal.
|
||||
*/
|
||||
TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_AllConditionsMet) {
|
||||
int stateAtomId = UID_PROCESS_STATE_ATOM_ID;
|
||||
|
||||
FieldMatcher whatMatcher;
|
||||
whatMatcher.set_field(util::OVERLAY_STATE_CHANGED);
|
||||
FieldMatcher* child11 = whatMatcher.add_child();
|
||||
child11->set_field(1);
|
||||
|
||||
FieldMatcher stateMatcher;
|
||||
stateMatcher.set_field(stateAtomId);
|
||||
FieldMatcher* child21 = stateMatcher.add_child();
|
||||
child21->set_field(1);
|
||||
|
||||
std::vector<Metric2State> mMetric2StateLinks;
|
||||
Metric2State ms;
|
||||
ms.stateAtomId = stateAtomId;
|
||||
translateFieldMatcher(whatMatcher, &ms.metricFields);
|
||||
translateFieldMatcher(stateMatcher, &ms.stateFields);
|
||||
mMetric2StateLinks.push_back(ms);
|
||||
|
||||
int32_t uid1 = 1000;
|
||||
HashableDimensionKey whatKey;
|
||||
getOverlayKey(uid1, "package", &whatKey);
|
||||
HashableDimensionKey primaryKey;
|
||||
getUidProcessKey(uid1, &primaryKey);
|
||||
|
||||
EXPECT_TRUE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId));
|
||||
}
|
||||
|
||||
} // namespace statsd
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
#endif
|
||||
@@ -14,12 +14,13 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "src/StatsLogProcessor.h"
|
||||
#include "src/state/StateTracker.h"
|
||||
#include "src/stats_log_util.h"
|
||||
#include "tests/statsd_test_util.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
@@ -101,7 +102,7 @@ TEST(DurationMetricE2eTest, TestOneBucket) {
|
||||
reports.reports(0).metrics(0).duration_metrics();
|
||||
EXPECT_EQ(1, durationMetrics.data_size());
|
||||
|
||||
auto data = durationMetrics.data(0);
|
||||
DurationMetricData data = durationMetrics.data(0);
|
||||
EXPECT_EQ(1, data.bucket_info_size());
|
||||
EXPECT_EQ(durationEndNs - durationStartNs, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
@@ -183,7 +184,7 @@ TEST(DurationMetricE2eTest, TestTwoBuckets) {
|
||||
reports.reports(0).metrics(0).duration_metrics();
|
||||
EXPECT_EQ(1, durationMetrics.data_size());
|
||||
|
||||
auto data = durationMetrics.data(0);
|
||||
DurationMetricData data = durationMetrics.data(0);
|
||||
EXPECT_EQ(1, data.bucket_info_size());
|
||||
|
||||
auto bucketInfo = data.bucket_info(0);
|
||||
@@ -353,7 +354,7 @@ TEST(DurationMetricE2eTest, TestWithActivation) {
|
||||
reports.reports(0).metrics(0).duration_metrics();
|
||||
EXPECT_EQ(1, durationMetrics.data_size());
|
||||
|
||||
auto data = durationMetrics.data(0);
|
||||
DurationMetricData data = durationMetrics.data(0);
|
||||
EXPECT_EQ(1, data.bucket_info_size());
|
||||
|
||||
auto bucketInfo = data.bucket_info(0);
|
||||
@@ -434,7 +435,7 @@ TEST(DurationMetricE2eTest, TestWithCondition) {
|
||||
EXPECT_EQ(1, reports.reports(0).metrics_size());
|
||||
EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
|
||||
|
||||
auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
|
||||
DurationMetricData data = reports.reports(0).metrics(0).duration_metrics().data(0);
|
||||
|
||||
// Validate bucket info.
|
||||
EXPECT_EQ(1, data.bucket_info_size());
|
||||
@@ -533,7 +534,7 @@ TEST(DurationMetricE2eTest, TestWithSlicedCondition) {
|
||||
EXPECT_EQ(1, reports.reports(0).metrics_size());
|
||||
EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
|
||||
|
||||
auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
|
||||
DurationMetricData data = reports.reports(0).metrics(0).duration_metrics().data(0);
|
||||
// Validate dimension value.
|
||||
ValidateAttributionUidDimension(data.dimensions_in_what(),
|
||||
util::WAKELOCK_STATE_CHANGED, appUid);
|
||||
@@ -691,7 +692,7 @@ TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) {
|
||||
EXPECT_EQ(1, reports.reports(0).metrics_size());
|
||||
EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
|
||||
|
||||
auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
|
||||
DurationMetricData data = reports.reports(0).metrics(0).duration_metrics().data(0);
|
||||
// Validate dimension value.
|
||||
ValidateAttributionUidDimension(data.dimensions_in_what(),
|
||||
util::WAKELOCK_STATE_CHANGED, appUid);
|
||||
@@ -709,6 +710,734 @@ TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) {
|
||||
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - duration2StartNs, bucketInfo.duration_nanos());
|
||||
}
|
||||
|
||||
TEST(DurationMetricE2eTest, TestWithSlicedState) {
|
||||
// Initialize config.
|
||||
StatsdConfig config;
|
||||
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
|
||||
|
||||
*config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
|
||||
*config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
|
||||
|
||||
auto batterySaverModePredicate = CreateBatterySaverModePredicate();
|
||||
*config.add_predicate() = batterySaverModePredicate;
|
||||
|
||||
auto screenState = CreateScreenState();
|
||||
*config.add_state() = screenState;
|
||||
|
||||
// Create duration metric that slices by screen state.
|
||||
auto durationMetric = config.add_duration_metric();
|
||||
durationMetric->set_id(StringToId("DurationBatterySaverModeSliceScreen"));
|
||||
durationMetric->set_what(batterySaverModePredicate.id());
|
||||
durationMetric->add_slice_by_state(screenState.id());
|
||||
durationMetric->set_aggregation_type(DurationMetric::SUM);
|
||||
durationMetric->set_bucket(FIVE_MINUTES);
|
||||
|
||||
// Initialize StatsLogProcessor.
|
||||
int uid = 12345;
|
||||
int64_t cfgId = 98765;
|
||||
ConfigKey cfgKey(uid, cfgId);
|
||||
uint64_t bucketStartTimeNs = 10000000000; // 0:10
|
||||
uint64_t bucketSizeNs =
|
||||
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
|
||||
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
|
||||
|
||||
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
|
||||
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
|
||||
EXPECT_TRUE(metricsManager->isConfigValid());
|
||||
EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
|
||||
EXPECT_TRUE(metricsManager->isActive());
|
||||
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
|
||||
EXPECT_TRUE(metricProducer->mIsActive);
|
||||
EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
|
||||
EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
|
||||
EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0);
|
||||
|
||||
// Check that StateTrackers were initialized correctly.
|
||||
EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
|
||||
EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
|
||||
|
||||
/*
|
||||
bucket #1 bucket #2
|
||||
| 1 2 3 4 5 6 7 8 9 10 (minutes)
|
||||
|-----------------------------|-----------------------------|--
|
||||
ON OFF ON (BatterySaverMode)
|
||||
| | | (ScreenIsOnEvent)
|
||||
| | (ScreenIsOffEvent)
|
||||
| (ScreenDozeEvent)
|
||||
*/
|
||||
// Initialize log events.
|
||||
std::vector<std::unique_ptr<LogEvent>> events;
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 10 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:20
|
||||
events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 50 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:00
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 80 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 1:30
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 120 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:10
|
||||
events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 250 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:20
|
||||
events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50
|
||||
|
||||
// Bucket boundary.
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 310 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 5:20
|
||||
|
||||
// Send log events to StatsLogProcessor.
|
||||
for (auto& event : events) {
|
||||
processor->OnLogEvent(event.get());
|
||||
}
|
||||
|
||||
// Check dump report.
|
||||
vector<uint8_t> buffer;
|
||||
ConfigMetricsReportList reports;
|
||||
processor->onDumpReport(cfgKey, bucketStartTimeNs + 360 * NS_PER_SEC,
|
||||
true /* include current partial bucket */, true, ADB_DUMP, FAST,
|
||||
&buffer); // 6:10
|
||||
EXPECT_GT(buffer.size(), 0);
|
||||
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
|
||||
backfillDimensionPath(&reports);
|
||||
backfillStringInReport(&reports);
|
||||
backfillStartEndTimestamp(&reports);
|
||||
|
||||
EXPECT_EQ(1, reports.reports_size());
|
||||
EXPECT_EQ(1, reports.reports(0).metrics_size());
|
||||
EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
|
||||
EXPECT_EQ(3, reports.reports(0).metrics(0).duration_metrics().data_size());
|
||||
|
||||
DurationMetricData data = reports.reports(0).metrics(0).duration_metrics().data(0);
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
|
||||
EXPECT_EQ(2, data.bucket_info_size());
|
||||
EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(370 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
|
||||
|
||||
data = reports.reports(0).metrics(0).duration_metrics().data(1);
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
|
||||
EXPECT_EQ(2, data.bucket_info_size());
|
||||
EXPECT_EQ(110 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(370 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
|
||||
|
||||
data = reports.reports(0).metrics(0).duration_metrics().data(2);
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, data.slice_by_state(0).value());
|
||||
EXPECT_EQ(1, data.bucket_info_size());
|
||||
EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
}
|
||||
|
||||
TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState) {
|
||||
// Initialize config.
|
||||
StatsdConfig config;
|
||||
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
|
||||
|
||||
*config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
|
||||
*config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
|
||||
*config.add_atom_matcher() = CreateBatteryStateNoneMatcher();
|
||||
*config.add_atom_matcher() = CreateBatteryStateUsbMatcher();
|
||||
|
||||
auto batterySaverModePredicate = CreateBatterySaverModePredicate();
|
||||
*config.add_predicate() = batterySaverModePredicate;
|
||||
|
||||
auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate();
|
||||
*config.add_predicate() = deviceUnpluggedPredicate;
|
||||
|
||||
auto screenState = CreateScreenState();
|
||||
*config.add_state() = screenState;
|
||||
|
||||
// Create duration metric that has a condition and slices by screen state.
|
||||
auto durationMetric = config.add_duration_metric();
|
||||
durationMetric->set_id(StringToId("DurationBatterySaverModeOnBatterySliceScreen"));
|
||||
durationMetric->set_what(batterySaverModePredicate.id());
|
||||
durationMetric->set_condition(deviceUnpluggedPredicate.id());
|
||||
durationMetric->add_slice_by_state(screenState.id());
|
||||
durationMetric->set_aggregation_type(DurationMetric::SUM);
|
||||
durationMetric->set_bucket(FIVE_MINUTES);
|
||||
|
||||
// Initialize StatsLogProcessor.
|
||||
int uid = 12345;
|
||||
int64_t cfgId = 98765;
|
||||
ConfigKey cfgKey(uid, cfgId);
|
||||
uint64_t bucketStartTimeNs = 10000000000; // 0:10
|
||||
uint64_t bucketSizeNs =
|
||||
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
|
||||
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
|
||||
|
||||
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
|
||||
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
|
||||
EXPECT_TRUE(metricsManager->isConfigValid());
|
||||
EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
|
||||
EXPECT_TRUE(metricsManager->isActive());
|
||||
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
|
||||
EXPECT_TRUE(metricProducer->mIsActive);
|
||||
EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
|
||||
EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
|
||||
EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0);
|
||||
|
||||
// Check that StateTrackers were initialized correctly.
|
||||
EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
|
||||
EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
|
||||
|
||||
/*
|
||||
bucket #1 bucket #2
|
||||
| 1 2 3 4 5 6 7 8 (minutes)
|
||||
|---------------------------------------|------------------
|
||||
ON OFF ON (BatterySaverMode)
|
||||
T F T (DeviceUnpluggedPredicate)
|
||||
| | | (ScreenIsOnEvent)
|
||||
| | | (ScreenIsOffEvent)
|
||||
| (ScreenDozeEvent)
|
||||
*/
|
||||
// Initialize log events.
|
||||
std::vector<std::unique_ptr<LogEvent>> events;
|
||||
events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 60 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 1:10
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 80 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:30
|
||||
events.push_back(
|
||||
CreateBatteryStateChangedEvent(bucketStartTimeNs + 110 * NS_PER_SEC,
|
||||
BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE)); // 2:00
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 145 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:35
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 170 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 3:00
|
||||
events.push_back(
|
||||
CreateBatteryStateChangedEvent(bucketStartTimeNs + 180 * NS_PER_SEC,
|
||||
BatteryPluggedStateEnum::BATTERY_PLUGGED_USB)); // 3:10
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 200 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:30
|
||||
events.push_back(
|
||||
CreateBatteryStateChangedEvent(bucketStartTimeNs + 230 * NS_PER_SEC,
|
||||
BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE)); // 4:00
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 260 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 4:30
|
||||
events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50
|
||||
|
||||
// Bucket boundary.
|
||||
events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 320 * NS_PER_SEC)); // 5:30
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 380 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 6:30
|
||||
|
||||
// Send log events to StatsLogProcessor.
|
||||
for (auto& event : events) {
|
||||
processor->OnLogEvent(event.get());
|
||||
}
|
||||
|
||||
// Check dump report.
|
||||
vector<uint8_t> buffer;
|
||||
ConfigMetricsReportList reports;
|
||||
processor->onDumpReport(cfgKey, bucketStartTimeNs + 410 * NS_PER_SEC,
|
||||
true /* include current partial bucket */, true, ADB_DUMP, FAST,
|
||||
&buffer);
|
||||
EXPECT_GT(buffer.size(), 0);
|
||||
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
|
||||
backfillDimensionPath(&reports);
|
||||
backfillStringInReport(&reports);
|
||||
backfillStartEndTimestamp(&reports);
|
||||
|
||||
EXPECT_EQ(1, reports.reports_size());
|
||||
EXPECT_EQ(1, reports.reports(0).metrics_size());
|
||||
EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
|
||||
EXPECT_EQ(3, reports.reports(0).metrics(0).duration_metrics().data_size());
|
||||
|
||||
DurationMetricData data = reports.reports(0).metrics(0).duration_metrics().data(0);
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
|
||||
EXPECT_EQ(2, data.bucket_info_size());
|
||||
EXPECT_EQ(45 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(420 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
|
||||
|
||||
data = reports.reports(0).metrics(0).duration_metrics().data(2);
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
|
||||
EXPECT_EQ(2, data.bucket_info_size());
|
||||
EXPECT_EQ(45 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(60 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(420 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
|
||||
|
||||
data = reports.reports(0).metrics(0).duration_metrics().data(1);
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, data.slice_by_state(0).value());
|
||||
EXPECT_EQ(1, data.bucket_info_size());
|
||||
EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
}
|
||||
|
||||
TEST(DurationMetricE2eTest, TestWithSlicedStateMapped) {
|
||||
// Initialize config.
|
||||
StatsdConfig config;
|
||||
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
|
||||
|
||||
*config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
|
||||
*config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
|
||||
|
||||
auto batterySaverModePredicate = CreateBatterySaverModePredicate();
|
||||
*config.add_predicate() = batterySaverModePredicate;
|
||||
|
||||
auto screenStateWithMap = CreateScreenStateWithOnOffMap();
|
||||
*config.add_state() = screenStateWithMap;
|
||||
|
||||
// Create duration metric that slices by mapped screen state.
|
||||
auto durationMetric = config.add_duration_metric();
|
||||
durationMetric->set_id(StringToId("DurationBatterySaverModeSliceScreenMapped"));
|
||||
durationMetric->set_what(batterySaverModePredicate.id());
|
||||
durationMetric->add_slice_by_state(screenStateWithMap.id());
|
||||
durationMetric->set_aggregation_type(DurationMetric::SUM);
|
||||
durationMetric->set_bucket(FIVE_MINUTES);
|
||||
|
||||
// Initialize StatsLogProcessor.
|
||||
int uid = 12345;
|
||||
int64_t cfgId = 98765;
|
||||
ConfigKey cfgKey(uid, cfgId);
|
||||
uint64_t bucketStartTimeNs = 10000000000; // 0:10
|
||||
uint64_t bucketSizeNs =
|
||||
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
|
||||
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
|
||||
|
||||
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
|
||||
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
|
||||
EXPECT_TRUE(metricsManager->isConfigValid());
|
||||
EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
|
||||
EXPECT_TRUE(metricsManager->isActive());
|
||||
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
|
||||
EXPECT_TRUE(metricProducer->mIsActive);
|
||||
EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
|
||||
EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
|
||||
EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1);
|
||||
|
||||
// Check that StateTrackers were initialized correctly.
|
||||
EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
|
||||
EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
|
||||
|
||||
/*
|
||||
bucket #1 bucket #2
|
||||
| 1 2 3 4 5 6 7 8 9 10 (minutes)
|
||||
|-----------------------------|-----------------------------|--
|
||||
ON OFF ON (BatterySaverMode)
|
||||
---------------------------------------------------------SCREEN_OFF events
|
||||
| | (ScreenStateOffEvent = 1)
|
||||
| (ScreenStateDozeEvent = 3)
|
||||
| (ScreenStateDozeSuspendEvent = 4)
|
||||
---------------------------------------------------------SCREEN_ON events
|
||||
| | | (ScreenStateOnEvent = 2)
|
||||
| (ScreenStateVrEvent = 5)
|
||||
| (ScreenStateOnSuspendEvent = 6)
|
||||
*/
|
||||
// Initialize log events.
|
||||
std::vector<std::unique_ptr<LogEvent>> events;
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 10 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:20
|
||||
events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 70 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:20
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 100 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 1:50
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 120 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:10
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 170 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_VR)); // 3:00
|
||||
events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 250 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:20
|
||||
events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50
|
||||
|
||||
// Bucket boundary 5:10.
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 320 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 5:30
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 390 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40
|
||||
events.push_back(CreateScreenStateChangedEvent(
|
||||
bucketStartTimeNs + 430 * NS_PER_SEC,
|
||||
android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND)); // 7:20
|
||||
// Send log events to StatsLogProcessor.
|
||||
for (auto& event : events) {
|
||||
processor->OnLogEvent(event.get());
|
||||
}
|
||||
|
||||
// Check dump report.
|
||||
vector<uint8_t> buffer;
|
||||
ConfigMetricsReportList reports;
|
||||
processor->onDumpReport(cfgKey, bucketStartTimeNs + 490 * NS_PER_SEC,
|
||||
true /* include current partial bucket */, true, ADB_DUMP, FAST,
|
||||
&buffer);
|
||||
EXPECT_GT(buffer.size(), 0);
|
||||
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
|
||||
backfillDimensionPath(&reports);
|
||||
backfillStringInReport(&reports);
|
||||
backfillStartEndTimestamp(&reports);
|
||||
|
||||
EXPECT_EQ(1, reports.reports_size());
|
||||
EXPECT_EQ(1, reports.reports(0).metrics_size());
|
||||
EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
|
||||
EXPECT_EQ(2, reports.reports(0).metrics(0).duration_metrics().data_size());
|
||||
|
||||
DurationMetricData data = reports.reports(0).metrics(0).duration_metrics().data(0);
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_group_id());
|
||||
EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id());
|
||||
EXPECT_EQ(2, data.bucket_info_size());
|
||||
EXPECT_EQ(130 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(110 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(500 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
|
||||
|
||||
data = reports.reports(0).metrics(0).duration_metrics().data(1);
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_group_id());
|
||||
EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id());
|
||||
EXPECT_EQ(2, data.bucket_info_size());
|
||||
EXPECT_EQ(70 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(80 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(500 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
|
||||
}
|
||||
|
||||
TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat) {
|
||||
// Initialize config.
|
||||
StatsdConfig config;
|
||||
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
|
||||
|
||||
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
|
||||
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
|
||||
|
||||
auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
|
||||
*config.add_predicate() = holdingWakelockPredicate;
|
||||
|
||||
auto uidProcessState = CreateUidProcessState();
|
||||
*config.add_state() = uidProcessState;
|
||||
|
||||
// Create duration metric that slices by uid process state.
|
||||
auto durationMetric = config.add_duration_metric();
|
||||
durationMetric->set_id(StringToId("DurationHoldingWakelockSliceUidProcessState"));
|
||||
durationMetric->set_what(holdingWakelockPredicate.id());
|
||||
durationMetric->add_slice_by_state(uidProcessState.id());
|
||||
durationMetric->set_aggregation_type(DurationMetric::SUM);
|
||||
durationMetric->set_bucket(FIVE_MINUTES);
|
||||
|
||||
// The state has only one primary field (uid).
|
||||
auto stateLink = durationMetric->add_state_link();
|
||||
stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
|
||||
auto fieldsInWhat = stateLink->mutable_fields_in_what();
|
||||
*fieldsInWhat = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
|
||||
auto fieldsInState = stateLink->mutable_fields_in_state();
|
||||
*fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
|
||||
|
||||
// Initialize StatsLogProcessor.
|
||||
int uid = 12345;
|
||||
int64_t cfgId = 98765;
|
||||
ConfigKey cfgKey(uid, cfgId);
|
||||
uint64_t bucketStartTimeNs = 10000000000; // 0:10
|
||||
uint64_t bucketSizeNs =
|
||||
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
|
||||
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
|
||||
|
||||
// This config is rejected because the dimension in what fields are not a superset of the sliced
|
||||
// state primary fields.
|
||||
EXPECT_EQ(processor->mMetricsManagers.size(), 0);
|
||||
}
|
||||
|
||||
TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset) {
|
||||
// Initialize config.
|
||||
StatsdConfig config;
|
||||
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
|
||||
|
||||
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
|
||||
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
|
||||
|
||||
auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
|
||||
*config.add_predicate() = holdingWakelockPredicate;
|
||||
|
||||
auto uidProcessState = CreateUidProcessState();
|
||||
*config.add_state() = uidProcessState;
|
||||
|
||||
// Create duration metric that slices by uid process state.
|
||||
auto durationMetric = config.add_duration_metric();
|
||||
durationMetric->set_id(StringToId("DurationPartialWakelockPerTagUidSliceProcessState"));
|
||||
durationMetric->set_what(holdingWakelockPredicate.id());
|
||||
durationMetric->add_slice_by_state(uidProcessState.id());
|
||||
durationMetric->set_aggregation_type(DurationMetric::SUM);
|
||||
durationMetric->set_bucket(FIVE_MINUTES);
|
||||
|
||||
// The metric is dimensioning by first uid of attribution node and tag.
|
||||
*durationMetric->mutable_dimensions_in_what() = CreateAttributionUidAndOtherDimensions(
|
||||
util::WAKELOCK_STATE_CHANGED, {Position::FIRST}, {3 /* tag */});
|
||||
// The state has only one primary field (uid).
|
||||
auto stateLink = durationMetric->add_state_link();
|
||||
stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
|
||||
auto fieldsInWhat = stateLink->mutable_fields_in_what();
|
||||
*fieldsInWhat = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
|
||||
auto fieldsInState = stateLink->mutable_fields_in_state();
|
||||
*fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
|
||||
|
||||
// Initialize StatsLogProcessor.
|
||||
int uid = 12345;
|
||||
int64_t cfgId = 98765;
|
||||
ConfigKey cfgKey(uid, cfgId);
|
||||
uint64_t bucketStartTimeNs = 10000000000; // 0:10
|
||||
uint64_t bucketSizeNs =
|
||||
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
|
||||
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
|
||||
|
||||
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
|
||||
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
|
||||
EXPECT_TRUE(metricsManager->isConfigValid());
|
||||
EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
|
||||
EXPECT_TRUE(metricsManager->isActive());
|
||||
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
|
||||
EXPECT_TRUE(metricProducer->mIsActive);
|
||||
EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
|
||||
EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID);
|
||||
EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0);
|
||||
|
||||
// Check that StateTrackers were initialized correctly.
|
||||
EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
|
||||
EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
|
||||
|
||||
// Initialize log events.
|
||||
int appUid1 = 1001;
|
||||
int appUid2 = 1002;
|
||||
std::vector<int> attributionUids1 = {appUid1};
|
||||
std::vector<string> attributionTags1 = {"App1"};
|
||||
|
||||
std::vector<int> attributionUids2 = {appUid2};
|
||||
std::vector<string> attributionTags2 = {"App2"};
|
||||
|
||||
std::vector<std::unique_ptr<LogEvent>> events;
|
||||
events.push_back(CreateUidProcessStateChangedEvent(
|
||||
bucketStartTimeNs + 10 * NS_PER_SEC, appUid1,
|
||||
android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:20
|
||||
events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 20 * NS_PER_SEC,
|
||||
attributionUids1, attributionTags1,
|
||||
"wakelock1")); // 0:30
|
||||
events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 25 * NS_PER_SEC,
|
||||
attributionUids1, attributionTags1,
|
||||
"wakelock2")); // 0:35
|
||||
events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 30 * NS_PER_SEC,
|
||||
attributionUids2, attributionTags2,
|
||||
"wakelock1")); // 0:40
|
||||
events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 35 * NS_PER_SEC,
|
||||
attributionUids2, attributionTags2,
|
||||
"wakelock2")); // 0:45
|
||||
events.push_back(CreateUidProcessStateChangedEvent(
|
||||
bucketStartTimeNs + 50 * NS_PER_SEC, appUid2,
|
||||
android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 1:00
|
||||
events.push_back(CreateUidProcessStateChangedEvent(
|
||||
bucketStartTimeNs + 60 * NS_PER_SEC, appUid1,
|
||||
android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 1:10
|
||||
events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 100 * NS_PER_SEC,
|
||||
attributionUids2, attributionTags2,
|
||||
"wakelock1")); // 1:50
|
||||
events.push_back(CreateUidProcessStateChangedEvent(
|
||||
bucketStartTimeNs + 120 * NS_PER_SEC, appUid2,
|
||||
android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 2:10
|
||||
events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 200 * NS_PER_SEC,
|
||||
attributionUids1, attributionTags1,
|
||||
"wakelock2")); // 3:30
|
||||
|
||||
// Send log events to StatsLogProcessor.
|
||||
for (auto& event : events) {
|
||||
processor->OnLogEvent(event.get());
|
||||
}
|
||||
|
||||
// Check dump report.
|
||||
vector<uint8_t> buffer;
|
||||
ConfigMetricsReportList reports;
|
||||
processor->onDumpReport(cfgKey, bucketStartTimeNs + 320 * NS_PER_SEC,
|
||||
true /* include current partial bucket */, true, ADB_DUMP, FAST,
|
||||
&buffer);
|
||||
EXPECT_GT(buffer.size(), 0);
|
||||
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
|
||||
backfillDimensionPath(&reports);
|
||||
backfillStringInReport(&reports);
|
||||
backfillStartEndTimestamp(&reports);
|
||||
|
||||
EXPECT_EQ(1, reports.reports_size());
|
||||
EXPECT_EQ(1, reports.reports(0).metrics_size());
|
||||
EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
|
||||
EXPECT_EQ(9, reports.reports(0).metrics(0).duration_metrics().data_size());
|
||||
|
||||
DurationMetricData data = reports.reports(0).metrics(0).duration_metrics().data(0);
|
||||
ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1,
|
||||
"wakelock2");
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
|
||||
data.slice_by_state(0).value());
|
||||
EXPECT_EQ(1, data.bucket_info_size());
|
||||
EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
|
||||
data = reports.reports(0).metrics(0).duration_metrics().data(1);
|
||||
ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1,
|
||||
"wakelock2");
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
|
||||
data.slice_by_state(0).value());
|
||||
EXPECT_EQ(1, data.bucket_info_size());
|
||||
EXPECT_EQ(140 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
|
||||
data = reports.reports(0).metrics(0).duration_metrics().data(2);
|
||||
ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
|
||||
"wakelock1");
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(-1 /* StateTracker:: kStateUnknown */, data.slice_by_state(0).value());
|
||||
EXPECT_EQ(1, data.bucket_info_size());
|
||||
EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
|
||||
data = reports.reports(0).metrics(0).duration_metrics().data(3);
|
||||
ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1,
|
||||
"wakelock1");
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
|
||||
data.slice_by_state(0).value());
|
||||
EXPECT_EQ(2, data.bucket_info_size());
|
||||
EXPECT_EQ(240 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(330 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
|
||||
|
||||
data = reports.reports(0).metrics(0).duration_metrics().data(4);
|
||||
ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
|
||||
"wakelock1");
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
|
||||
data.slice_by_state(0).value());
|
||||
EXPECT_EQ(1, data.bucket_info_size());
|
||||
EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
|
||||
data = reports.reports(0).metrics(0).duration_metrics().data(5);
|
||||
ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
|
||||
"wakelock2");
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
|
||||
data.slice_by_state(0).value());
|
||||
EXPECT_EQ(2, data.bucket_info_size());
|
||||
EXPECT_EQ(180 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(330 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
|
||||
|
||||
data = reports.reports(0).metrics(0).duration_metrics().data(6);
|
||||
ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
|
||||
"wakelock2");
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(-1 /* StateTracker:: kStateUnknown */, data.slice_by_state(0).value());
|
||||
EXPECT_EQ(1, data.bucket_info_size());
|
||||
EXPECT_EQ(15 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
|
||||
data = reports.reports(0).metrics(0).duration_metrics().data(7);
|
||||
ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1,
|
||||
"wakelock1");
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
|
||||
data.slice_by_state(0).value());
|
||||
EXPECT_EQ(1, data.bucket_info_size());
|
||||
EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
|
||||
data = reports.reports(0).metrics(0).duration_metrics().data(8);
|
||||
ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
|
||||
"wakelock2");
|
||||
EXPECT_EQ(1, data.slice_by_state_size());
|
||||
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
|
||||
EXPECT_TRUE(data.slice_by_state(0).has_value());
|
||||
EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
|
||||
data.slice_by_state(0).value());
|
||||
EXPECT_EQ(1, data.bucket_info_size());
|
||||
EXPECT_EQ(70 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
|
||||
EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
|
||||
EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
|
||||
}
|
||||
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
#endif
|
||||
|
||||
@@ -62,9 +62,8 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
|
||||
int64_t bucketNum = 0;
|
||||
|
||||
int64_t metricId = 1;
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1,
|
||||
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
|
||||
false, false, {});
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
|
||||
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
|
||||
|
||||
tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey());
|
||||
// Event starts again. This would not change anything as it already starts.
|
||||
@@ -97,9 +96,8 @@ TEST(MaxDurationTrackerTest, TestStopAll) {
|
||||
int64_t bucketNum = 0;
|
||||
|
||||
int64_t metricId = 1;
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1,
|
||||
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
|
||||
false, false, {});
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
|
||||
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
|
||||
|
||||
tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey());
|
||||
|
||||
@@ -132,9 +130,8 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
|
||||
int64_t bucketNum = 0;
|
||||
|
||||
int64_t metricId = 1;
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1,
|
||||
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
|
||||
false, false, {});
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
|
||||
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
|
||||
|
||||
// The event starts.
|
||||
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
|
||||
@@ -172,9 +169,8 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
|
||||
int64_t bucketNum = 0;
|
||||
|
||||
int64_t metricId = 1;
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1,
|
||||
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
|
||||
false, false, {});
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
|
||||
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
|
||||
|
||||
// 2 starts
|
||||
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
|
||||
@@ -218,9 +214,8 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
|
||||
int64_t eventStopTimeNs = conditionStops2 + 8 * NS_PER_SEC;
|
||||
|
||||
int64_t metricId = 1;
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
false, bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, true,
|
||||
false, {});
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
|
||||
0, bucketStartTimeNs, bucketSizeNs, true, false, {});
|
||||
EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
|
||||
|
||||
tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1);
|
||||
@@ -267,9 +262,9 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
|
||||
sp<AlarmMonitor> alarmMonitor;
|
||||
sp<DurationAnomalyTracker> anomalyTracker =
|
||||
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
|
||||
true, false, {anomalyTracker});
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
|
||||
bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
|
||||
{anomalyTracker});
|
||||
|
||||
tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
|
||||
sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
|
||||
@@ -326,9 +321,9 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) {
|
||||
sp<AlarmMonitor> alarmMonitor;
|
||||
sp<DurationAnomalyTracker> anomalyTracker =
|
||||
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
|
||||
true, false, {anomalyTracker});
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
|
||||
bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
|
||||
{anomalyTracker});
|
||||
|
||||
tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1);
|
||||
tracker.noteConditionChanged(key1, true, conditionStarts1);
|
||||
@@ -408,9 +403,9 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) {
|
||||
sp<AlarmMonitor> alarmMonitor;
|
||||
sp<DurationAnomalyTracker> anomalyTracker =
|
||||
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
|
||||
true, false, {anomalyTracker});
|
||||
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
|
||||
bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
|
||||
{anomalyTracker});
|
||||
|
||||
tracker.noteStart(key1, true, eventStartTimeNs1, conditionKey1);
|
||||
tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2);
|
||||
|
||||
@@ -61,9 +61,9 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) {
|
||||
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
|
||||
int64_t durationTimeNs = 2 * 1000;
|
||||
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
|
||||
bucketSizeNs, false, false, {});
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
|
||||
bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
|
||||
false, false, {});
|
||||
|
||||
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
|
||||
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
|
||||
@@ -92,9 +92,8 @@ TEST(OringDurationTrackerTest, TestDurationNested) {
|
||||
int64_t bucketNum = 0;
|
||||
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
|
||||
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
|
||||
bucketSizeNs, false, false, {});
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
|
||||
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
|
||||
|
||||
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
|
||||
tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
|
||||
@@ -124,9 +123,8 @@ TEST(OringDurationTrackerTest, TestStopAll) {
|
||||
int64_t bucketNum = 0;
|
||||
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
|
||||
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
|
||||
bucketSizeNs, false, false, {});
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
|
||||
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
|
||||
|
||||
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
|
||||
tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
|
||||
@@ -154,9 +152,8 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
|
||||
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
|
||||
int64_t durationTimeNs = 2 * 1000;
|
||||
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
|
||||
bucketSizeNs, false, false, {});
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
|
||||
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
|
||||
|
||||
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
|
||||
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
|
||||
@@ -198,9 +195,9 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) {
|
||||
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
|
||||
int64_t durationTimeNs = 2 * 1000;
|
||||
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
|
||||
bucketSizeNs, true, false, {});
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
|
||||
bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
|
||||
true, false, {});
|
||||
|
||||
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
|
||||
|
||||
@@ -237,9 +234,9 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
|
||||
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
|
||||
int64_t durationTimeNs = 2 * 1000;
|
||||
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
|
||||
bucketSizeNs, true, false, {});
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
|
||||
bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
|
||||
true, false, {});
|
||||
|
||||
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
|
||||
// condition to false; record duration 5n
|
||||
@@ -275,9 +272,8 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
|
||||
int64_t bucketNum = 0;
|
||||
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
|
||||
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
|
||||
bucketSizeNs, true, false, {});
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
|
||||
bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {});
|
||||
|
||||
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
|
||||
tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1);
|
||||
@@ -316,9 +312,9 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
|
||||
sp<AlarmMonitor> alarmMonitor;
|
||||
sp<DurationAnomalyTracker> anomalyTracker =
|
||||
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
|
||||
bucketSizeNs, true, false, {anomalyTracker});
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
|
||||
bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
|
||||
{anomalyTracker});
|
||||
|
||||
// Nothing in the past bucket.
|
||||
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
|
||||
@@ -422,9 +418,8 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp3) {
|
||||
sp<AlarmMonitor> alarmMonitor;
|
||||
sp<DurationAnomalyTracker> anomalyTracker =
|
||||
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
|
||||
OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY,
|
||||
wizard, 1,
|
||||
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
|
||||
OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, wizard,
|
||||
1, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
|
||||
bucketSizeNs, true, false, {anomalyTracker});
|
||||
|
||||
int64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC;
|
||||
@@ -481,15 +476,15 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
|
||||
sp<AlarmMonitor> alarmMonitor;
|
||||
sp<DurationAnomalyTracker> anomalyTracker =
|
||||
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
true /*nesting*/, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
|
||||
bucketSizeNs, false, false, {anomalyTracker});
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
|
||||
bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
|
||||
false, false, {anomalyTracker});
|
||||
|
||||
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
|
||||
tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
|
||||
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
|
||||
EXPECT_TRUE(tracker.mStarted.empty());
|
||||
EXPECT_EQ(10LL, tracker.mDuration); // 10ns
|
||||
EXPECT_EQ(10LL, tracker.mStateKeyDurationMap[DEFAULT_DIMENSION_KEY].mDuration); // 10ns
|
||||
|
||||
EXPECT_EQ(0u, tracker.mStarted.size());
|
||||
|
||||
@@ -530,11 +525,11 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
|
||||
sp<AlarmMonitor> alarmMonitor;
|
||||
sp<DurationAnomalyTracker> anomalyTracker =
|
||||
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
|
||||
true /*nesting*/, bucketStartTimeNs, 0, bucketStartTimeNs,
|
||||
bucketSizeNs, false, false, {anomalyTracker});
|
||||
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
|
||||
bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, false,
|
||||
false, {anomalyTracker});
|
||||
|
||||
tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
|
||||
tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
|
||||
EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
|
||||
sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
|
||||
EXPECT_EQ((long long)(55ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
|
||||
@@ -544,13 +539,13 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
|
||||
EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
|
||||
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
|
||||
|
||||
tracker.noteStart(kEventKey1, true, 22 * NS_PER_SEC, conkey); // start key1 again
|
||||
tracker.noteStart(kEventKey1, true, 22 * NS_PER_SEC, conkey); // start key1 again
|
||||
EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
|
||||
alarm = anomalyTracker->mAlarms.begin()->second;
|
||||
EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
|
||||
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
|
||||
|
||||
tracker.noteStart(kEventKey2, true, 32 * NS_PER_SEC, conkey); // start key2
|
||||
tracker.noteStart(kEventKey2, true, 32 * NS_PER_SEC, conkey); // start key2
|
||||
EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
|
||||
alarm = anomalyTracker->mAlarms.begin()->second;
|
||||
EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
|
||||
|
||||
@@ -105,63 +105,6 @@ std::unique_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::stri
|
||||
}
|
||||
// END: build event functions.
|
||||
|
||||
// START: get primary key functions
|
||||
void getUidProcessKey(int uid, HashableDimensionKey* key) {
|
||||
int pos1[] = {1, 0, 0};
|
||||
Field field1(27 /* atom id */, pos1, 0 /* depth */);
|
||||
Value value1((int32_t)uid);
|
||||
|
||||
key->addValue(FieldValue(field1, value1));
|
||||
}
|
||||
|
||||
void getOverlayKey(int uid, string packageName, HashableDimensionKey* key) {
|
||||
int pos1[] = {1, 0, 0};
|
||||
int pos2[] = {2, 0, 0};
|
||||
|
||||
Field field1(59 /* atom id */, pos1, 0 /* depth */);
|
||||
Field field2(59 /* atom id */, pos2, 0 /* depth */);
|
||||
|
||||
Value value1((int32_t)uid);
|
||||
Value value2(packageName);
|
||||
|
||||
key->addValue(FieldValue(field1, value1));
|
||||
key->addValue(FieldValue(field2, value2));
|
||||
}
|
||||
|
||||
void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key) {
|
||||
int pos1[] = {1, 1, 1};
|
||||
int pos3[] = {2, 0, 0};
|
||||
int pos4[] = {3, 0, 0};
|
||||
|
||||
Field field1(10 /* atom id */, pos1, 2 /* depth */);
|
||||
|
||||
Field field3(10 /* atom id */, pos3, 0 /* depth */);
|
||||
Field field4(10 /* atom id */, pos4, 0 /* depth */);
|
||||
|
||||
Value value1((int32_t)uid);
|
||||
Value value3((int32_t)1 /*partial*/);
|
||||
Value value4(tag);
|
||||
|
||||
key->addValue(FieldValue(field1, value1));
|
||||
key->addValue(FieldValue(field3, value3));
|
||||
key->addValue(FieldValue(field4, value4));
|
||||
}
|
||||
|
||||
void getPartialWakelockKey(int uid, HashableDimensionKey* key) {
|
||||
int pos1[] = {1, 1, 1};
|
||||
int pos3[] = {2, 0, 0};
|
||||
|
||||
Field field1(10 /* atom id */, pos1, 2 /* depth */);
|
||||
Field field3(10 /* atom id */, pos3, 0 /* depth */);
|
||||
|
||||
Value value1((int32_t)uid);
|
||||
Value value3((int32_t)1 /*partial*/);
|
||||
|
||||
key->addValue(FieldValue(field1, value1));
|
||||
key->addValue(FieldValue(field3, value3));
|
||||
}
|
||||
// END: get primary key functions
|
||||
|
||||
TEST(StateListenerTest, TestStateListenerWeakPointer) {
|
||||
sp<TestStateListener> listener = new TestStateListener();
|
||||
wp<TestStateListener> wListener = listener;
|
||||
|
||||
@@ -135,6 +135,27 @@ AtomMatcher CreateBatterySaverModeStopAtomMatcher() {
|
||||
"BatterySaverModeStop", BatterySaverModeStateChanged::OFF);
|
||||
}
|
||||
|
||||
AtomMatcher CreateBatteryStateChangedAtomMatcher(const string& name,
|
||||
BatteryPluggedStateEnum state) {
|
||||
AtomMatcher atom_matcher;
|
||||
atom_matcher.set_id(StringToId(name));
|
||||
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
|
||||
simple_atom_matcher->set_atom_id(util::PLUGGED_STATE_CHANGED);
|
||||
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
|
||||
field_value_matcher->set_field(1); // State field.
|
||||
field_value_matcher->set_eq_int(state);
|
||||
return atom_matcher;
|
||||
}
|
||||
|
||||
AtomMatcher CreateBatteryStateNoneMatcher() {
|
||||
return CreateBatteryStateChangedAtomMatcher("BatteryPluggedNone",
|
||||
BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE);
|
||||
}
|
||||
|
||||
AtomMatcher CreateBatteryStateUsbMatcher() {
|
||||
return CreateBatteryStateChangedAtomMatcher("BatteryPluggedUsb",
|
||||
BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
|
||||
}
|
||||
|
||||
AtomMatcher CreateScreenStateChangedAtomMatcher(
|
||||
const string& name, android::view::DisplayStateEnum state) {
|
||||
@@ -234,6 +255,14 @@ Predicate CreateBatterySaverModePredicate() {
|
||||
return predicate;
|
||||
}
|
||||
|
||||
Predicate CreateDeviceUnpluggedPredicate() {
|
||||
Predicate predicate;
|
||||
predicate.set_id(StringToId("DeviceUnplugged"));
|
||||
predicate.mutable_simple_predicate()->set_start(StringToId("BatteryPluggedNone"));
|
||||
predicate.mutable_simple_predicate()->set_stop(StringToId("BatteryPluggedUsb"));
|
||||
return predicate;
|
||||
}
|
||||
|
||||
Predicate CreateScreenIsOnPredicate() {
|
||||
Predicate predicate;
|
||||
predicate.set_id(StringToId("ScreenIsOn"));
|
||||
@@ -410,6 +439,74 @@ FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields)
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
FieldMatcher CreateAttributionUidAndOtherDimensions(const int atomId,
|
||||
const std::vector<Position>& positions,
|
||||
const std::vector<int>& fields) {
|
||||
FieldMatcher dimensions = CreateAttributionUidDimensions(atomId, positions);
|
||||
|
||||
for (const int field : fields) {
|
||||
dimensions.add_child()->set_field(field);
|
||||
}
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
// START: get primary key functions
|
||||
void getUidProcessKey(int uid, HashableDimensionKey* key) {
|
||||
int pos1[] = {1, 0, 0};
|
||||
Field field1(27 /* atom id */, pos1, 0 /* depth */);
|
||||
Value value1((int32_t)uid);
|
||||
|
||||
key->addValue(FieldValue(field1, value1));
|
||||
}
|
||||
|
||||
void getOverlayKey(int uid, string packageName, HashableDimensionKey* key) {
|
||||
int pos1[] = {1, 0, 0};
|
||||
int pos2[] = {2, 0, 0};
|
||||
|
||||
Field field1(59 /* atom id */, pos1, 0 /* depth */);
|
||||
Field field2(59 /* atom id */, pos2, 0 /* depth */);
|
||||
|
||||
Value value1((int32_t)uid);
|
||||
Value value2(packageName);
|
||||
|
||||
key->addValue(FieldValue(field1, value1));
|
||||
key->addValue(FieldValue(field2, value2));
|
||||
}
|
||||
|
||||
void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key) {
|
||||
int pos1[] = {1, 1, 1};
|
||||
int pos3[] = {2, 0, 0};
|
||||
int pos4[] = {3, 0, 0};
|
||||
|
||||
Field field1(10 /* atom id */, pos1, 2 /* depth */);
|
||||
|
||||
Field field3(10 /* atom id */, pos3, 0 /* depth */);
|
||||
Field field4(10 /* atom id */, pos4, 0 /* depth */);
|
||||
|
||||
Value value1((int32_t)uid);
|
||||
Value value3((int32_t)1 /*partial*/);
|
||||
Value value4(tag);
|
||||
|
||||
key->addValue(FieldValue(field1, value1));
|
||||
key->addValue(FieldValue(field3, value3));
|
||||
key->addValue(FieldValue(field4, value4));
|
||||
}
|
||||
|
||||
void getPartialWakelockKey(int uid, HashableDimensionKey* key) {
|
||||
int pos1[] = {1, 1, 1};
|
||||
int pos3[] = {2, 0, 0};
|
||||
|
||||
Field field1(10 /* atom id */, pos1, 2 /* depth */);
|
||||
Field field3(10 /* atom id */, pos3, 0 /* depth */);
|
||||
|
||||
Value value1((int32_t)uid);
|
||||
Value value3((int32_t)1 /*partial*/);
|
||||
|
||||
key->addValue(FieldValue(field1, value1));
|
||||
key->addValue(FieldValue(field3, value3));
|
||||
}
|
||||
// END: get primary key functions
|
||||
|
||||
shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
|
||||
int32_t value2) {
|
||||
AStatsEvent* statsEvent = AStatsEvent_obtain();
|
||||
@@ -595,6 +692,23 @@ std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) {
|
||||
return logEvent;
|
||||
}
|
||||
|
||||
std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestampNs, const BatteryPluggedStateEnum state) {
|
||||
AStatsEvent* statsEvent = AStatsEvent_obtain();
|
||||
AStatsEvent_setAtomId(statsEvent, util::PLUGGED_STATE_CHANGED);
|
||||
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
|
||||
|
||||
AStatsEvent_writeInt32(statsEvent, state);
|
||||
AStatsEvent_build(statsEvent);
|
||||
|
||||
size_t size;
|
||||
uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
|
||||
|
||||
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
|
||||
logEvent->parseBuffer(buf, size);
|
||||
AStatsEvent_release(statsEvent);
|
||||
return logEvent;
|
||||
}
|
||||
|
||||
std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level) {
|
||||
AStatsEvent* statsEvent = AStatsEvent_obtain();
|
||||
AStatsEvent_setAtomId(statsEvent, util::SCREEN_BRIGHTNESS_CHANGED);
|
||||
@@ -964,6 +1078,22 @@ int64_t StringToId(const string& str) {
|
||||
return static_cast<int64_t>(std::hash<std::string>()(str));
|
||||
}
|
||||
|
||||
void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
|
||||
const int uid, const string& tag) {
|
||||
EXPECT_EQ(value.field(), atomId);
|
||||
EXPECT_EQ(value.value_tuple().dimensions_value_size(), 2);
|
||||
// Attribution field.
|
||||
EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1);
|
||||
// Uid field.
|
||||
EXPECT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 1);
|
||||
EXPECT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1);
|
||||
EXPECT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(),
|
||||
uid);
|
||||
// Tag field.
|
||||
EXPECT_EQ(value.value_tuple().dimensions_value(1).field(), 3);
|
||||
EXPECT_EQ(value.value_tuple().dimensions_value(1).value_str(), tag);
|
||||
}
|
||||
|
||||
void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid) {
|
||||
EXPECT_EQ(value.field(), atomId);
|
||||
EXPECT_EQ(value.value_tuple().dimensions_value_size(), 1);
|
||||
|
||||
@@ -68,6 +68,12 @@ AtomMatcher CreateBatterySaverModeStartAtomMatcher();
|
||||
// Create AtomMatcher proto for stopping battery save mode.
|
||||
AtomMatcher CreateBatterySaverModeStopAtomMatcher();
|
||||
|
||||
// Create AtomMatcher proto for battery state none mode.
|
||||
AtomMatcher CreateBatteryStateNoneMatcher();
|
||||
|
||||
// Create AtomMatcher proto for battery state usb mode.
|
||||
AtomMatcher CreateBatteryStateUsbMatcher();
|
||||
|
||||
// Create AtomMatcher proto for process state changed.
|
||||
AtomMatcher CreateUidProcessStateChangedAtomMatcher();
|
||||
|
||||
@@ -110,6 +116,9 @@ Predicate CreateScheduledJobPredicate();
|
||||
// Create Predicate proto for battery saver mode.
|
||||
Predicate CreateBatterySaverModePredicate();
|
||||
|
||||
// Create Predicate proto for device unplogged mode.
|
||||
Predicate CreateDeviceUnpluggedPredicate();
|
||||
|
||||
// Create Predicate proto for holding wakelock.
|
||||
Predicate CreateHoldingWakelockPredicate();
|
||||
|
||||
@@ -164,6 +173,22 @@ FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
|
||||
FieldMatcher CreateAttributionUidDimensions(const int atomId,
|
||||
const std::vector<Position>& positions);
|
||||
|
||||
FieldMatcher CreateAttributionUidAndOtherDimensions(const int atomId,
|
||||
const std::vector<Position>& positions,
|
||||
const std::vector<int>& fields);
|
||||
|
||||
// START: get primary key functions
|
||||
// These functions take in atom field information and create FieldValues which are stored in the
|
||||
// given HashableDimensionKey.
|
||||
void getUidProcessKey(int uid, HashableDimensionKey* key);
|
||||
|
||||
void getOverlayKey(int uid, string packageName, HashableDimensionKey* key);
|
||||
|
||||
void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key);
|
||||
|
||||
void getPartialWakelockKey(int uid, HashableDimensionKey* key);
|
||||
// END: get primary key functions
|
||||
|
||||
shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
|
||||
int32_t value2);
|
||||
|
||||
@@ -213,6 +238,9 @@ std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs);
|
||||
// Create log event when battery saver stops.
|
||||
std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs);
|
||||
|
||||
// Create log event when battery state changes.
|
||||
std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestampNs, const BatteryPluggedStateEnum state);
|
||||
|
||||
// Create log event for app moving to background.
|
||||
std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid);
|
||||
|
||||
@@ -277,6 +305,8 @@ void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
|
||||
|
||||
int64_t StringToId(const string& str);
|
||||
|
||||
void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
|
||||
const int uid, const string& tag);
|
||||
void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid);
|
||||
void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid);
|
||||
void ValidateAttributionUidAndTagDimension(
|
||||
|
||||
Reference in New Issue
Block a user