Add StatsdStats and guardrail.

+ StatsdStats is the global class that tracks the stats about statsd.

+ Added guardrail for classes that have a map which could potentially grow
  unboundedly with the number of logs.

TODO: add unit tests & CTS for StatsdStats, and guardrail
      add stats for pulled atoms.

Test: statsd_test

Change-Id: I0ea562de4dd3f6162f7923a9c193420b482c1d51
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index c8e6cce..a0374c0 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -18,6 +18,7 @@
 
 #include "Log.h"
 #include "DurationMetricProducer.h"
+#include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 
 #include <limits.h>
@@ -60,14 +61,14 @@
 const int FIELD_ID_END_BUCKET_NANOS = 2;
 const int FIELD_ID_DURATION = 3;
 
-DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
+DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const DurationMetric& metric,
                                                const int conditionIndex, const size_t startIndex,
                                                const size_t stopIndex, const size_t stopAllIndex,
                                                const bool nesting,
                                                const sp<ConditionWizard>& wizard,
                                                const vector<KeyMatcher>& internalDimension,
                                                const uint64_t startTimeNs)
-    : MetricProducer(startTimeNs, conditionIndex, wizard),
+    : MetricProducer(key, startTimeNs, conditionIndex, wizard),
       mMetric(metric),
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
@@ -113,13 +114,13 @@
         const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) {
     switch (mMetric.aggregation_type()) {
         case DurationMetric_AggregationType_SUM:
-            return make_unique<OringDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
-                                                     mNested, mCurrentBucketStartTimeNs,
-                                                     mBucketSizeNs, mAnomalyTrackers, bucket);
+            return make_unique<OringDurationTracker>(
+                    mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket);
         case DurationMetric_AggregationType_MAX_SPARSE:
-            return make_unique<MaxDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
-                                                   mNested, mCurrentBucketStartTimeNs,
-                                                   mBucketSizeNs, mAnomalyTrackers, bucket);
+            return make_unique<MaxDurationTracker>(
+                    mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket);
     }
 }
 
@@ -238,6 +239,26 @@
     mCurrentBucketNum += numBucketsForward;
 }
 
+bool DurationMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
+    // the key is not new, we are good.
+    if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) {
+        return false;
+    }
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mCurrentSlicedDuration.size() + 1;
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
+                                                           newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("DurationMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+                  newKey.c_str());
+            return true;
+        }
+    }
+    return false;
+}
+
 void DurationMetricProducer::onMatchedLogEventInternal(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
         const map<string, HashableDimensionKey>& conditionKeys, bool condition,
@@ -254,6 +275,9 @@
     HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
 
     if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
+        if (hitGuardRail(eventKey)) {
+            return;
+        }
         mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]);
     }