Guardrail for dimension in condition in duration tracker.

Test: statsd test

BUG: b/74437017
Change-Id: I349528e419ede817904f1e3884260c06651c6d0b
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index ef637df..22ff942 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -76,6 +76,7 @@
 const int FIELD_ID_CONFIG_STATS_CONDITION_STATS = 14;
 const int FIELD_ID_CONFIG_STATS_METRIC_STATS = 15;
 const int FIELD_ID_CONFIG_STATS_ALERT_STATS = 16;
+const int FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS = 17;
 
 const int FIELD_ID_MATCHER_STATS_ID = 1;
 const int FIELD_ID_MATCHER_STATS_COUNT = 2;
@@ -255,6 +256,20 @@
     }
 }
 
+void StatsdStats::noteMetricDimensionInConditionSize(
+        const ConfigKey& key, const int64_t& id, int size) {
+    lock_guard<std::mutex> lock(mLock);
+    // if name doesn't exist before, it will create the key with count 0.
+    auto statsIt = mConfigStats.find(key);
+    if (statsIt == mConfigStats.end()) {
+        return;
+    }
+    auto& metricsDimensionMap = statsIt->second->metric_dimension_in_condition_stats;
+    if (size > metricsDimensionMap[id]) {
+        metricsDimensionMap[id] = size;
+    }
+}
+
 void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) {
     lock_guard<std::mutex> lock(mLock);
 
@@ -339,6 +354,7 @@
         config.second->matcher_stats.clear();
         config.second->condition_stats.clear();
         config.second->metric_stats.clear();
+        config.second->metric_dimension_in_condition_stats.clear();
         config.second->alert_stats.clear();
     }
 }
@@ -504,6 +520,13 @@
         proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second);
         proto->end(tmpToken);
     }
+    for (const auto& pair : configStats.metric_dimension_in_condition_stats) {
+        uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+                                         FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS);
+        proto->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_STATS_ID, (long long)pair.first);
+        proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second);
+        proto->end(tmpToken);
+    }
 
     for (const auto& pair : configStats.alert_stats) {
         uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 7f8755b..bd395c4 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -57,6 +57,12 @@
     // it means some data has been dropped. The map size is capped by kMaxConfigCount.
     std::map<const int64_t, int> metric_stats;
 
+    // Stores the max number of output tuple of dimensions in condition across dimensions in what
+    // when it's bigger than kDimensionKeySizeSoftLimit. When you see the number is
+    // kDimensionKeySizeHardLimit +1, it means some data has been dropped. The map size is capped by
+    // kMaxConfigCount.
+    std::map<const int64_t, int> metric_dimension_in_condition_stats;
+
     // Stores the number of times an anomaly detection alert has been declared.
     // The map size is capped by kMaxConfigCount.
     std::map<const int64_t, int> alert_stats;
@@ -183,6 +189,19 @@
      */
     void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size);
 
+
+    /**
+     * Report the max size of output tuple of dimension in condition across dimensions in what.
+     *
+     * Note: only report when the metric has an output dimension in condition, and the max tuple
+     * count > kDimensionKeySizeSoftLimit.
+     *
+     * [key]: The config key that this metric belongs to.
+     * [id]: The id of the metric.
+     * [size]: The output tuple size.
+     */
+    void noteMetricDimensionInConditionSize(const ConfigKey& key, const int64_t& id, int size);
+
     /**
      * Report a matcher has been matched.
      *
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index c28bb88..f02f307 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -308,11 +308,14 @@
                     if (mMetric2ConditionLinks.size() == 0 ||
                         trueDim.contains(linkedConditionDimensionKey)) {
                         if (!whatIt.second.empty()) {
+                            auto newEventKey = MetricDimensionKey(whatIt.first, trueDim);
+                            if (hitGuardRailLocked(newEventKey)) {
+                                continue;
+                            }
                             unique_ptr<DurationTracker> newTracker =
                                 whatIt.second.begin()->second->clone(eventTime);
                             if (newTracker != nullptr) {
-                                newTracker->setEventKey(
-                                    MetricDimensionKey(whatIt.first, trueDim));
+                                newTracker->setEventKey(newEventKey);
                                 newTracker->onConditionChanged(true, eventTime);
                                 whatIt.second[trueDim] = std::move(newTracker);
                             }
@@ -370,11 +373,14 @@
         for (const auto& conditionDimension : conditionDimensionsKeySet) {
             for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
                 if (!whatIt.second.empty()) {
+                    auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension);
+                    if (hitGuardRailLocked(newEventKey)) {
+                        continue;
+                    }
                     unique_ptr<DurationTracker> newTracker =
                         whatIt.second.begin()->second->clone(eventTime);
                     if (newTracker != nullptr) {
-                        newTracker->setEventKey(MetricDimensionKey(
-                                whatIt.first, conditionDimension));
+                        newTracker->setEventKey(MetricDimensionKey(newEventKey));
                         newTracker->onSlicedConditionMayChange(overallCondition, eventTime);
                         whatIt.second[conditionDimension] = std::move(newTracker);
                     }
@@ -397,10 +403,13 @@
             for (const auto& conditionDimension : conditionDimensionsKeys) {
                 if (!whatIt.second.empty() &&
                     whatIt.second.find(conditionDimension) == whatIt.second.end()) {
+                    auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension);
+                    if (hitGuardRailLocked(newEventKey)) {
+                        continue;
+                    }
                     auto newTracker = whatIt.second.begin()->second->clone(eventTime);
                     if (newTracker != nullptr) {
-                        newTracker->setEventKey(
-                            MetricDimensionKey(whatIt.first, conditionDimension));
+                        newTracker->setEventKey(newEventKey);
                         newTracker->onSlicedConditionMayChange(overallCondition, eventTime);
                         whatIt.second[conditionDimension] = std::move(newTracker);
                     }
@@ -552,15 +561,35 @@
 }
 
 bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
-    // 1. Report the tuple count if the tuple count > soft limit
-    if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
-        size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
-        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
-        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("DurationMetric %lld dropping data for dimension key %s",
-                (long long)mMetricId, newKey.toString().c_str());
-            return true;
+    auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat());
+    if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
+        auto condIt = whatIt->second.find(newKey.getDimensionKeyInCondition());
+        if (condIt != whatIt->second.end()) {
+            return false;
+        }
+        if (whatIt->second.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+            size_t newTupleCount = whatIt->second.size() + 1;
+            StatsdStats::getInstance().noteMetricDimensionInConditionSize(
+                    mConfigKey, mMetricId, newTupleCount);
+            // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+            if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+                ALOGE("DurationMetric %lld dropping data for condition dimension key %s",
+                    (long long)mMetricId, newKey.getDimensionKeyInCondition().toString().c_str());
+                return true;
+            }
+        }
+    } else {
+        // 1. Report the tuple count if the tuple count > soft limit
+        if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+            size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
+            StatsdStats::getInstance().noteMetricDimensionSize(
+                    mConfigKey, mMetricId, newTupleCount);
+            // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+            if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+                ALOGE("DurationMetric %lld dropping data for what dimension key %s",
+                    (long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str());
+                return true;
+            }
         }
     }
     return false;
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index dd3b37c..a25df3f 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -241,6 +241,7 @@
         repeated ConditionStats condition_stats = 14;
         repeated MetricStats metric_stats = 15;
         repeated AlertStats alert_stats = 16;
+        repeated MetricStats metric_dimension_in_condition_stats = 17;
     }
 
     repeated ConfigStats config_stats = 3;