Loading cmds/statsd/src/config/ConfigManager.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -144,6 +144,12 @@ static StatsdConfig build_fake_config() { metric->set_what("SCREEN_TURNED_ON"); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); // Anomaly threshold for screen-on count. Alert* alert = metric->add_alerts(); alert->set_number_of_buckets(6); alert->set_trigger_if_sum_gt(10); alert->set_refractory_period_secs(30); // Count process state changes, slice by uid. metric = config.add_count_metric(); metric->set_metric_id(2); Loading cmds/statsd/src/metrics/CountAnomalyTracker.cpp +39 −17 Original line number Diff line number Diff line Loading @@ -17,23 +17,22 @@ #define DEBUG true // STOPSHIP if true #include "Log.h" #define VLOG(...) \ if (DEBUG) ALOGD(__VA_ARGS__); #include "CountAnomalyTracker.h" #include <time.h> namespace android { namespace os { namespace statsd { CountAnomalyTracker::CountAnomalyTracker(size_t numBuckets, int thresholdGt) : mNumPastBuckets(numBuckets > 0 ? numBuckets - 1 : 0), mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr), mThresholdGt(thresholdGt) { CountAnomalyTracker::CountAnomalyTracker(const Alert& alert) : mAlert(alert), mNumPastBuckets(alert.number_of_buckets() > 0 ? alert.number_of_buckets() - 1 : 0), mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr) { VLOG("CountAnomalyTracker() called"); if (numBuckets < 1) { ALOGE("Cannot create CountAnomalyTracker with %zu buckets", numBuckets); if (alert.number_of_buckets() < 1) { ALOGE("Cannot create CountAnomalyTracker with %d buckets", alert.number_of_buckets()); } reset(); // initialization } Loading Loading @@ -84,22 +83,45 @@ void CountAnomalyTracker::reset() { } void CountAnomalyTracker::checkAnomaly(int currentCount) { // Note that this works even if mNumPastBuckets < 1 (since then // mSumPastCounters = 0 so the comparison is based only on currentCount). // Skip the check if in refractory period. if (time(nullptr) < mRefractoryPeriodEndsSec) { VLOG("Skipping anomaly check since within refractory period"); return; } // TODO: Remove these extremely verbose debugging log. VLOG("Checking whether %d + %d > %d", mSumPastCounters, currentCount, mThresholdGt); VLOG("Checking whether %d + %d > %lld", mSumPastCounters, currentCount, mAlert.trigger_if_sum_gt()); if (mSumPastCounters + currentCount > mThresholdGt) { // Note that this works even if mNumPastBuckets < 1 (since then // mSumPastCounters = 0 so the comparison is based only on currentCount). if (mAlert.has_trigger_if_sum_gt() && mSumPastCounters + currentCount > mAlert.trigger_if_sum_gt()) { declareAnomaly(); } } void CountAnomalyTracker::declareAnomaly() { // TODO: check that not in refractory period. // TODO: Do something. ALOGI("An anomaly has occurred!"); // TODO(guardrail): Consider guarding against too short refractory periods. time_t currTime = time(nullptr); mRefractoryPeriodEndsSec = currTime + mAlert.refractory_period_secs(); // TODO: If we had access to the bucket_size_millis, consider calling reset() // if (mAlert.refractory_period_secs() > mNumPastBuckets * bucket_size_millis * 1000). if (mAlert.has_incidentd_details()) { const Alert_IncidentdDetails& incident = mAlert.incidentd_details(); if (incident.has_alert_name()) { ALOGW("An anomaly (%s) has occurred! Informing incidentd.", incident.alert_name().c_str()); } else { // TODO: Can construct a name based on the criteria (and/or relay the criteria). ALOGW("An anomaly (nameless) has occurred! Informing incidentd."); } // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd } else { ALOGW("An anomaly has occurred! (But informing incidentd not requested.)"); } } } // namespace statsd Loading cmds/statsd/src/metrics/CountAnomalyTracker.h +10 −4 Original line number Diff line number Diff line Loading @@ -17,8 +17,10 @@ #ifndef COUNT_ANOMALY_TRACKER_H #define COUNT_ANOMALY_TRACKER_H #include <stdlib.h> #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert #include <memory> // unique_ptr #include <stdlib.h> namespace android { namespace os { Loading @@ -26,7 +28,7 @@ namespace statsd { class CountAnomalyTracker { public: CountAnomalyTracker(size_t numBuckets, int thresholdGt); CountAnomalyTracker(const Alert& alert); virtual ~CountAnomalyTracker(); Loading @@ -43,6 +45,9 @@ public: void checkAnomaly(int currentCount); private: // statsd_config.proto Alert message that defines this tracker. const Alert mAlert; // 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). const size_t mNumPastBuckets; Loading @@ -59,8 +64,9 @@ private: // Index of the oldest bucket (i.e. the next bucket to be overwritten). size_t mOldestBucketIndex = 0; // If mSumPastCounters + currentCount > mThresholdGt --> Anomaly! const int mThresholdGt; // Timestamp that the refractory period (if this anomaly was declared) ends, in seconds. // If an anomaly was never declared, set to 0. time_t mRefractoryPeriodEndsSec = 0; void declareAnomaly(); Loading cmds/statsd/src/metrics/CountMetricProducer.cpp +22 −3 Original line number Diff line number Diff line Loading @@ -39,9 +39,7 @@ CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int co const sp<ConditionWizard>& wizard) // TODO: Pass in the start time from MetricsManager, instead of calling time() here. : MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard), mMetric(metric), // TODO: read mAnomalyTracker parameters from config file. mAnomalyTracker(6, 10) { mMetric(metric) { // TODO: evaluate initial conditions. and set mConditionMet. if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) { mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000; Loading @@ -49,6 +47,17 @@ CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int co mBucketSizeNs = LLONG_MAX; } mAnomalyTrackers.reserve(metric.alerts_size()); for (int i = 0; i < metric.alerts_size(); i++) { const Alert& alert = metric.alerts(i); if (alert.trigger_if_sum_gt() > 0 && alert.number_of_buckets() > 0) { mAnomalyTrackers.push_back(std::make_unique<CountAnomalyTracker>(alert)); } else { ALOGW("Ignoring invalid count metric alert: threshold=%lld num_buckets= %d", alert.trigger_if_sum_gt(), alert.number_of_buckets()); } } // TODO: use UidMap if uid->pkg_name is required mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end()); Loading Loading @@ -148,6 +157,11 @@ void CountMetricProducer::onMatchedLogEventInternal( count++; } // TODO: Re-add anomaly detection (similar to): // for (auto& tracker : mAnomalyTrackers) { // tracker->checkAnomaly(mCounter); // } VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(), mCurrentSlicedCounter[eventKey]); } Loading Loading @@ -176,6 +190,11 @@ void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) { counter.second); } // TODO: Re-add anomaly detection (similar to): // for (auto& tracker : mAnomalyTrackers) { // tracker->addPastBucket(mCounter, numBucketsForward); //} // Reset counters mCurrentSlicedCounter.clear(); Loading cmds/statsd/src/metrics/CountMetricProducer.h +2 −2 Original line number Diff line number Diff line Loading @@ -60,14 +60,14 @@ protected: private: const CountMetric mMetric; CountAnomalyTracker mAnomalyTracker; // Save the past buckets and we can clear when the StatsLogReport is dumped. std::unordered_map<HashableDimensionKey, std::vector<CountBucketInfo>> mPastBuckets; // The current bucket. std::unordered_map<HashableDimensionKey, int> mCurrentSlicedCounter; vector<unique_ptr<CountAnomalyTracker>> mAnomalyTrackers; void flushCounterIfNeeded(const uint64_t newEventTime); }; Loading Loading
cmds/statsd/src/config/ConfigManager.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -144,6 +144,12 @@ static StatsdConfig build_fake_config() { metric->set_what("SCREEN_TURNED_ON"); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); // Anomaly threshold for screen-on count. Alert* alert = metric->add_alerts(); alert->set_number_of_buckets(6); alert->set_trigger_if_sum_gt(10); alert->set_refractory_period_secs(30); // Count process state changes, slice by uid. metric = config.add_count_metric(); metric->set_metric_id(2); Loading
cmds/statsd/src/metrics/CountAnomalyTracker.cpp +39 −17 Original line number Diff line number Diff line Loading @@ -17,23 +17,22 @@ #define DEBUG true // STOPSHIP if true #include "Log.h" #define VLOG(...) \ if (DEBUG) ALOGD(__VA_ARGS__); #include "CountAnomalyTracker.h" #include <time.h> namespace android { namespace os { namespace statsd { CountAnomalyTracker::CountAnomalyTracker(size_t numBuckets, int thresholdGt) : mNumPastBuckets(numBuckets > 0 ? numBuckets - 1 : 0), mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr), mThresholdGt(thresholdGt) { CountAnomalyTracker::CountAnomalyTracker(const Alert& alert) : mAlert(alert), mNumPastBuckets(alert.number_of_buckets() > 0 ? alert.number_of_buckets() - 1 : 0), mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr) { VLOG("CountAnomalyTracker() called"); if (numBuckets < 1) { ALOGE("Cannot create CountAnomalyTracker with %zu buckets", numBuckets); if (alert.number_of_buckets() < 1) { ALOGE("Cannot create CountAnomalyTracker with %d buckets", alert.number_of_buckets()); } reset(); // initialization } Loading Loading @@ -84,22 +83,45 @@ void CountAnomalyTracker::reset() { } void CountAnomalyTracker::checkAnomaly(int currentCount) { // Note that this works even if mNumPastBuckets < 1 (since then // mSumPastCounters = 0 so the comparison is based only on currentCount). // Skip the check if in refractory period. if (time(nullptr) < mRefractoryPeriodEndsSec) { VLOG("Skipping anomaly check since within refractory period"); return; } // TODO: Remove these extremely verbose debugging log. VLOG("Checking whether %d + %d > %d", mSumPastCounters, currentCount, mThresholdGt); VLOG("Checking whether %d + %d > %lld", mSumPastCounters, currentCount, mAlert.trigger_if_sum_gt()); if (mSumPastCounters + currentCount > mThresholdGt) { // Note that this works even if mNumPastBuckets < 1 (since then // mSumPastCounters = 0 so the comparison is based only on currentCount). if (mAlert.has_trigger_if_sum_gt() && mSumPastCounters + currentCount > mAlert.trigger_if_sum_gt()) { declareAnomaly(); } } void CountAnomalyTracker::declareAnomaly() { // TODO: check that not in refractory period. // TODO: Do something. ALOGI("An anomaly has occurred!"); // TODO(guardrail): Consider guarding against too short refractory periods. time_t currTime = time(nullptr); mRefractoryPeriodEndsSec = currTime + mAlert.refractory_period_secs(); // TODO: If we had access to the bucket_size_millis, consider calling reset() // if (mAlert.refractory_period_secs() > mNumPastBuckets * bucket_size_millis * 1000). if (mAlert.has_incidentd_details()) { const Alert_IncidentdDetails& incident = mAlert.incidentd_details(); if (incident.has_alert_name()) { ALOGW("An anomaly (%s) has occurred! Informing incidentd.", incident.alert_name().c_str()); } else { // TODO: Can construct a name based on the criteria (and/or relay the criteria). ALOGW("An anomaly (nameless) has occurred! Informing incidentd."); } // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd } else { ALOGW("An anomaly has occurred! (But informing incidentd not requested.)"); } } } // namespace statsd Loading
cmds/statsd/src/metrics/CountAnomalyTracker.h +10 −4 Original line number Diff line number Diff line Loading @@ -17,8 +17,10 @@ #ifndef COUNT_ANOMALY_TRACKER_H #define COUNT_ANOMALY_TRACKER_H #include <stdlib.h> #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert #include <memory> // unique_ptr #include <stdlib.h> namespace android { namespace os { Loading @@ -26,7 +28,7 @@ namespace statsd { class CountAnomalyTracker { public: CountAnomalyTracker(size_t numBuckets, int thresholdGt); CountAnomalyTracker(const Alert& alert); virtual ~CountAnomalyTracker(); Loading @@ -43,6 +45,9 @@ public: void checkAnomaly(int currentCount); private: // statsd_config.proto Alert message that defines this tracker. const Alert mAlert; // 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). const size_t mNumPastBuckets; Loading @@ -59,8 +64,9 @@ private: // Index of the oldest bucket (i.e. the next bucket to be overwritten). size_t mOldestBucketIndex = 0; // If mSumPastCounters + currentCount > mThresholdGt --> Anomaly! const int mThresholdGt; // Timestamp that the refractory period (if this anomaly was declared) ends, in seconds. // If an anomaly was never declared, set to 0. time_t mRefractoryPeriodEndsSec = 0; void declareAnomaly(); Loading
cmds/statsd/src/metrics/CountMetricProducer.cpp +22 −3 Original line number Diff line number Diff line Loading @@ -39,9 +39,7 @@ CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int co const sp<ConditionWizard>& wizard) // TODO: Pass in the start time from MetricsManager, instead of calling time() here. : MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard), mMetric(metric), // TODO: read mAnomalyTracker parameters from config file. mAnomalyTracker(6, 10) { mMetric(metric) { // TODO: evaluate initial conditions. and set mConditionMet. if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) { mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000; Loading @@ -49,6 +47,17 @@ CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int co mBucketSizeNs = LLONG_MAX; } mAnomalyTrackers.reserve(metric.alerts_size()); for (int i = 0; i < metric.alerts_size(); i++) { const Alert& alert = metric.alerts(i); if (alert.trigger_if_sum_gt() > 0 && alert.number_of_buckets() > 0) { mAnomalyTrackers.push_back(std::make_unique<CountAnomalyTracker>(alert)); } else { ALOGW("Ignoring invalid count metric alert: threshold=%lld num_buckets= %d", alert.trigger_if_sum_gt(), alert.number_of_buckets()); } } // TODO: use UidMap if uid->pkg_name is required mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end()); Loading Loading @@ -148,6 +157,11 @@ void CountMetricProducer::onMatchedLogEventInternal( count++; } // TODO: Re-add anomaly detection (similar to): // for (auto& tracker : mAnomalyTrackers) { // tracker->checkAnomaly(mCounter); // } VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(), mCurrentSlicedCounter[eventKey]); } Loading Loading @@ -176,6 +190,11 @@ void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) { counter.second); } // TODO: Re-add anomaly detection (similar to): // for (auto& tracker : mAnomalyTrackers) { // tracker->addPastBucket(mCounter, numBucketsForward); //} // Reset counters mCurrentSlicedCounter.clear(); Loading
cmds/statsd/src/metrics/CountMetricProducer.h +2 −2 Original line number Diff line number Diff line Loading @@ -60,14 +60,14 @@ protected: private: const CountMetric mMetric; CountAnomalyTracker mAnomalyTracker; // Save the past buckets and we can clear when the StatsLogReport is dumped. std::unordered_map<HashableDimensionKey, std::vector<CountBucketInfo>> mPastBuckets; // The current bucket. std::unordered_map<HashableDimensionKey, int> mCurrentSlicedCounter; vector<unique_ptr<CountAnomalyTracker>> mAnomalyTrackers; void flushCounterIfNeeded(const uint64_t newEventTime); }; Loading