Merge "Slice by state in DurationMetricProducer" into rvc-dev am: 8ad7c0ec42

Change-Id: I7c77e56f533b42dbffb4a55138523d2340a19554
This commit is contained in:
Christine Tsai
2020-04-03 00:48:35 +00:00
committed by Automerger Merge Worker
25 changed files with 1517 additions and 224 deletions

View File

@@ -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",

View File

@@ -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();

View File

@@ -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

View File

@@ -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);

View File

@@ -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"];

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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()) {

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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];
}

View File

@@ -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 {

View 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

View File

@@ -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

View File

@@ -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);

View File

@@ -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));

View File

@@ -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;

View File

@@ -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);

View File

@@ -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(