diff options
-rw-r--r-- | cmds/statsd/src/condition/ConditionTracker.h | 6 | ||||
-rw-r--r-- | cmds/statsd/src/metrics/EventMetricProducer.cpp | 47 | ||||
-rw-r--r-- | cmds/statsd/src/metrics/EventMetricProducer.h | 16 | ||||
-rw-r--r-- | cmds/statsd/src/metrics/MetricProducer.cpp | 32 | ||||
-rw-r--r-- | cmds/statsd/src/metrics/MetricProducer.h | 50 | ||||
-rw-r--r-- | cmds/statsd/src/metrics/MetricsManager.cpp | 21 | ||||
-rw-r--r-- | cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp | 247 | ||||
-rw-r--r-- | cmds/statsd/src/metrics/parsing_utils/config_update_utils.h | 79 | ||||
-rw-r--r-- | cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp | 301 | ||||
-rw-r--r-- | cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h | 63 | ||||
-rw-r--r-- | cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp | 599 |
11 files changed, 1308 insertions, 153 deletions
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 3bf4e636be89..5a6b8cf334eb 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -68,9 +68,9 @@ public: // index: the new index of this tracker in allConditionProtos and allConditionTrackers. // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also // need to call onConfigUpdated() on child conditions) - // [atomMatchingTrackerMap]: map of atom matcher id to index after the config update - // [conditionTrackerMap]: map of condition tracker id to index after the config update. - // returns whether or not the update is successful + // atomMatchingTrackerMap: map of atom matcher id to index after the config update. + // conditionTrackerMap: map of condition tracker id to index after the config update. + // returns whether or not the update is successful. virtual bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index, const std::vector<sp<ConditionTracker>>& allConditionTrackers, const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index dfe4559b05fb..ca302c0e71fb 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -18,12 +18,14 @@ #include "Log.h" #include "EventMetricProducer.h" -#include "stats_util.h" -#include "stats_log_util.h" #include <limits.h> #include <stdlib.h> +#include "metrics/parsing_utils/metrics_manager_util.h" +#include "stats_log_util.h" +#include "stats_util.h" + using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_BOOL; using android::util::FIELD_TYPE_FLOAT; @@ -82,6 +84,47 @@ EventMetricProducer::~EventMetricProducer() { VLOG("~EventMetricProducer() called"); } +bool EventMetricProducer::onConfigUpdatedLocked( + const StatsdConfig& config, const int configIndex, const int metricIndex, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const sp<EventMatcherWizard>& matcherWizard, + const vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard, + const unordered_map<int64_t, int>& metricToActivationMap, + unordered_map<int, vector<int>>& trackerToMetricMap, + unordered_map<int, vector<int>>& conditionToMetricMap, + unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation) { + if (!MetricProducer::onConfigUpdatedLocked( + config, configIndex, metricIndex, allAtomMatchingTrackers, + oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, + allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, + trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation)) { + return false; + } + + const EventMetric& metric = config.event_metric(configIndex); + int trackerIndex; + // Update appropriate indices, specifically mConditionIndex and MetricsManager maps. + if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false, + allAtomMatchingTrackers, newAtomMatchingTrackerMap, + trackerToMetricMap, trackerIndex)) { + return false; + } + + if (metric.has_condition() && + !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, + metric.links(), allConditionTrackers, mConditionTrackerIndex, + conditionToMetricMap)) { + return false; + } + return true; +} + void EventMetricProducer::dropDataLocked(const int64_t dropTimeNs) { mProto->clear(); StatsdStats::getInstance().noteBucketDropped(mMetricId); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index e828dddcbb18..3347d7b6aab5 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -69,6 +69,22 @@ private: // Internal interface to handle sliced condition change. void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override; + bool onConfigUpdatedLocked( + const StatsdConfig& config, const int configIndex, const int metricIndex, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const sp<EventMatcherWizard>& matcherWizard, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& conditionTrackerMap, + const sp<ConditionWizard>& wizard, + const std::unordered_map<int64_t, int>& metricToActivationMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::vector<int>& metricsWithActivation) override; + void dropDataLocked(const int64_t dropTimeNs) override; // Internal function to calculate the current used bytes. diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index c1c1d20f00e2..95a7d40ea9a9 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -20,6 +20,7 @@ #include "MetricProducer.h" #include "../guardrail/StatsdStats.h" +#include "metrics/parsing_utils/metrics_manager_util.h" #include "state/StateTracker.h" using android::util::FIELD_COUNT_REPEATED; @@ -72,6 +73,37 @@ MetricProducer::MetricProducer( mStateGroupMap(stateGroupMap) { } +bool MetricProducer::onConfigUpdatedLocked( + const StatsdConfig& config, const int configIndex, const int metricIndex, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const sp<EventMatcherWizard>& matcherWizard, + const vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard, + const unordered_map<int64_t, int>& metricToActivationMap, + unordered_map<int, vector<int>>& trackerToMetricMap, + unordered_map<int, vector<int>>& conditionToMetricMap, + unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation) { + sp<ConditionWizard> tmpWizard = mWizard; + mWizard = wizard; + + unordered_map<int, shared_ptr<Activation>> newEventActivationMap; + unordered_map<int, vector<shared_ptr<Activation>>> newEventDeactivationMap; + if (!handleMetricActivationOnConfigUpdate( + config, mMetricId, metricIndex, metricToActivationMap, oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, mEventActivationMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, newEventActivationMap, + newEventDeactivationMap)) { + return false; + } + mEventActivationMap = newEventActivationMap; + mEventDeactivationMap = newEventDeactivationMap; + return true; +} + void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { if (!mIsActive) { return; diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index bb590aac54d6..320b28581a38 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -26,6 +26,7 @@ #include "anomaly/AnomalyTracker.h" #include "condition/ConditionWizard.h" #include "config/ConfigKey.h" +#include "matchers/EventMatcherWizard.h" #include "matchers/matcher_util.h" #include "packages/PackageInfoListener.h" #include "state/StateListener.h" @@ -151,6 +152,33 @@ public: return conditionIndex >= 0 ? initialConditionCache[conditionIndex] : ConditionState::kTrue; } + // Update appropriate state on config updates. Primarily, all indices need to be updated. + // This metric and all of its dependencies are guaranteed to be preserved across the update. + // This function also updates several maps used by metricsManager. + bool onConfigUpdated( + const StatsdConfig& config, const int configIndex, const int metricIndex, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const sp<EventMatcherWizard>& matcherWizard, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& conditionTrackerMap, + const sp<ConditionWizard>& wizard, + const std::unordered_map<int64_t, int>& metricToActivationMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::vector<int>& metricsWithActivation) { + std::lock_guard<std::mutex> lock(mMutex); + return onConfigUpdatedLocked(config, configIndex, metricIndex, allAtomMatchingTrackers, + oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, + matcherWizard, allConditionTrackers, conditionTrackerMap, + wizard, metricToActivationMap, trackerToMetricMap, + conditionToMetricMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation); + }; + /** * Force a partial bucket split on app upgrade */ @@ -209,6 +237,25 @@ public: dumpLatency, str_set, protoOutput); } + // Update appropriate state on config updates. Primarily, all indices need to be updated. + // This metric and all of its dependencies are guaranteed to be preserved across the update. + // This function also updates several maps used by metricsManager. + virtual bool onConfigUpdatedLocked( + const StatsdConfig& config, const int configIndex, const int metricIndex, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const sp<EventMatcherWizard>& matcherWizard, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& conditionTrackerMap, + const sp<ConditionWizard>& wizard, + const std::unordered_map<int64_t, int>& metricToActivationMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::vector<int>& metricsWithActivation); + void clearPastBuckets(const int64_t dumpTimeNs) { std::lock_guard<std::mutex> lock(mMutex); return clearPastBucketsLocked(dumpTimeNs); @@ -517,6 +564,9 @@ protected: FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges); FRIEND_TEST(MetricsManagerTest, TestInitialConditions); + + FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricActivations); + FRIEND_TEST(ConfigUpdateTest, TestUpdateEventMetrics); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 39806890c42d..ab0d286d6b29 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -206,18 +206,33 @@ bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t time unordered_map<int64_t, int> newAtomMatchingTrackerMap; vector<sp<ConditionTracker>> newConditionTrackers; unordered_map<int64_t, int> newConditionTrackerMap; + map<int64_t, uint64_t> newStateProtoHashes; + vector<sp<MetricProducer>> newMetricProducers; + unordered_map<int64_t, int> newMetricProducerMap; mTagIds.clear(); + mConditionToMetricMap.clear(); + mTrackerToMetricMap.clear(); mTrackerToConditionMap.clear(); + mActivationAtomTrackerToMetricMap.clear(); + mDeactivationAtomTrackerToMetricMap.clear(); + mMetricIndexesWithActivation.clear(); + mNoReportMetricIds.clear(); mConfigValid = updateStatsdConfig( mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap, - mAllConditionTrackers, mConditionTrackerMap, mTagIds, newAtomMatchingTrackers, - newAtomMatchingTrackerMap, newConditionTrackers, newConditionTrackerMap, - mTrackerToConditionMap); + mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap, + mStateProtoHashes, mTagIds, newAtomMatchingTrackers, newAtomMatchingTrackerMap, + newConditionTrackers, newConditionTrackerMap, newMetricProducers, newMetricProducerMap, + mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, + mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap, + mMetricIndexesWithActivation, newStateProtoHashes, mNoReportMetricIds); mAllAtomMatchingTrackers = newAtomMatchingTrackers; mAtomMatchingTrackerMap = newAtomMatchingTrackerMap; mAllConditionTrackers = newConditionTrackers; mConditionTrackerMap = newConditionTrackerMap; + mAllMetricProducers = newMetricProducers; + mMetricProducerMap = newMetricProducerMap; + mStateProtoHashes = newStateProtoHashes; return mConfigValid; } diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp index bd60b6bfcb8e..5dbf16deb552 100644 --- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp @@ -21,6 +21,7 @@ #include "external/StatsPullerManager.h" #include "hash.h" +#include "matchers/EventMatcherWizard.h" #include "metrics_manager_util.h" namespace android { @@ -394,6 +395,210 @@ bool updateConditions(const ConfigKey& key, const StatsdConfig& config, return true; } +// Returns true if any matchers in the metric activation were replaced. +bool metricActivationDepsChange(const StatsdConfig& config, + const unordered_map<int64_t, int>& metricToActivationMap, + const int64_t metricId, const set<int64_t>& replacedMatchers) { + const auto& metricActivationIt = metricToActivationMap.find(metricId); + if (metricActivationIt == metricToActivationMap.end()) { + return false; + } + const MetricActivation& metricActivation = config.metric_activation(metricActivationIt->second); + for (int i = 0; i < metricActivation.event_activation_size(); i++) { + const EventActivation& activation = metricActivation.event_activation(i); + if (replacedMatchers.find(activation.atom_matcher_id()) != replacedMatchers.end()) { + return true; + } + if (activation.has_deactivation_atom_matcher_id()) { + if (replacedMatchers.find(activation.deactivation_atom_matcher_id()) != + replacedMatchers.end()) { + return true; + } + } + } + return false; +} + +bool determineEventMetricUpdateStatus(const StatsdConfig& config, const EventMetric& metric, + const unordered_map<int64_t, int>& oldMetricProducerMap, + const vector<sp<MetricProducer>>& oldMetricProducers, + const unordered_map<int64_t, int>& metricToActivationMap, + const set<int64_t>& replacedMatchers, + const set<int64_t>& replacedConditions, + UpdateStatus& updateStatus) { + int64_t id = metric.id(); + // Check if new metric + const auto& oldMetricProducerIt = oldMetricProducerMap.find(id); + if (oldMetricProducerIt == oldMetricProducerMap.end()) { + updateStatus = UPDATE_NEW; + return true; + } + + // This is an existing metric, check if it has changed. + uint64_t metricHash; + if (!getMetricProtoHash(config, metric, id, metricToActivationMap, metricHash)) { + return false; + } + const sp<MetricProducer> oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second]; + if (oldMetricProducer->getMetricType() != METRIC_TYPE_EVENT || + oldMetricProducer->getProtoHash() != metricHash) { + updateStatus = UPDATE_REPLACE; + return true; + } + + // Metric type and definition are the same. Need to check dependencies to see if they changed. + if (replacedMatchers.find(metric.what()) != replacedMatchers.end()) { + updateStatus = UPDATE_REPLACE; + return true; + } + + if (metric.has_condition()) { + if (replacedConditions.find(metric.condition()) != replacedConditions.end()) { + updateStatus = UPDATE_REPLACE; + return true; + } + } + + if (metricActivationDepsChange(config, metricToActivationMap, id, replacedMatchers)) { + updateStatus = UPDATE_REPLACE; + return true; + } + + for (const auto& metricConditionLink : metric.links()) { + if (replacedConditions.find(metricConditionLink.condition()) != replacedConditions.end()) { + updateStatus = UPDATE_REPLACE; + return true; + } + } + updateStatus = UPDATE_PRESERVE; + return true; +} + +bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, + const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const set<int64_t>& replacedMatchers, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& conditionTrackerMap, + const set<int64_t>& replacedConditions, + vector<sp<ConditionTracker>>& allConditionTrackers, + const vector<ConditionState>& initialConditionCache, + const unordered_map<int64_t, int>& stateAtomIdMap, + const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, + const set<int64_t>& replacedStates, + const unordered_map<int64_t, int>& oldMetricProducerMap, + const vector<sp<MetricProducer>>& oldMetricProducers, + unordered_map<int64_t, int>& newMetricProducerMap, + vector<sp<MetricProducer>>& newMetricProducers, + unordered_map<int, vector<int>>& conditionToMetricMap, + unordered_map<int, vector<int>>& trackerToMetricMap, + set<int64_t>& noReportMetricIds, + unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation) { + sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers); + sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers); + const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() + + config.event_metric_size() + config.gauge_metric_size() + + config.value_metric_size(); + newMetricProducers.reserve(allMetricsCount); + + // Construct map from metric id to metric activation index. The map will be used to determine + // the metric activation corresponding to a metric. + unordered_map<int64_t, int> metricToActivationMap; + for (int i = 0; i < config.metric_activation_size(); i++) { + const MetricActivation& metricActivation = config.metric_activation(i); + int64_t metricId = metricActivation.metric_id(); + if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) { + ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId); + return false; + } + metricToActivationMap.insert({metricId, i}); + } + + vector<UpdateStatus> metricsToUpdate(allMetricsCount, UPDATE_UNKNOWN); + int metricIndex = 0; + for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) { + newMetricProducerMap[config.event_metric(i).id()] = metricIndex; + if (!determineEventMetricUpdateStatus(config, config.event_metric(i), oldMetricProducerMap, + oldMetricProducers, metricToActivationMap, + replacedMatchers, replacedConditions, + metricsToUpdate[metricIndex])) { + return false; + } + } + + // TODO: determine update status for count, gauge, value, duration metrics. + + // Now, perform the update. Must iterate the metric types in the same order + metricIndex = 0; + for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) { + const EventMetric& metric = config.event_metric(i); + switch (metricsToUpdate[metricIndex]) { + case UPDATE_PRESERVE: { + const auto& oldMetricProducerIt = oldMetricProducerMap.find(metric.id()); + if (oldMetricProducerIt == oldMetricProducerMap.end()) { + ALOGE("Could not find Metric %lld in the previous config, but expected it " + "to be there", + (long long)metric.id()); + return false; + } + const int oldIndex = oldMetricProducerIt->second; + sp<MetricProducer> producer = oldMetricProducers[oldIndex]; + producer->onConfigUpdated( + config, i, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, + conditionTrackerMap, wizard, metricToActivationMap, trackerToMetricMap, + conditionToMetricMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation); + newMetricProducers.push_back(producer); + break; + } + case UPDATE_REPLACE: + case UPDATE_NEW: { + sp<MetricProducer> producer = createEventMetricProducerAndUpdateMetadata( + key, config, timeBaseNs, metric, metricIndex, allAtomMatchingTrackers, + newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, + initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap, + conditionToMetricMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation); + if (producer == nullptr) { + return false; + } + newMetricProducers.push_back(producer); + break; + } + default: { + ALOGE("Metric \"%lld\" update state is unknown. This should never happen", + (long long)metric.id()); + return false; + } + } + } + // TODO: perform update for count, gauge, value, duration metric. + + const set<int> atomsAllowedFromAnyUid(config.whitelisted_atom_ids().begin(), + config.whitelisted_atom_ids().end()); + for (int i = 0; i < allMetricsCount; i++) { + sp<MetricProducer> producer = newMetricProducers[i]; + // Register metrics to StateTrackers + for (int atomId : producer->getSlicedStateAtoms()) { + // Register listener for atoms that use allowed_log_sources. + // Using atoms allowed from any uid as a sliced state atom is not allowed. + // Redo this check for all metrics in case the atoms allowed from any uid changed. + if (atomsAllowedFromAnyUid.find(atomId) != atomsAllowedFromAnyUid.end()) { + return false; + // Preserved metrics should've already registered.` + } else if (metricsToUpdate[i] != UPDATE_PRESERVE) { + StateManager::getInstance().registerListener(atomId, producer); + } + } + } + + return true; +} + bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor, @@ -403,15 +608,28 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, const vector<sp<ConditionTracker>>& oldConditionTrackers, const unordered_map<int64_t, int>& oldConditionTrackerMap, - set<int>& allTagIds, + const vector<sp<MetricProducer>>& oldMetricProducers, + const unordered_map<int64_t, int>& oldMetricProducerMap, + const map<int64_t, uint64_t>& oldStateProtoHashes, set<int>& allTagIds, vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, unordered_map<int64_t, int>& newAtomMatchingTrackerMap, vector<sp<ConditionTracker>>& newConditionTrackers, unordered_map<int64_t, int>& newConditionTrackerMap, - unordered_map<int, vector<int>>& trackerToConditionMap) { + vector<sp<MetricProducer>>& newMetricProducers, + unordered_map<int64_t, int>& newMetricProducerMap, + unordered_map<int, vector<int>>& conditionToMetricMap, + unordered_map<int, vector<int>>& trackerToMetricMap, + unordered_map<int, vector<int>>& trackerToConditionMap, + unordered_map<int, vector<int>>& activationTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationTrackerToMetricMap, + vector<int>& metricsWithActivation, + map<int64_t, uint64_t>& newStateProtoHashes, + set<int64_t>& noReportMetricIds) { set<int64_t> replacedMatchers; set<int64_t> replacedConditions; vector<ConditionState> conditionCache; + unordered_map<int64_t, int> stateAtomIdMap; + unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; if (!updateAtomMatchingTrackers(config, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, allTagIds, newAtomMatchingTrackerMap, @@ -430,6 +648,31 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const } VLOG("updateConditions succeeded"); + // Share with metrics_manager_util, + if (!initStates(config, stateAtomIdMap, allStateGroupMaps, newStateProtoHashes)) { + ALOGE("initStates failed"); + return false; + } + + set<int64_t> replacedStates; + for (const auto& [stateId, stateHash] : oldStateProtoHashes) { + const auto& it = newStateProtoHashes.find(stateId); + if (it != newStateProtoHashes.end() && it->second != stateHash) { + replacedStates.insert(stateId); + } + } + if (!updateMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, + oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, + newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, + newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, + replacedStates, oldMetricProducerMap, oldMetricProducers, + newMetricProducerMap, newMetricProducers, conditionToMetricMap, + trackerToMetricMap, noReportMetricIds, activationTrackerToMetricMap, + deactivationTrackerToMetricMap, metricsWithActivation)) { + ALOGE("initMetricProducers failed"); + return false; + } + return true; } diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h index 7ba684a65e88..1cd0ce524b31 100644 --- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h +++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h @@ -22,6 +22,7 @@ #include "condition/ConditionTracker.h" #include "external/StatsPullerManager.h" #include "matchers/AtomMatchingTracker.h" +#include "metrics/MetricProducer.h" namespace android { namespace os { @@ -112,7 +113,7 @@ bool determineConditionUpdateStatus(const StatsdConfig& config, const int condit // [trackerToConditionMap]: contains the mapping from the index of an atom matcher // to indices of condition trackers that use the matcher // [conditionCache]: stores the current conditions for each ConditionTracker -// [replacedConditions]: set of matcher ids that have changed and have been replaced +// [replacedConditions]: set of condition ids that have changed and have been replaced bool updateConditions(const ConfigKey& key, const StatsdConfig& config, const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, const std::set<int64_t>& replacedMatchers, @@ -124,6 +125,68 @@ bool updateConditions(const ConfigKey& key, const StatsdConfig& config, std::vector<ConditionState>& conditionCache, std::set<int64_t>& replacedConditions); +// Function to determine if an event metric needs to be updated. Populates updateStatus. +// [config]: the input StatsdConfig +// [metric]: the current metric to be updated +// [oldMetricProducerMap]: metric id to index mapping in the existing MetricsManager +// [oldMetricProducers]: stores the existing MetricProducers +// [metricToActivationMap]: map from metric id to metric activation index. +// [replacedMatchers]: set of replaced matcher ids. conditions using these matchers must be replaced +// output: +// [updateStatus]: update status for the metric. Will be changed from UPDATE_UNKNOWN after this call +// Returns whether the function was successful or not. +bool determineEventMetricUpdateStatus(const StatsdConfig& config, const EventMetric& metric, + const unordered_map<int64_t, int>& oldMetricProducerMap, + const vector<sp<MetricProducer>>& oldMetricProducers, + const unordered_map<int64_t, int>& metricToActivationMap, + const set<int64_t>& replacedMatchers, + const set<int64_t>& replacedConditions, + UpdateStatus& updateStatus); + +// Update MetricProducers. +// input: +// [key]: the config key that this config belongs to +// [config]: the input config +// [timeBaseNs]: start time base for all metrics +// [currentTimeNs]: time of the config update +// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step. +// [replacedMatchers]: ids of replaced matchers. Metrics depending on these must also be replaced +// [allAtomMatchingTrackers]: stores the sp of the atom matchers. +// [conditionTrackerMap]: condition name to index mapping +// [replacedConditions]: set of condition ids that have changed and have been replaced +// [stateAtomIdMap]: contains the mapping from state ids to atom ids +// [allStateGroupMaps]: contains the mapping from atom ids and state values to +// state group ids for all states +// output: +// [allMetricProducers]: contains the list of sp to the MetricProducers created. +// [conditionToMetricMap]: contains the mapping from condition tracker index to +// the list of MetricProducer index +// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index. +bool updateMetrics( + const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, + const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, + const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const std::set<int64_t>& replacedMatchers, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& conditionTrackerMap, + const std::set<int64_t>& replacedConditions, + std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::vector<ConditionState>& initialConditionCache, + const std::unordered_map<int64_t, int>& stateAtomIdMap, + const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps, + const std::set<int64_t>& replacedStates, + const std::unordered_map<int64_t, int>& oldMetricProducerMap, + const std::vector<sp<MetricProducer>>& oldMetricProducers, + std::unordered_map<int64_t, int>& newMetricProducerMap, + std::vector<sp<MetricProducer>>& newMetricProducers, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::set<int64_t>& noReportMetricIds, + std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::vector<int>& metricsWithActivation); + // Updates the existing MetricsManager from a new StatsdConfig. // Parameters are the members of MetricsManager. See MetricsManager for declaration. bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, @@ -135,12 +198,24 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, const std::vector<sp<ConditionTracker>>& oldConditionTrackers, const std::unordered_map<int64_t, int>& oldConditionTrackerMap, + const std::vector<sp<MetricProducer>>& oldMetricProducers, + const std::unordered_map<int64_t, int>& oldMetricProducerMap, + const std::map<int64_t, uint64_t>& oldStateProtoHashes, std::set<int>& allTagIds, std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, std::vector<sp<ConditionTracker>>& newConditionTrackers, std::unordered_map<int64_t, int>& newConditionTrackerMap, - std::unordered_map<int, std::vector<int>>& trackerToConditionMap); + std::vector<sp<MetricProducer>>& newMetricProducers, + std::unordered_map<int64_t, int>& newMetricProducerMap, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::unordered_map<int, std::vector<int>>& trackerToConditionMap, + std::unordered_map<int, std::vector<int>>& activationTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationTrackerToMetricMap, + std::vector<int>& metricsWithActivation, + std::map<int64_t, uint64_t>& newStateProtoHashes, + std::set<int64_t>& noReportMetricIds); } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp index 3f40c90d515a..51df7df5174e 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp @@ -61,20 +61,6 @@ bool hasLeafNode(const FieldMatcher& matcher) { return true; } -bool getMetricProtoHash(const MessageLite& metric, const int64_t id, const bool hasActivation, - const uint64_t activationHash, uint64_t& metricHash) { - string serializedMetric; - if (!metric.SerializeToString(&serializedMetric)) { - ALOGE("Unable to serialize metric %lld", (long long)id); - return false; - } - metricHash = Hash64(serializedMetric); - if (hasActivation) { - metricHash = Hash64(to_string(metricHash).append(to_string(activationHash))); - } - return true; -} - } // namespace sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index, @@ -120,43 +106,45 @@ sp<ConditionTracker> createConditionTracker( } } -bool handleMetricWithAtomMatchingTrackers( - const int64_t what, const int metricIndex, const bool usedForDimension, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex) { - auto logTrackerIt = atomMatchingTrackerMap.find(what); - if (logTrackerIt == atomMatchingTrackerMap.end()) { - ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)what); +bool getMetricProtoHash(const StatsdConfig& config, const MessageLite& metric, const int64_t id, + const unordered_map<int64_t, int>& metricToActivationMap, + uint64_t& metricHash) { + string serializedMetric; + if (!metric.SerializeToString(&serializedMetric)) { + ALOGE("Unable to serialize metric %lld", (long long)id); return false; } - if (usedForDimension && - allAtomMatchingTrackers[logTrackerIt->second]->getAtomIds().size() > 1) { - ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, " - "the \"what\" can only about one atom type.", - (long long)what); - return false; + metricHash = Hash64(serializedMetric); + + // Combine with activation hash, if applicable + const auto& metricActivationIt = metricToActivationMap.find(id); + if (metricActivationIt != metricToActivationMap.end()) { + string serializedActivation; + const MetricActivation& activation = config.metric_activation(metricActivationIt->second); + if (!activation.SerializeToString(&serializedActivation)) { + ALOGE("Unable to serialize metric activation for metric %lld", (long long)id); + return false; + } + metricHash = Hash64(to_string(metricHash).append(to_string(Hash64(serializedActivation)))); } - logTrackerIndex = logTrackerIt->second; - auto& metric_list = trackerToMetricMap[logTrackerIndex]; - metric_list.push_back(metricIndex); return true; } -bool handlePullMetricTriggerWithAtomMatchingTrackers( - const int64_t trigger, const int metricIndex, +bool handleMetricWithAtomMatchingTrackers( + const int64_t matcherId, const int metricIndex, const bool enforceOneAtom, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const unordered_map<int64_t, int>& atomMatchingTrackerMap, - unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex) { - auto logTrackerIt = atomMatchingTrackerMap.find(trigger); + unordered_map<int, vector<int>>& trackerToMetricMap, int& logTrackerIndex) { + auto logTrackerIt = atomMatchingTrackerMap.find(matcherId); if (logTrackerIt == atomMatchingTrackerMap.end()) { - ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)trigger); + ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)matcherId); return false; } - if (allAtomMatchingTrackers[logTrackerIt->second]->getAtomIds().size() > 1) { - ALOGE("AtomMatcher \"%lld\" has more than one tag ids." - "Trigger can only be one atom type.", - (long long)trigger); + if (enforceOneAtom && allAtomMatchingTrackers[logTrackerIt->second]->getAtomIds().size() > 1) { + ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, " + "the \"what\" can only be about one atom type. trigger_event matchers can also only " + "be about one atom type.", + (long long)matcherId); return false; } logTrackerIndex = logTrackerIt->second; @@ -170,8 +158,8 @@ bool handleMetricWithConditions( const unordered_map<int64_t, int>& conditionTrackerMap, const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>& links, - vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex, - unordered_map<int, std::vector<int>>& conditionToMetricMap) { + const vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex, + unordered_map<int, vector<int>>& conditionToMetricMap) { auto condition_it = conditionTrackerMap.find(condition); if (condition_it == conditionTrackerMap.end()) { ALOGW("cannot find Predicate \"%lld\" in the config", (long long)condition); @@ -243,31 +231,21 @@ bool handleMetricWithStateLink(const FieldMatcher& stateMatcher, bool handleMetricActivation( const StatsdConfig& config, const int64_t metricId, const int metricIndex, const unordered_map<int64_t, int>& metricToActivationMap, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, bool& hasActivation, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, vector<int>& metricsWithActivation, unordered_map<int, shared_ptr<Activation>>& eventActivationMap, - unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, - uint64_t& activationHash) { + unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) { // Check if metric has an associated activation auto itr = metricToActivationMap.find(metricId); if (itr == metricToActivationMap.end()) { - hasActivation = false; return true; } - hasActivation = true; int activationIndex = itr->second; const MetricActivation& metricActivation = config.metric_activation(activationIndex); - string serializedActivation; - if (!metricActivation.SerializeToString(&serializedActivation)) { - ALOGE("Unable to serialize metric activation for metric %lld", (long long)metricId); - return false; - } - activationHash = Hash64(serializedActivation); - for (int i = 0; i < metricActivation.event_activation_size(); i++) { const EventActivation& activation = metricActivation.event_activation(i); @@ -303,6 +281,130 @@ bool handleMetricActivation( return true; } +// Validates a metricActivation and populates state. +// Fills the new event activation/deactivation maps, preserving the existing activations +// Returns false if there are errors. +bool handleMetricActivationOnConfigUpdate( + const StatsdConfig& config, const int64_t metricId, const int metricIndex, + const unordered_map<int64_t, int>& metricToActivationMap, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const unordered_map<int, shared_ptr<Activation>>& oldEventActivationMap, + unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation, + unordered_map<int, shared_ptr<Activation>>& newEventActivationMap, + unordered_map<int, vector<shared_ptr<Activation>>>& newEventDeactivationMap) { + // Check if metric has an associated activation. + const auto& itr = metricToActivationMap.find(metricId); + if (itr == metricToActivationMap.end()) { + return true; + } + + int activationIndex = itr->second; + const MetricActivation& metricActivation = config.metric_activation(activationIndex); + + for (int i = 0; i < metricActivation.event_activation_size(); i++) { + const int64_t activationMatcherId = metricActivation.event_activation(i).atom_matcher_id(); + + const auto& newActivationIt = newAtomMatchingTrackerMap.find(activationMatcherId); + if (newActivationIt == newAtomMatchingTrackerMap.end()) { + ALOGE("Atom matcher not found in new config for event activation."); + return false; + } + int newActivationMatcherIndex = newActivationIt->second; + + // Find the old activation struct and copy it over. + const auto& oldActivationIt = oldAtomMatchingTrackerMap.find(activationMatcherId); + if (oldActivationIt == oldAtomMatchingTrackerMap.end()) { + ALOGE("Atom matcher not found in existing config for event activation."); + return false; + } + int oldActivationMatcherIndex = oldActivationIt->second; + const auto& oldEventActivationIt = oldEventActivationMap.find(oldActivationMatcherIndex); + if (oldEventActivationIt == oldEventActivationMap.end()) { + ALOGE("Could not find existing event activation to update"); + return false; + } + newEventActivationMap.emplace(newActivationMatcherIndex, oldEventActivationIt->second); + activationAtomTrackerToMetricMap[newActivationMatcherIndex].push_back(metricIndex); + + if (metricActivation.event_activation(i).has_deactivation_atom_matcher_id()) { + const int64_t deactivationMatcherId = + metricActivation.event_activation(i).deactivation_atom_matcher_id(); + const auto& newDeactivationIt = newAtomMatchingTrackerMap.find(deactivationMatcherId); + if (newDeactivationIt == newAtomMatchingTrackerMap.end()) { + ALOGE("Deactivation atom matcher not found in new config for event activation."); + return false; + } + int newDeactivationMatcherIndex = newDeactivationIt->second; + newEventDeactivationMap[newDeactivationMatcherIndex].push_back( + oldEventActivationIt->second); + deactivationAtomTrackerToMetricMap[newDeactivationMatcherIndex].push_back(metricIndex); + } + } + + metricsWithActivation.push_back(metricIndex); + return true; +} + +sp<MetricProducer> createEventMetricProducerAndUpdateMetadata( + const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, + const EventMetric& metric, const int metricIndex, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, + vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<int64_t, int>& conditionTrackerMap, + const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, + const unordered_map<int64_t, int>& metricToActivationMap, + unordered_map<int, vector<int>>& trackerToMetricMap, + unordered_map<int, vector<int>>& conditionToMetricMap, + unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation) { + if (!metric.has_id() || !metric.has_what()) { + ALOGW("cannot find the metric name or what in config"); + return nullptr; + } + int trackerIndex; + if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false, + allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, trackerIndex)) { + return nullptr; + } + + int conditionIndex = -1; + if (metric.has_condition()) { + if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, + metric.links(), allConditionTrackers, conditionIndex, + conditionToMetricMap)) { + return nullptr; + } + } else { + if (metric.links_size() > 0) { + ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); + return nullptr; + } + } + + unordered_map<int, shared_ptr<Activation>> eventActivationMap; + unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; + bool success = handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, + atomMatchingTrackerMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, + eventActivationMap, eventDeactivationMap); + if (!success) return nullptr; + + uint64_t metricHash; + if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { + return nullptr; + } + + return new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, + metricHash, timeBaseNs, eventActivationMap, + eventDeactivationMap); +} + bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, unordered_map<int64_t, int>& atomMatchingTrackerMap, vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, @@ -492,18 +594,16 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } - bool hasActivation = false; unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - uint64_t activationHash; bool success = handleMetricActivation( config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, - hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash); + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap); if (!success) return false; uint64_t metricHash; - if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) { + if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { return false; } @@ -608,18 +708,16 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } - bool hasActivation = false; unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - uint64_t activationHash; bool success = handleMetricActivation( config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, - hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash); + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap); if (!success) return false; uint64_t metricHash; - if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) { + if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { return false; } @@ -637,52 +735,16 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t int metricIndex = allMetricProducers.size(); const EventMetric& metric = config.event_metric(i); metricMap.insert({metric.id(), metricIndex}); - if (!metric.has_id() || !metric.has_what()) { - ALOGW("cannot find the metric name or what in config"); - return false; - } - int trackerIndex; - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false, - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, trackerIndex)) { - return false; - } - - int conditionIndex = -1; - if (metric.has_condition()) { - bool good = handleMetricWithConditions( - metric.condition(), metricIndex, conditionTrackerMap, metric.links(), - allConditionTrackers, conditionIndex, conditionToMetricMap); - if (!good) { - return false; - } - } else { - if (metric.links_size() > 0) { - ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); - return false; - } - } - - bool hasActivation = false; - unordered_map<int, shared_ptr<Activation>> eventActivationMap; - unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - uint64_t activationHash; - bool success = handleMetricActivation( - config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, - hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash); - if (!success) return false; - - uint64_t metricHash; - if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) { + sp<MetricProducer> producer = createEventMetricProducerAndUpdateMetadata( + key, config, timeBaseTimeNs, metric, metricIndex, allAtomMatchingTrackers, + atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, + initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap, + conditionToMetricMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation); + if (producer == nullptr) { return false; } - - sp<MetricProducer> eventMetric = new EventMetricProducer( - key, metric, conditionIndex, initialConditionCache, wizard, metricHash, - timeBaseTimeNs, eventActivationMap, eventDeactivationMap); - - allMetricProducers.push_back(eventMetric); + allMetricProducers.push_back(producer); } // build ValueMetricProducer @@ -759,18 +821,16 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } - bool hasActivation = false; unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - uint64_t activationHash; bool success = handleMetricActivation( config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, - hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash); + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap); if (!success) return false; uint64_t metricHash; - if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) { + if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { return false; } @@ -832,9 +892,10 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) { return false; } - if (!handlePullMetricTriggerWithAtomMatchingTrackers( - metric.trigger_event(), metricIndex, allAtomMatchingTrackers, - atomMatchingTrackerMap, trackerToMetricMap, triggerTrackerIndex)) { + if (!handleMetricWithAtomMatchingTrackers( + metric.trigger_event(), metricIndex, /*enforceOneAtom=*/true, + allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, + triggerTrackerIndex)) { return false; } sp<AtomMatchingTracker> triggerAtomMatcher = @@ -863,18 +924,16 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } - bool hasActivation = false; unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - uint64_t activationHash; bool success = handleMetricActivation( config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, - hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash); + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap); if (!success) return false; uint64_t metricHash; - if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) { + if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { return false; } diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h index 4979c3051133..fdfe5cfa6491 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h @@ -30,7 +30,7 @@ namespace android { namespace os { namespace statsd { -// Helper functions for creating individual config components from StatsdConfig. +// Helper functions for creating, validating, and updating config components from StatsdConfig. // Should only be called from metrics_manager_util and config_update_utils. // Create a AtomMatchingTracker. @@ -53,6 +53,61 @@ sp<ConditionTracker> createConditionTracker( const ConfigKey& key, const Predicate& predicate, const int index, const unordered_map<int64_t, int>& atomMatchingTrackerMap); +// Get the hash of a metric, combining the activation if the metric has one. +bool getMetricProtoHash(const StatsdConfig& config, const google::protobuf::MessageLite& metric, + const int64_t id, + const std::unordered_map<int64_t, int>& metricToActivationMap, + uint64_t& metricHash); + +// 1. Validates matcher existence +// 2. Enforces matchers with dimensions and those used for trigger_event are about one atom +// 3. Gets matcher index and updates tracker to metric map +bool handleMetricWithAtomMatchingTrackers( + const int64_t matcherId, const int metricIndex, const bool enforceOneAtom, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex); + +// 1. Validates condition existence, including those in links +// 2. Gets condition index and updates condition to metric map +bool handleMetricWithConditions( + const int64_t condition, const int metricIndex, + const std::unordered_map<int64_t, int>& conditionTrackerMap, + const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>& + links, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap); + +// Validates a metricActivation and populates state. +// Fills the new event activation/deactivation maps, preserving the existing activations. +// Returns false if there are errors. +bool handleMetricActivationOnConfigUpdate( + const StatsdConfig& config, const int64_t metricId, const int metricIndex, + const std::unordered_map<int64_t, int>& metricToActivationMap, + const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const std::unordered_map<int, shared_ptr<Activation>>& oldEventActivationMap, + std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::vector<int>& metricsWithActivation, + std::unordered_map<int, shared_ptr<Activation>>& newEventActivationMap, + std::unordered_map<int, std::vector<shared_ptr<Activation>>>& newEventDeactivationMap); + +sp<MetricProducer> createEventMetricProducerAndUpdateMetadata( + const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, + const EventMetric& metric, const int metricIndex, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& conditionTrackerMap, + const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, + const std::unordered_map<int64_t, int>& metricToActivationMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::vector<int>& metricsWithActivation); + // Helper functions for MetricsManager to initialize from StatsdConfig. // *Note*: only initStatsdConfig() should be called from outside. // All other functions are intermediate @@ -154,10 +209,10 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp std::unordered_map<int, std::vector<int>>& conditionToMetricMap, std::unordered_map<int, std::vector<int>>& trackerToMetricMap, std::unordered_map<int, std::vector<int>>& trackerToConditionMap, - unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, std::unordered_map<int64_t, int>& alertTrackerMap, - vector<int>& metricsWithActivation, + std::vector<int>& metricsWithActivation, std::map<int64_t, uint64_t>& stateProtoHashes, std::set<int64_t>& noReportMetricIds); diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp index 843d836a2c0b..076000fab80f 100644 --- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp +++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp @@ -34,6 +34,8 @@ using namespace testing; using android::sp; using android::os::statsd::Predicate; using std::map; +using std::nullopt; +using std::optional; using std::set; using std::unordered_map; using std::vector; @@ -61,11 +63,11 @@ vector<sp<MetricProducer>> oldMetricProducers; unordered_map<int64_t, int> oldMetricProducerMap; std::vector<sp<AnomalyTracker>> oldAnomalyTrackers; std::vector<sp<AlarmTracker>> oldAlarmTrackers; -unordered_map<int, std::vector<int>> conditionToMetricMap; -unordered_map<int, std::vector<int>> trackerToMetricMap; -unordered_map<int, std::vector<int>> trackerToConditionMap; -unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; -unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; +unordered_map<int, std::vector<int>> tmpConditionToMetricMap; +unordered_map<int, std::vector<int>> tmpTrackerToMetricMap; +unordered_map<int, std::vector<int>> tmpTrackerToConditionMap; +unordered_map<int, std::vector<int>> tmpActivationAtomTrackerToMetricMap; +unordered_map<int, std::vector<int>> tmpDeactivationAtomTrackerToMetricMap; unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; map<int64_t, uint64_t> oldStateHashes; @@ -86,11 +88,11 @@ public: oldMetricProducerMap.clear(); oldAnomalyTrackers.clear(); oldAlarmTrackers.clear(); - conditionToMetricMap.clear(); - trackerToMetricMap.clear(); - trackerToConditionMap.clear(); - activationAtomTrackerToMetricMap.clear(); - deactivationAtomTrackerToMetricMap.clear(); + tmpConditionToMetricMap.clear(); + tmpTrackerToMetricMap.clear(); + tmpTrackerToConditionMap.clear(); + tmpActivationAtomTrackerToMetricMap.clear(); + tmpDeactivationAtomTrackerToMetricMap.clear(); alertTrackerMap.clear(); metricsWithActivation.clear(); oldStateHashes.clear(); @@ -103,12 +105,22 @@ bool initConfig(const StatsdConfig& config) { key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, timeBaseNs, allTagIds, oldAtomMatchingTrackers, oldAtomMatchingTrackerMap, oldConditionTrackers, oldConditionTrackerMap, oldMetricProducers, oldMetricProducerMap, - oldAnomalyTrackers, oldAlarmTrackers, conditionToMetricMap, trackerToMetricMap, - trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, + oldAnomalyTrackers, oldAlarmTrackers, tmpConditionToMetricMap, tmpTrackerToMetricMap, + tmpTrackerToConditionMap, tmpActivationAtomTrackerToMetricMap, + tmpDeactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, oldStateHashes, noReportMetricIds); } +EventMetric createEventMetric(string name, int64_t what, optional<int64_t> condition) { + EventMetric metric; + metric.set_id(StringToId(name)); + metric.set_what(what); + if (condition) { + metric.set_condition(condition.value()); + } + return metric; +} + } // anonymous namespace TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) { @@ -660,7 +672,6 @@ TEST_F(ConfigUpdateTest, TestCombinationConditionDepsChange) { TEST_F(ConfigUpdateTest, TestUpdateConditions) { StatsdConfig config; - // Add atom matchers. These are mostly needed for initStatsdConfig AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); int64_t matcher1Id = matcher1.id(); @@ -734,7 +745,7 @@ TEST_F(ConfigUpdateTest, TestUpdateConditions) { set<int64_t> replacedMatchers; replacedMatchers.insert(matcher6Id); - // Change the condition of simple1 to true. + // Change the condition of simple1 to false. ASSERT_EQ(oldConditionTrackers[0]->getConditionId(), simple1Id); LogEvent event(/*uid=*/0, /*pid=*/0); // Empty event is fine since there are no dimensions. // Mark the stop matcher as matched, condition should be false. @@ -747,7 +758,7 @@ TEST_F(ConfigUpdateTest, TestUpdateConditions) { EXPECT_EQ(tmpConditionCache[0], ConditionState::kFalse); EXPECT_EQ(conditionChangeCache[0], true); - // New combination matcher. Should have an initial condition of true since it is NOT(simple1). + // New combination predicate. Should have an initial condition of true since it is NOT(simple1). Predicate combination4; combination4.set_id(StringToId("COMBINATION4")); combination4.mutable_combination()->set_operation(LogicalOperation::NOT); @@ -888,6 +899,562 @@ TEST_F(ConfigUpdateTest, TestUpdateConditions) { UnorderedElementsAre(simple1Index, simple2Index)); EXPECT_THAT(combinationTracker1->mSlicedChildren, IsEmpty()); } + +TEST_F(ConfigUpdateTest, TestEventMetricPreserve) { + StatsdConfig config; + AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = startMatcher; + AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = stopMatcher; + AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); + *config.add_atom_matcher() = whatMatcher; + + Predicate predicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = predicate; + + EventMetric* metric = config.add_event_metric(); + metric->set_id(12345); + metric->set_what(whatMatcher.id()); + metric->set_condition(predicate.id()); + + // Create an initial config. + EXPECT_TRUE(initConfig(config)); + + set<int64_t> replacedMatchers; + set<int64_t> replacedConditions; + unordered_map<int64_t, int> metricToActivationMap; + UpdateStatus status = UPDATE_UNKNOWN; + EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap, + oldMetricProducers, metricToActivationMap, + replacedMatchers, replacedConditions, status)); + EXPECT_EQ(status, UPDATE_PRESERVE); +} + +TEST_F(ConfigUpdateTest, TestEventMetricActivationAdded) { + StatsdConfig config; + AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = startMatcher; + AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = stopMatcher; + AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); + *config.add_atom_matcher() = whatMatcher; + + Predicate predicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = predicate; + + EventMetric* metric = config.add_event_metric(); + metric->set_id(12345); + metric->set_what(whatMatcher.id()); + metric->set_condition(predicate.id()); + + // Create an initial config. + EXPECT_TRUE(initConfig(config)); + + // Add a metric activation, which should change the proto, causing replacement. + MetricActivation* activation = config.add_metric_activation(); + activation->set_metric_id(12345); + EventActivation* eventActivation = activation->add_event_activation(); + eventActivation->set_atom_matcher_id(startMatcher.id()); + eventActivation->set_ttl_seconds(5); + + set<int64_t> replacedMatchers; + set<int64_t> replacedConditions; + unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}}; + UpdateStatus status = UPDATE_UNKNOWN; + EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap, + oldMetricProducers, metricToActivationMap, + replacedMatchers, replacedConditions, status)); + EXPECT_EQ(status, UPDATE_REPLACE); +} + +TEST_F(ConfigUpdateTest, TestEventMetricWhatChanged) { + StatsdConfig config; + AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = startMatcher; + AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = stopMatcher; + AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); + *config.add_atom_matcher() = whatMatcher; + + Predicate predicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = predicate; + + EventMetric* metric = config.add_event_metric(); + metric->set_id(12345); + metric->set_what(whatMatcher.id()); + metric->set_condition(predicate.id()); + + // Create an initial config. + EXPECT_TRUE(initConfig(config)); + + set<int64_t> replacedMatchers = {whatMatcher.id()}; + set<int64_t> replacedConditions; + unordered_map<int64_t, int> metricToActivationMap; + UpdateStatus status = UPDATE_UNKNOWN; + EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap, + oldMetricProducers, metricToActivationMap, + replacedMatchers, replacedConditions, status)); + EXPECT_EQ(status, UPDATE_REPLACE); +} + +TEST_F(ConfigUpdateTest, TestEventMetricConditionChanged) { + StatsdConfig config; + AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = startMatcher; + AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = stopMatcher; + AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); + *config.add_atom_matcher() = whatMatcher; + + Predicate predicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = predicate; + + EventMetric* metric = config.add_event_metric(); + metric->set_id(12345); + metric->set_what(whatMatcher.id()); + metric->set_condition(predicate.id()); + + // Create an initial config. + EXPECT_TRUE(initConfig(config)); + + set<int64_t> replacedMatchers; + set<int64_t> replacedConditions = {predicate.id()}; + unordered_map<int64_t, int> metricToActivationMap; + UpdateStatus status = UPDATE_UNKNOWN; + EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap, + oldMetricProducers, metricToActivationMap, + replacedMatchers, replacedConditions, status)); + EXPECT_EQ(status, UPDATE_REPLACE); +} + +TEST_F(ConfigUpdateTest, TestMetricConditionLinkDepsChanged) { + StatsdConfig config; + AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = startMatcher; + AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = stopMatcher; + AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); + *config.add_atom_matcher() = whatMatcher; + + Predicate predicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = predicate; + + Predicate linkPredicate = CreateScreenIsOffPredicate(); + *config.add_predicate() = linkPredicate; + + EventMetric* metric = config.add_event_metric(); + metric->set_id(12345); + metric->set_what(whatMatcher.id()); + metric->set_condition(predicate.id()); + // Doesn't make sense as a real metric definition, but suffices as a separate predicate + // From the one in the condition. + MetricConditionLink* link = metric->add_links(); + link->set_condition(linkPredicate.id()); + + // Create an initial config. + EXPECT_TRUE(initConfig(config)); + + set<int64_t> replacedMatchers; + set<int64_t> replacedConditions = {linkPredicate.id()}; + unordered_map<int64_t, int> metricToActivationMap; + UpdateStatus status = UPDATE_UNKNOWN; + EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap, + oldMetricProducers, metricToActivationMap, + replacedMatchers, replacedConditions, status)); + EXPECT_EQ(status, UPDATE_REPLACE); +} + +TEST_F(ConfigUpdateTest, TestEventMetricActivationDepsChange) { + StatsdConfig config; + AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = startMatcher; + AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = stopMatcher; + AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); + *config.add_atom_matcher() = whatMatcher; + + Predicate predicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = predicate; + + EventMetric* metric = config.add_event_metric(); + metric->set_id(12345); + metric->set_what(whatMatcher.id()); + metric->set_condition(predicate.id()); + + MetricActivation* activation = config.add_metric_activation(); + activation->set_metric_id(12345); + EventActivation* eventActivation = activation->add_event_activation(); + eventActivation->set_atom_matcher_id(startMatcher.id()); + eventActivation->set_ttl_seconds(5); + + // Create an initial config. + EXPECT_TRUE(initConfig(config)); + + set<int64_t> replacedMatchers = {startMatcher.id()}; // The activation matcher is replaced. + set<int64_t> replacedConditions; + unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}}; + UpdateStatus status = UPDATE_UNKNOWN; + EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap, + oldMetricProducers, metricToActivationMap, + replacedMatchers, replacedConditions, status)); + EXPECT_EQ(status, UPDATE_REPLACE); +} + +TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) { + StatsdConfig config; + + // Add atom matchers/predicates. These are mostly needed for initStatsdConfig + AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); + int64_t matcher1Id = matcher1.id(); + *config.add_atom_matcher() = matcher1; + + AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); + int64_t matcher2Id = matcher2.id(); + *config.add_atom_matcher() = matcher2; + + AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher(); + int64_t matcher3Id = matcher3.id(); + *config.add_atom_matcher() = matcher3; + + AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher(); + int64_t matcher4Id = matcher4.id(); + *config.add_atom_matcher() = matcher4; + + AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher(); + int64_t matcher5Id = matcher5.id(); + *config.add_atom_matcher() = matcher5; + + Predicate predicate1 = CreateScreenIsOnPredicate(); + int64_t predicate1Id = predicate1.id(); + *config.add_predicate() = predicate1; + + Predicate predicate2 = CreateScheduledJobPredicate(); + int64_t predicate2Id = predicate2.id(); + *config.add_predicate() = predicate2; + + // Add a few event metrics. + // Will be preserved. + EventMetric event1 = createEventMetric("EVENT1", matcher1Id, predicate2Id); + int64_t event1Id = event1.id(); + *config.add_event_metric() = event1; + + // Will be replaced. + EventMetric event2 = createEventMetric("EVENT2", matcher2Id, nullopt); + int64_t event2Id = event2.id(); + *config.add_event_metric() = event2; + + // Will be replaced. + EventMetric event3 = createEventMetric("EVENT3", matcher3Id, nullopt); + int64_t event3Id = event3.id(); + *config.add_event_metric() = event3; + + MetricActivation event3Activation; + event3Activation.set_metric_id(event3Id); + EventActivation* eventActivation = event3Activation.add_event_activation(); + eventActivation->set_atom_matcher_id(matcher5Id); + eventActivation->set_ttl_seconds(5); + *config.add_metric_activation() = event3Activation; + + // Will be replaced. + EventMetric event4 = createEventMetric("EVENT4", matcher4Id, predicate1Id); + int64_t event4Id = event4.id(); + *config.add_event_metric() = event4; + + // Will be deleted. + EventMetric event5 = createEventMetric("EVENT5", matcher5Id, nullopt); + int64_t event5Id = event5.id(); + *config.add_event_metric() = event5; + + EXPECT_TRUE(initConfig(config)); + + // Used later to ensure the condition wizard is replaced. Get it before doing the update. + sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard; + EXPECT_EQ(oldConditionWizard->getStrongCount(), oldMetricProducers.size() + 1); + + // Add a condition to event2, causing it to be replaced. + event2.set_condition(predicate1Id); + + // Mark matcher 5 as replaced. Causes event3 to be replaced. + set<int64_t> replacedMatchers; + replacedMatchers.insert(matcher5Id); + + // Mark predicate 1 as replaced. Causes event4 to be replaced. + set<int64_t> replacedConditions; + replacedConditions.insert(predicate1Id); + + // Fake that predicate 2 is true. + ASSERT_EQ(oldMetricProducers[0]->getMetricId(), event1Id); + oldMetricProducers[0]->onConditionChanged(true, /*timestamp=*/0); + EXPECT_EQ(oldMetricProducers[0]->mCondition, ConditionState::kTrue); + + // New event metric. Should have an initial condition of true since it depends on predicate2. + EventMetric event6 = createEventMetric("EVENT6", matcher3Id, predicate2Id); + int64_t event6Id = event6.id(); + MetricActivation event6Activation; + event6Activation.set_metric_id(event6Id); + eventActivation = event6Activation.add_event_activation(); + eventActivation->set_atom_matcher_id(matcher5Id); + eventActivation->set_ttl_seconds(20); + + // Map the matchers and predicates in reverse order to force the indices to change. + std::unordered_map<int64_t, int> newAtomMatchingTrackerMap; + const int matcher5Index = 0; + newAtomMatchingTrackerMap[matcher5Id] = 0; + const int matcher4Index = 1; + newAtomMatchingTrackerMap[matcher4Id] = 1; + const int matcher3Index = 2; + newAtomMatchingTrackerMap[matcher3Id] = 2; + const int matcher2Index = 3; + newAtomMatchingTrackerMap[matcher2Id] = 3; + const int matcher1Index = 4; + newAtomMatchingTrackerMap[matcher1Id] = 4; + // Use the existing matchers. A bit hacky, but saves code and we don't rely on them. + vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5); + std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(), + newAtomMatchingTrackers.begin()); + + std::unordered_map<int64_t, int> newConditionTrackerMap; + const int predicate2Index = 0; + newConditionTrackerMap[predicate2Id] = 0; + const int predicate1Index = 1; + newConditionTrackerMap[predicate1Id] = 1; + // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them. + vector<sp<ConditionTracker>> newConditionTrackers(2); + std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(), + newConditionTrackers.begin()); + // Fake that predicate2 is true. + vector<ConditionState> conditionCache = {ConditionState::kTrue, ConditionState::kUnknown}; + + StatsdConfig newConfig; + *newConfig.add_event_metric() = event6; + const int event6Index = 0; + *newConfig.add_event_metric() = event3; + const int event3Index = 1; + *newConfig.add_event_metric() = event1; + const int event1Index = 2; + *newConfig.add_event_metric() = event4; + const int event4Index = 3; + *newConfig.add_event_metric() = event2; + const int event2Index = 4; + *newConfig.add_metric_activation() = event3Activation; + *newConfig.add_metric_activation() = event6Activation; + + // Output data structures to validate. + unordered_map<int64_t, int> newMetricProducerMap; + vector<sp<MetricProducer>> newMetricProducers; + unordered_map<int, vector<int>> conditionToMetricMap; + unordered_map<int, vector<int>> trackerToMetricMap; + set<int64_t> noReportMetricIds; + unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; + unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; + vector<int> metricsWithActivation; + EXPECT_TRUE(updateMetrics( + key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), + oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, + newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, + newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{}, + /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, + newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation)); + + unordered_map<int64_t, int> expectedMetricProducerMap = { + {event1Id, event1Index}, {event2Id, event2Index}, {event3Id, event3Index}, + {event4Id, event4Index}, {event6Id, event6Index}, + }; + EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap)); + + // Make sure preserved metrics are the same. + ASSERT_EQ(newMetricProducers.size(), 5); + EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(event1Id)], + newMetricProducers[newMetricProducerMap.at(event1Id)]); + + // Make sure replaced conditions are different and included in replacedConditions. + EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event2Id)], + newMetricProducers[newMetricProducerMap.at(event2Id)]); + EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event3Id)], + newMetricProducers[newMetricProducerMap.at(event3Id)]); + EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event4Id)], + newMetricProducers[newMetricProducerMap.at(event4Id)]); + + // Verify the conditionToMetricMap. + ASSERT_EQ(conditionToMetricMap.size(), 2); + const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index]; + EXPECT_THAT(condition1Metrics, UnorderedElementsAre(event2Index, event4Index)); + const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index]; + EXPECT_THAT(condition2Metrics, UnorderedElementsAre(event1Index, event6Index)); + + // Verify the trackerToMetricMap. + ASSERT_EQ(trackerToMetricMap.size(), 4); + const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index]; + EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(event1Index)); + const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index]; + EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(event2Index)); + const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index]; + EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(event3Index, event6Index)); + const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index]; + EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(event4Index)); + + // Verify event activation/deactivation maps. + ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 1); + EXPECT_THAT(activationAtomTrackerToMetricMap[matcher5Index], + UnorderedElementsAre(event3Index, event6Index)); + ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0); + ASSERT_EQ(metricsWithActivation.size(), 2); + EXPECT_THAT(metricsWithActivation, UnorderedElementsAre(event3Index, event6Index)); + + // Verify tracker indices/ids/conditions are correct. + EXPECT_EQ(newMetricProducers[event1Index]->getMetricId(), event1Id); + EXPECT_EQ(newMetricProducers[event1Index]->mConditionTrackerIndex, predicate2Index); + EXPECT_EQ(newMetricProducers[event1Index]->mCondition, ConditionState::kTrue); + EXPECT_EQ(newMetricProducers[event2Index]->getMetricId(), event2Id); + EXPECT_EQ(newMetricProducers[event2Index]->mConditionTrackerIndex, predicate1Index); + EXPECT_EQ(newMetricProducers[event2Index]->mCondition, ConditionState::kUnknown); + EXPECT_EQ(newMetricProducers[event3Index]->getMetricId(), event3Id); + EXPECT_EQ(newMetricProducers[event3Index]->mConditionTrackerIndex, -1); + EXPECT_EQ(newMetricProducers[event3Index]->mCondition, ConditionState::kTrue); + EXPECT_EQ(newMetricProducers[event4Index]->getMetricId(), event4Id); + EXPECT_EQ(newMetricProducers[event4Index]->mConditionTrackerIndex, predicate1Index); + EXPECT_EQ(newMetricProducers[event4Index]->mCondition, ConditionState::kUnknown); + EXPECT_EQ(newMetricProducers[event6Index]->getMetricId(), event6Id); + EXPECT_EQ(newMetricProducers[event6Index]->mConditionTrackerIndex, predicate2Index); + EXPECT_EQ(newMetricProducers[event6Index]->mCondition, ConditionState::kTrue); + + sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard; + EXPECT_NE(newConditionWizard, oldConditionWizard); + EXPECT_EQ(newConditionWizard->getStrongCount(), newMetricProducers.size() + 1); + oldMetricProducers.clear(); + // Only reference to the old wizard should be the one in the test. + EXPECT_EQ(oldConditionWizard->getStrongCount(), 1); +} + +TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) { + StatsdConfig config; + // Add atom matchers + AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); + int64_t matcher1Id = matcher1.id(); + *config.add_atom_matcher() = matcher1; + + AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); + int64_t matcher2Id = matcher2.id(); + *config.add_atom_matcher() = matcher2; + + AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher(); + int64_t matcher3Id = matcher3.id(); + *config.add_atom_matcher() = matcher3; + + AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher(); + int64_t matcher4Id = matcher4.id(); + *config.add_atom_matcher() = matcher4; + + // Add an event metric with multiple activations. + EventMetric event1 = createEventMetric("EVENT1", matcher1Id, nullopt); + int64_t event1Id = event1.id(); + *config.add_event_metric() = event1; + + int64_t matcher2TtlSec = 2, matcher3TtlSec = 3, matcher4TtlSec = 4; + MetricActivation metricActivation; + metricActivation.set_metric_id(event1Id); + EventActivation* activation = metricActivation.add_event_activation(); + activation->set_atom_matcher_id(matcher2Id); + activation->set_ttl_seconds(matcher2TtlSec); + activation->set_activation_type(ACTIVATE_IMMEDIATELY); + activation->set_deactivation_atom_matcher_id(matcher1Id); + activation = metricActivation.add_event_activation(); + activation->set_atom_matcher_id(matcher3Id); + activation->set_ttl_seconds(matcher3TtlSec); + activation->set_activation_type(ACTIVATE_ON_BOOT); + activation->set_deactivation_atom_matcher_id(matcher1Id); + activation = metricActivation.add_event_activation(); + activation->set_atom_matcher_id(matcher4Id); + activation->set_ttl_seconds(matcher4TtlSec); + activation->set_activation_type(ACTIVATE_IMMEDIATELY); + activation->set_deactivation_atom_matcher_id(matcher2Id); + *config.add_metric_activation() = metricActivation; + + EXPECT_TRUE(initConfig(config)); + + // Activate some of the event activations. + ASSERT_EQ(oldMetricProducers[0]->getMetricId(), event1Id); + int64_t matcher2StartNs = 12345; + oldMetricProducers[0]->activate(oldAtomMatchingTrackerMap[matcher2Id], matcher2StartNs); + int64_t matcher3StartNs = 23456; + oldMetricProducers[0]->activate(oldAtomMatchingTrackerMap[matcher3Id], matcher3StartNs); + EXPECT_TRUE(oldMetricProducers[0]->isActive()); + + // Map the matchers and predicates in reverse order to force the indices to change. + std::unordered_map<int64_t, int> newAtomMatchingTrackerMap; + const int matcher4Index = 0; + newAtomMatchingTrackerMap[matcher4Id] = 0; + const int matcher3Index = 1; + newAtomMatchingTrackerMap[matcher3Id] = 1; + const int matcher2Index = 2; + newAtomMatchingTrackerMap[matcher2Id] = 2; + const int matcher1Index = 3; + newAtomMatchingTrackerMap[matcher1Id] = 3; + // Use the existing matchers. A bit hacky, but saves code and we don't rely on them. + vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(4); + std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(), + newAtomMatchingTrackers.begin()); + set<int64_t> replacedMatchers; + + unordered_map<int64_t, int> newConditionTrackerMap; + vector<sp<ConditionTracker>> newConditionTrackers; + set<int64_t> replacedConditions; + vector<ConditionState> conditionCache; + unordered_map<int64_t, int> newMetricProducerMap; + vector<sp<MetricProducer>> newMetricProducers; + unordered_map<int, vector<int>> conditionToMetricMap; + unordered_map<int, vector<int>> trackerToMetricMap; + set<int64_t> noReportMetricIds; + unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; + unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; + vector<int> metricsWithActivation; + EXPECT_TRUE(updateMetrics( + key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), + oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, + newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, + newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{}, + /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, + newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation)); + + // Verify event activation/deactivation maps. + ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 3); + EXPECT_THAT(activationAtomTrackerToMetricMap[matcher2Index], UnorderedElementsAre(0)); + EXPECT_THAT(activationAtomTrackerToMetricMap[matcher3Index], UnorderedElementsAre(0)); + EXPECT_THAT(activationAtomTrackerToMetricMap[matcher4Index], UnorderedElementsAre(0)); + ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 2); + EXPECT_THAT(deactivationAtomTrackerToMetricMap[matcher1Index], UnorderedElementsAre(0, 0)); + EXPECT_THAT(deactivationAtomTrackerToMetricMap[matcher2Index], UnorderedElementsAre(0)); + ASSERT_EQ(metricsWithActivation.size(), 1); + EXPECT_THAT(metricsWithActivation, UnorderedElementsAre(0)); + + // Verify mEventActivation and mEventDeactivation map of the producer. + sp<MetricProducer> producer = newMetricProducers[0]; + EXPECT_TRUE(producer->isActive()); + ASSERT_EQ(producer->mEventActivationMap.size(), 3); + shared_ptr<Activation> matcher2Activation = producer->mEventActivationMap[matcher2Index]; + EXPECT_EQ(matcher2Activation->ttl_ns, matcher2TtlSec * NS_PER_SEC); + EXPECT_EQ(matcher2Activation->activationType, ACTIVATE_IMMEDIATELY); + EXPECT_EQ(matcher2Activation->state, kActive); + EXPECT_EQ(matcher2Activation->start_ns, matcher2StartNs); + shared_ptr<Activation> matcher3Activation = producer->mEventActivationMap[matcher3Index]; + EXPECT_EQ(matcher3Activation->ttl_ns, matcher3TtlSec * NS_PER_SEC); + EXPECT_EQ(matcher3Activation->activationType, ACTIVATE_ON_BOOT); + EXPECT_EQ(matcher3Activation->state, kActiveOnBoot); + shared_ptr<Activation> matcher4Activation = producer->mEventActivationMap[matcher4Index]; + EXPECT_EQ(matcher4Activation->ttl_ns, matcher4TtlSec * NS_PER_SEC); + EXPECT_EQ(matcher4Activation->activationType, ACTIVATE_IMMEDIATELY); + EXPECT_EQ(matcher4Activation->state, kNotActive); + + ASSERT_EQ(producer->mEventDeactivationMap.size(), 2); + EXPECT_THAT(producer->mEventDeactivationMap[matcher1Index], + UnorderedElementsAre(matcher2Activation, matcher3Activation)); + EXPECT_THAT(producer->mEventDeactivationMap[matcher2Index], + UnorderedElementsAre(matcher4Activation)); +} } // namespace statsd } // namespace os } // namespace android |