Loading cmds/statsd/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -218,6 +218,7 @@ LOCAL_SRC_FILES := \ tests/metrics/metrics_test_helper.cpp \ tests/statsd_test_util.cpp \ tests/e2e/WakelockDuration_e2e_test.cpp \ tests/e2e/MetricActivation_e2e_test.cpp \ tests/e2e/MetricConditionLink_e2e_test.cpp \ tests/e2e/Alarm_e2e_test.cpp \ tests/e2e/Attribution_e2e_test.cpp \ Loading cmds/statsd/src/StatsLogProcessor.h +1 −0 Original line number Diff line number Diff line Loading @@ -219,6 +219,7 @@ private: FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric); FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); }; } // namespace statsd Loading cmds/statsd/src/metrics/MetricProducer.cpp +47 −1 Original line number Diff line number Diff line Loading @@ -64,9 +64,55 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo onMatchedLogEventInternalLocked( matcherIndex, metricKey, conditionKey, condition, event); } } bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) { bool isActive = mEventActivationMap.empty(); for (auto& it : mEventActivationMap) { if (it.second.state == ActivationState::kActive && elapsedTimestampNs > it.second.ttl_ns + it.second.activation_ns) { it.second.state = ActivationState::kNotActive; } if (it.second.state == ActivationState::kActive) { isActive = true; } } return isActive; } void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) { std::lock_guard<std::mutex> lock(mMutex); if (!mIsActive) { return; } mIsActive = evaluateActiveStateLocked(elapsedTimestampNs); if (!mIsActive) { flushLocked(elapsedTimestampNs); } } void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_seconds) { std::lock_guard<std::mutex> lock(mMutex); // When a metric producer does not depend on any activation, its mIsActive is true. // Therefor, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not // change. if (mEventActivationMap.empty()) { mIsActive = false; } mEventActivationMap[activationTrackerIndex].ttl_ns = ttl_seconds * NS_PER_SEC; } void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) { auto it = mEventActivationMap.find(activationTrackerIndex); if (it == mEventActivationMap.end()) { return; } it->second.activation_ns = elapsedTimestampNs; it->second.state = ActivationState::kActive; mIsActive = true; } } // namespace statsd } // namespace os } // namespace android cmds/statsd/src/metrics/MetricProducer.h +53 −7 Original line number Diff line number Diff line Loading @@ -34,6 +34,17 @@ namespace android { namespace os { namespace statsd { // If the metric has no activation requirement, it will be active once the metric producer is // created. // If the metric needs to be activated by atoms, the metric producer will start // with kNotActive state, turn to kActive when the activation event arrives, become kNotActive // when it reaches the duration limit (timebomb). If the activation event arrives again before // or after it expires, the event producer will be re-activated and ttl will be reset. enum ActivationState { kNotActive = 0, kActive = 1, }; // A MetricProducer is responsible for compute one single metrics, creating stats log report, and // writing the report to dropbox. MetricProducers should respond to package changes as required in // PackageInfoListener, but if none of the metrics are slicing by package name, then the update can Loading @@ -54,7 +65,8 @@ public: mContainANYPositionInDimensionsInWhat(false), mSliceByPositionALL(false), mSameConditionDimensionsInTracker(false), mHasLinksToAllConditionDimensionsInTracker(false) { mHasLinksToAllConditionDimensionsInTracker(false), mIsActive(true) { } virtual ~MetricProducer(){}; Loading Loading @@ -93,18 +105,24 @@ public: // Consume the parsed stats log entry that already matched the "what" of the metric. void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) { std::lock_guard<std::mutex> lock(mMutex); if (mIsActive) { onMatchedLogEventLocked(matcherIndex, event); } } void onConditionChanged(const bool condition, const int64_t eventTime) { std::lock_guard<std::mutex> lock(mMutex); if (mIsActive) { onConditionChangedLocked(condition, eventTime); } } void onSlicedConditionMayChange(bool overallCondition, const int64_t eventTime) { std::lock_guard<std::mutex> lock(mMutex); if (mIsActive) { onSlicedConditionMayChangeLocked(overallCondition, eventTime); } } bool isConditionSliced() const { std::lock_guard<std::mutex> lock(mMutex); Loading Loading @@ -177,6 +195,15 @@ public: return mCurrentBucketNum; } void activate(int activationTrackerIndex, int64_t elapsedTimestampNs) { std::lock_guard<std::mutex> lock(mMutex); activateLocked(activationTrackerIndex, elapsedTimestampNs); } void addActivation(int activationTrackerIndex, int64_t ttl_seconds); void flushIfExpire(int64_t elapsedTimestampNs); protected: virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0; virtual void onSlicedConditionMayChangeLocked(bool overallCondition, Loading @@ -189,6 +216,10 @@ protected: virtual size_t byteSizeLocked() const = 0; virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0; bool evaluateActiveStateLocked(int64_t elapsedTimestampNs); void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs); /** * Flushes the current bucket if the eventTime is after the current bucket's end time. This will also flush the current partial bucket in memory. Loading @@ -198,9 +229,9 @@ protected: /** * Flushes all the data including the current partial bucket. */ virtual void flushLocked(const int64_t& eventTime) { flushIfNeededLocked(eventTime); flushCurrentBucketLocked(eventTime); virtual void flushLocked(const int64_t& eventTimeNs) { flushIfNeededLocked(eventTimeNs); flushCurrentBucketLocked(eventTimeNs); }; /** Loading Loading @@ -295,6 +326,21 @@ protected: virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event); mutable std::mutex mMutex; struct Activation { Activation() : ttl_ns(0), activation_ns(0), state(ActivationState::kNotActive) {} int64_t ttl_ns; int64_t activation_ns; ActivationState state; }; // When the metric producer has multiple activations, these activations are ORed to determine // whether the metric producer is ready to generate metrics. std::unordered_map<int, Activation> mEventActivationMap; bool mIsActive; FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); }; } // namespace statsd Loading cmds/statsd/src/metrics/MetricsManager.cpp +18 −4 Original line number Diff line number Diff line Loading @@ -73,7 +73,8 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, key, config, *uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds); mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, mNoReportMetricIds); mHashStringsInReport = config.hash_strings_in_metric_report(); Loading Loading @@ -298,7 +299,12 @@ void MetricsManager::onLogEvent(const LogEvent& event) { } int tagId = event.GetTagId(); int64_t eventTime = event.GetElapsedTimestampNs(); int64_t eventTimeNs = event.GetElapsedTimestampNs(); for (int metric : mMetricIndexesWithActivation) { mAllMetricProducers[metric]->flushIfExpire(eventTimeNs); } if (mTagIds.find(tagId) == mTagIds.end()) { // not interesting... return; Loading @@ -310,6 +316,14 @@ void MetricsManager::onLogEvent(const LogEvent& event) { matcher->onLogEvent(event, mAllAtomMatchers, matcherCache); } for (const auto& it : mActivationAtomTrackerToMetricMap) { if (matcherCache[it.first] == MatchingState::kMatched) { for (int metricIndex : it.second) { mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs); } } } // A bitmap to see which ConditionTracker needs to be re-evaluated. vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false); Loading Loading @@ -347,13 +361,13 @@ void MetricsManager::onLogEvent(const LogEvent& event) { // Push the new condition to it directly. if (!mAllMetricProducers[metricIndex]->isConditionSliced()) { mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i], eventTime); eventTimeNs); // metric cares about sliced conditions, and it may have changed. Send // notification, and the metric can query the sliced conditions that are // interesting to it. } else { mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i], eventTime); eventTimeNs); } } } Loading Loading
cmds/statsd/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -218,6 +218,7 @@ LOCAL_SRC_FILES := \ tests/metrics/metrics_test_helper.cpp \ tests/statsd_test_util.cpp \ tests/e2e/WakelockDuration_e2e_test.cpp \ tests/e2e/MetricActivation_e2e_test.cpp \ tests/e2e/MetricConditionLink_e2e_test.cpp \ tests/e2e/Alarm_e2e_test.cpp \ tests/e2e/Attribution_e2e_test.cpp \ Loading
cmds/statsd/src/StatsLogProcessor.h +1 −0 Original line number Diff line number Diff line Loading @@ -219,6 +219,7 @@ private: FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric); FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); }; } // namespace statsd Loading
cmds/statsd/src/metrics/MetricProducer.cpp +47 −1 Original line number Diff line number Diff line Loading @@ -64,9 +64,55 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo onMatchedLogEventInternalLocked( matcherIndex, metricKey, conditionKey, condition, event); } } bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) { bool isActive = mEventActivationMap.empty(); for (auto& it : mEventActivationMap) { if (it.second.state == ActivationState::kActive && elapsedTimestampNs > it.second.ttl_ns + it.second.activation_ns) { it.second.state = ActivationState::kNotActive; } if (it.second.state == ActivationState::kActive) { isActive = true; } } return isActive; } void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) { std::lock_guard<std::mutex> lock(mMutex); if (!mIsActive) { return; } mIsActive = evaluateActiveStateLocked(elapsedTimestampNs); if (!mIsActive) { flushLocked(elapsedTimestampNs); } } void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_seconds) { std::lock_guard<std::mutex> lock(mMutex); // When a metric producer does not depend on any activation, its mIsActive is true. // Therefor, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not // change. if (mEventActivationMap.empty()) { mIsActive = false; } mEventActivationMap[activationTrackerIndex].ttl_ns = ttl_seconds * NS_PER_SEC; } void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) { auto it = mEventActivationMap.find(activationTrackerIndex); if (it == mEventActivationMap.end()) { return; } it->second.activation_ns = elapsedTimestampNs; it->second.state = ActivationState::kActive; mIsActive = true; } } // namespace statsd } // namespace os } // namespace android
cmds/statsd/src/metrics/MetricProducer.h +53 −7 Original line number Diff line number Diff line Loading @@ -34,6 +34,17 @@ namespace android { namespace os { namespace statsd { // If the metric has no activation requirement, it will be active once the metric producer is // created. // If the metric needs to be activated by atoms, the metric producer will start // with kNotActive state, turn to kActive when the activation event arrives, become kNotActive // when it reaches the duration limit (timebomb). If the activation event arrives again before // or after it expires, the event producer will be re-activated and ttl will be reset. enum ActivationState { kNotActive = 0, kActive = 1, }; // A MetricProducer is responsible for compute one single metrics, creating stats log report, and // writing the report to dropbox. MetricProducers should respond to package changes as required in // PackageInfoListener, but if none of the metrics are slicing by package name, then the update can Loading @@ -54,7 +65,8 @@ public: mContainANYPositionInDimensionsInWhat(false), mSliceByPositionALL(false), mSameConditionDimensionsInTracker(false), mHasLinksToAllConditionDimensionsInTracker(false) { mHasLinksToAllConditionDimensionsInTracker(false), mIsActive(true) { } virtual ~MetricProducer(){}; Loading Loading @@ -93,18 +105,24 @@ public: // Consume the parsed stats log entry that already matched the "what" of the metric. void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) { std::lock_guard<std::mutex> lock(mMutex); if (mIsActive) { onMatchedLogEventLocked(matcherIndex, event); } } void onConditionChanged(const bool condition, const int64_t eventTime) { std::lock_guard<std::mutex> lock(mMutex); if (mIsActive) { onConditionChangedLocked(condition, eventTime); } } void onSlicedConditionMayChange(bool overallCondition, const int64_t eventTime) { std::lock_guard<std::mutex> lock(mMutex); if (mIsActive) { onSlicedConditionMayChangeLocked(overallCondition, eventTime); } } bool isConditionSliced() const { std::lock_guard<std::mutex> lock(mMutex); Loading Loading @@ -177,6 +195,15 @@ public: return mCurrentBucketNum; } void activate(int activationTrackerIndex, int64_t elapsedTimestampNs) { std::lock_guard<std::mutex> lock(mMutex); activateLocked(activationTrackerIndex, elapsedTimestampNs); } void addActivation(int activationTrackerIndex, int64_t ttl_seconds); void flushIfExpire(int64_t elapsedTimestampNs); protected: virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0; virtual void onSlicedConditionMayChangeLocked(bool overallCondition, Loading @@ -189,6 +216,10 @@ protected: virtual size_t byteSizeLocked() const = 0; virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0; bool evaluateActiveStateLocked(int64_t elapsedTimestampNs); void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs); /** * Flushes the current bucket if the eventTime is after the current bucket's end time. This will also flush the current partial bucket in memory. Loading @@ -198,9 +229,9 @@ protected: /** * Flushes all the data including the current partial bucket. */ virtual void flushLocked(const int64_t& eventTime) { flushIfNeededLocked(eventTime); flushCurrentBucketLocked(eventTime); virtual void flushLocked(const int64_t& eventTimeNs) { flushIfNeededLocked(eventTimeNs); flushCurrentBucketLocked(eventTimeNs); }; /** Loading Loading @@ -295,6 +326,21 @@ protected: virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event); mutable std::mutex mMutex; struct Activation { Activation() : ttl_ns(0), activation_ns(0), state(ActivationState::kNotActive) {} int64_t ttl_ns; int64_t activation_ns; ActivationState state; }; // When the metric producer has multiple activations, these activations are ORed to determine // whether the metric producer is ready to generate metrics. std::unordered_map<int, Activation> mEventActivationMap; bool mIsActive; FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); }; } // namespace statsd Loading
cmds/statsd/src/metrics/MetricsManager.cpp +18 −4 Original line number Diff line number Diff line Loading @@ -73,7 +73,8 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, key, config, *uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds); mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, mNoReportMetricIds); mHashStringsInReport = config.hash_strings_in_metric_report(); Loading Loading @@ -298,7 +299,12 @@ void MetricsManager::onLogEvent(const LogEvent& event) { } int tagId = event.GetTagId(); int64_t eventTime = event.GetElapsedTimestampNs(); int64_t eventTimeNs = event.GetElapsedTimestampNs(); for (int metric : mMetricIndexesWithActivation) { mAllMetricProducers[metric]->flushIfExpire(eventTimeNs); } if (mTagIds.find(tagId) == mTagIds.end()) { // not interesting... return; Loading @@ -310,6 +316,14 @@ void MetricsManager::onLogEvent(const LogEvent& event) { matcher->onLogEvent(event, mAllAtomMatchers, matcherCache); } for (const auto& it : mActivationAtomTrackerToMetricMap) { if (matcherCache[it.first] == MatchingState::kMatched) { for (int metricIndex : it.second) { mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs); } } } // A bitmap to see which ConditionTracker needs to be re-evaluated. vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false); Loading Loading @@ -347,13 +361,13 @@ void MetricsManager::onLogEvent(const LogEvent& event) { // Push the new condition to it directly. if (!mAllMetricProducers[metricIndex]->isConditionSliced()) { mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i], eventTime); eventTimeNs); // metric cares about sliced conditions, and it may have changed. Send // notification, and the metric can query the sliced conditions that are // interesting to it. } else { mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i], eventTime); eventTimeNs); } } } Loading