diff options
| -rw-r--r-- | cmds/statsd/src/anomaly/AnomalyTracker.cpp | 6 | ||||
| -rw-r--r-- | cmds/statsd/src/anomaly/AnomalyTracker.h | 7 | ||||
| -rw-r--r-- | cmds/statsd/src/guardrail/StatsdStats.cpp | 31 | ||||
| -rw-r--r-- | cmds/statsd/src/guardrail/StatsdStats.h | 15 | ||||
| -rw-r--r-- | cmds/statsd/src/metrics/DurationMetricProducer.cpp | 2 | ||||
| -rw-r--r-- | cmds/statsd/src/metrics/MetricProducer.h | 2 | ||||
| -rw-r--r-- | cmds/statsd/src/stats_log.proto | 6 | ||||
| -rw-r--r-- | cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp | 6 | ||||
| -rw-r--r-- | cmds/statsd/tests/guardrail/StatsdStats_test.cpp | 17 | ||||
| -rw-r--r-- | cmds/statsd/tests/metrics/CountMetricProducer_test.cpp | 2 | ||||
| -rw-r--r-- | cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp | 4 | ||||
| -rw-r--r-- | cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp | 2 | ||||
| -rw-r--r-- | cmds/statsd/tests/metrics/OringDurationTracker_test.cpp | 4 |
13 files changed, 84 insertions, 20 deletions
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index 7bacb441ee48..e8b408328181 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "AnomalyTracker.h" +#include "guardrail/StatsdStats.h" #include <android/os/IIncidentManager.h> #include <android/os/IncidentReportArgs.h> @@ -31,8 +32,9 @@ namespace statsd { // TODO: Separate DurationAnomalyTracker as a separate subclass and let each MetricProducer // decide and let which one it wants. // TODO: Get rid of bucketNumbers, and return to the original circular array method. -AnomalyTracker::AnomalyTracker(const Alert& alert) +AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey) : mAlert(alert), + mConfigKey(configKey), mNumOfPastBuckets(mAlert.number_of_buckets() - 1) { VLOG("AnomalyTracker() called"); if (mAlert.number_of_buckets() <= 0) { @@ -220,6 +222,8 @@ void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs) { } else { ALOGW("An anomaly has occurred! (But informing incidentd not requested.)"); } + + StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.name()); } void AnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey, diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h index 49e83235f73b..874add2ba798 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.h +++ b/cmds/statsd/src/anomaly/AnomalyTracker.h @@ -18,6 +18,7 @@ #include <gtest/gtest_prod.h> #include "AnomalyMonitor.h" +#include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert #include "stats_util.h" // HashableDimensionKey and DimToValMap @@ -35,7 +36,7 @@ using std::shared_ptr; // Does NOT allow negative values. class AnomalyTracker : public virtual RefBase { public: - AnomalyTracker(const Alert& alert); + AnomalyTracker(const Alert& alert, const ConfigKey& configKey); virtual ~AnomalyTracker(); @@ -107,9 +108,13 @@ public: protected: void flushPastBuckets(const int64_t& currBucketNum); + // statsd_config.proto Alert message that defines this tracker. const Alert mAlert; + // A reference to the Alert's config key. + const ConfigKey& mConfigKey; + // Number of past buckets. One less than the total number of buckets needed // for the anomaly detection (since the current bucket is not in the past). int mNumOfPastBuckets; diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index a48ebc35a3e3..b02b9daa4b2d 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -41,9 +41,6 @@ using std::vector; const int FIELD_ID_BEGIN_TIME = 1; const int FIELD_ID_END_TIME = 2; const int FIELD_ID_CONFIG_STATS = 3; -const int FIELD_ID_MATCHER_STATS = 4; -const int FIELD_ID_CONDITION_STATS = 5; -const int FIELD_ID_METRIC_STATS = 6; const int FIELD_ID_ATOM_STATS = 7; const int FIELD_ID_UIDMAP_STATS = 8; const int FIELD_ID_ANOMALY_ALARM_STATS = 9; @@ -104,11 +101,12 @@ void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) { if (it != mConfigStats.end()) { int32_t nowTimeSec = time(nullptr); it->second.set_deletion_time_sec(nowTimeSec); - // Add condition stats, metrics stats, matcher stats - addSubStatsToConfig(key, it->second); + // Add condition stats, metrics stats, matcher stats, alert stats + addSubStatsToConfigLocked(key, it->second); // Remove them after they are added to the config stats. mMatcherStats.erase(key); mMetricsStats.erase(key); + mAlertStats.erase(key); mConditionStats.erase(key); mIceBox.push_back(it->second); mConfigStats.erase(it); @@ -222,6 +220,12 @@ void StatsdStats::noteMatcherMatched(const ConfigKey& key, const string& name) { matcherStats[name]++; } +void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const string& name) { + lock_guard<std::mutex> lock(mLock); + auto& alertStats = mAlertStats[key]; + alertStats[name]++; +} + void StatsdStats::noteRegisteredAnomalyAlarmChanged() { lock_guard<std::mutex> lock(mLock); mAnomalyAlarmRegisteredStats++; @@ -254,6 +258,7 @@ void StatsdStats::resetInternalLocked() { mConditionStats.clear(); mMetricsStats.clear(); std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0); + mAlertStats.clear(); mAnomalyAlarmRegisteredStats = 0; mMatcherStats.clear(); for (auto& config : mConfigStats) { @@ -263,10 +268,11 @@ void StatsdStats::resetInternalLocked() { config.second.clear_matcher_stats(); config.second.clear_condition_stats(); config.second.clear_metric_stats(); + config.second.clear_alert_stats(); } } -void StatsdStats::addSubStatsToConfig(const ConfigKey& key, +void StatsdStats::addSubStatsToConfigLocked(const ConfigKey& key, StatsdStatsReport_ConfigStats& configStats) { // Add matcher stats if (mMatcherStats.find(key) != mMatcherStats.end()) { @@ -298,6 +304,16 @@ void StatsdStats::addSubStatsToConfig(const ConfigKey& key, VLOG("metrics %s max output tuple size %d", stats.first.c_str(), stats.second); } } + // Add anomaly detection alert stats + if (mAlertStats.find(key) != mAlertStats.end()) { + const auto& alertStats = mAlertStats[key]; + for (const auto& stats : alertStats) { + auto output = configStats.add_alert_stats(); + output->set_name(stats.first); + output->set_declared_times(stats.second); + VLOG("alert %s declared %d times", stats.first.c_str(), stats.second); + } + } } void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { @@ -367,7 +383,7 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { } } - addSubStatsToConfig(pair.first, configStats); + addSubStatsToConfigLocked(pair.first, configStats); const int numBytes = configStats.ByteSize(); vector<char> buffer(numBytes); @@ -379,6 +395,7 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { configStats.clear_matcher_stats(); configStats.clear_condition_stats(); configStats.clear_metric_stats(); + configStats.clear_alert_stats(); } VLOG("********Atom stats***********"); diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 95a4e2640b19..394056303722 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -112,6 +112,14 @@ public: void noteMatcherMatched(const ConfigKey& key, const std::string& name); /** + * Report that an anomaly detection alert has been declared. + * + * [key]: The config key that this alert belongs to. + * [name]: The name of the alert. + */ + void noteAnomalyDeclared(const ConfigKey& key, const std::string& name); + + /** * Report an atom event has been logged. */ void noteAtomLogged(int atomId, int32_t timeSec); @@ -183,6 +191,10 @@ private: // StatsCompanionService. int mAnomalyAlarmRegisteredStats = 0; + // Stores the number of times an anomaly detection alert has been declared + // (per config, per alert name). + std::map<const ConfigKey, std::map<const std::string, int>> mAlertStats; + // Stores how many times a matcher have been matched. std::map<const ConfigKey, std::map<const std::string, int>> mMatcherStats; @@ -190,7 +202,8 @@ private: void resetInternalLocked(); - void addSubStatsToConfig(const ConfigKey& key, StatsdStatsReport_ConfigStats& configStats); + void addSubStatsToConfigLocked(const ConfigKey& key, + StatsdStatsReport_ConfigStats& configStats); void noteDataDropped(const ConfigKey& key, int32_t timeSec); diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index fd484c25dbaa..cedea301de27 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -111,7 +111,7 @@ sp<AnomalyTracker> DurationMetricProducer::createAnomalyTracker(const Alert &ale return nullptr; } // TODO: return a DurationAnomalyTracker (which should sublclass AnomalyTracker) - return new AnomalyTracker(alert); + return new AnomalyTracker(alert, mConfigKey); } void DurationMetricProducer::startNewProtoOutputStreamLocked(long long startTime) { diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 819df7705a75..71d6d07920f8 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -94,7 +94,7 @@ public: } virtual sp<AnomalyTracker> createAnomalyTracker(const Alert &alert) { - return new AnomalyTracker(alert); + return new AnomalyTracker(alert, mConfigKey); } void addAnomalyTracker(sp<AnomalyTracker> tracker) { diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 79c47277b355..f8b91fe0ef0a 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -193,6 +193,11 @@ message StatsdStatsReport { optional int32 max_tuple_counts = 2; } + message AlertStats { + optional string name = 1; + optional int32 declared_times = 2; + } + message ConfigStats { optional int32 uid = 1; optional string name = 2; @@ -210,6 +215,7 @@ message StatsdStatsReport { repeated MatcherStats matcher_stats = 13; repeated ConditionStats condition_stats = 14; repeated MetricStats metric_stats = 15; + repeated AlertStats alert_stats = 16; } repeated ConfigStats config_stats = 3; diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp index 65c2a05543b9..f38576398f11 100644 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp @@ -30,6 +30,8 @@ namespace android { namespace os { namespace statsd { +const ConfigKey kConfigKey(0, "test"); + void AddValueToBucket(const std::vector<std::pair<string, long>>& key_value_pair_list, std::shared_ptr<DimToValMap> bucket) { for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) { @@ -51,7 +53,7 @@ TEST(AnomalyTrackerTest, TestConsecutiveBuckets) { alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC); alert.set_trigger_if_sum_gt(2); - AnomalyTracker anomalyTracker(alert); + AnomalyTracker anomalyTracker(alert, kConfigKey); std::shared_ptr<DimToValMap> bucket0 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}}); int64_t eventTimestamp0 = 10; @@ -168,7 +170,7 @@ TEST(AnomalyTrackerTest, TestSparseBuckets) { alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC); alert.set_trigger_if_sum_gt(2); - AnomalyTracker anomalyTracker(alert); + AnomalyTracker anomalyTracker(alert, kConfigKey); std::shared_ptr<DimToValMap> bucket9 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}}); std::shared_ptr<DimToValMap> bucket16 = MockBucket({{"b", 4}}); diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp index 7baa9003056e..9fed4f8b5e30 100644 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -118,6 +118,10 @@ TEST(StatsdStatsTest, TestSubStats) { stats.noteMetricDimensionSize(key, "metric1", 201); stats.noteMetricDimensionSize(key, "metric1", 202); + stats.noteAnomalyDeclared(key, "alert1"); + stats.noteAnomalyDeclared(key, "alert1"); + stats.noteAnomalyDeclared(key, "alert2"); + // broadcast-> 2 stats.noteBroadcastSent(key); stats.noteBroadcastSent(key); @@ -142,7 +146,6 @@ TEST(StatsdStatsTest, TestSubStats) { EXPECT_EQ(3, configReport.dump_report_time_sec_size()); EXPECT_EQ(2, configReport.matcher_stats_size()); - // matcher1 is the first in the list if (!configReport.matcher_stats(0).name().compare("matcher1")) { EXPECT_EQ(2, configReport.matcher_stats(0).matched_times()); @@ -157,6 +160,13 @@ TEST(StatsdStatsTest, TestSubStats) { EXPECT_EQ("matcher1", configReport.matcher_stats(1).name()); } + EXPECT_EQ(2, configReport.alert_stats_size()); + bool alert1first = !configReport.alert_stats(0).name().compare("alert1"); + EXPECT_EQ("alert1", configReport.alert_stats(alert1first ? 0 : 1).name()); + EXPECT_EQ(2, configReport.alert_stats(alert1first ? 0 : 1).declared_times()); + EXPECT_EQ("alert2", configReport.alert_stats(alert1first ? 1 : 0).name()); + EXPECT_EQ(1, configReport.alert_stats(alert1first ? 1 : 0).declared_times()); + EXPECT_EQ(1, configReport.condition_stats_size()); EXPECT_EQ("condition1", configReport.condition_stats(0).name()); EXPECT_EQ(250, configReport.condition_stats(0).max_tuple_counts()); @@ -169,6 +179,7 @@ TEST(StatsdStatsTest, TestSubStats) { stats.noteMatcherMatched(key, "matcher99"); stats.noteConditionDimensionSize(key, "condition99", 300); stats.noteMetricDimensionSize(key, "metric99", 270); + stats.noteAnomalyDeclared(key, "alert99"); // now the config stats should only contain the stats about the new event. stats.dumpStats(&output, false); @@ -187,6 +198,10 @@ TEST(StatsdStatsTest, TestSubStats) { EXPECT_EQ(1, configReport2.metric_stats_size()); EXPECT_EQ("metric99", configReport2.metric_stats(0).name()); EXPECT_EQ(270, configReport2.metric_stats(0).max_tuple_counts()); + + EXPECT_EQ(1, configReport2.alert_stats_size()); + EXPECT_EQ("alert99", configReport2.alert_stats(0).name()); + EXPECT_EQ(1, configReport2.alert_stats(0).declared_times()); } TEST(StatsdStatsTest, TestAtomLog) { diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index dc42943e8233..4e24ab530155 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -196,7 +196,7 @@ TEST(CountMetricProducerTest, TestAnomalyDetection) { int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey); CountMetric metric; metric.set_name("1"); diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index ed13db25397a..59475d266217 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -33,6 +33,8 @@ namespace android { namespace os { namespace statsd { +const ConfigKey kConfigKey(0, "test"); + TEST(GaugeMetricProducerTest, TestWithCondition) { int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL; @@ -148,7 +150,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { alert.set_metric_name("1"); alert.set_trigger_if_sum_gt(25); alert.set_number_of_buckets(2); - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey); gaugeProducer.addAnomalyTracker(anomalyTracker); std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1); diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index 1adcc1146c42..4e5e0d6dbfb3 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -208,7 +208,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey); MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs, bucketSizeNs, {anomalyTracker}); diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index fa7b9a734524..99d3e054d893 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -269,7 +269,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey); OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {anomalyTracker}); @@ -331,7 +331,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetection) { uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey); OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true /*nesting*/, bucketStartTimeNs, bucketSizeNs, {anomalyTracker}); |