From a9a310ec54729a7007954a6c45453b3f6d68a3d5 Mon Sep 17 00:00:00 2001 From: Chenjie Yu Date: Wed, 6 Feb 2019 13:40:10 -0800 Subject: [PATCH] metric activation on boot Bug: 123038368 Test: unit test Change-Id: Id374bdfd8d15264ada0e7bac0388080be308ac8f --- cmds/statsd/src/StatsLogProcessor.cpp | 7 +- cmds/statsd/src/StatsLogProcessor.h | 1 + cmds/statsd/src/metrics/MetricProducer.cpp | 36 +++++- cmds/statsd/src/metrics/MetricProducer.h | 21 ++- cmds/statsd/src/metrics/MetricsManager.h | 9 +- .../src/metrics/metrics_manager_util.cpp | 2 + cmds/statsd/src/statsd_config.proto | 7 + cmds/statsd/tests/StatsLogProcessor_test.cpp | 121 ++++++++++++++++++ 8 files changed, 191 insertions(+), 13 deletions(-) diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 250f5bfd06859..dd18bd4cc8ad8 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -526,9 +526,10 @@ void StatsLogProcessor::WriteMetricsActivationToDisk(int64_t currentTimeNs) { proto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_ID, (long long)pair.first.GetId()); proto.write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_UID, pair.first.GetUid()); - vector acrtiveMetrics; - pair.second->getActiveMetrics(acrtiveMetrics); - for (const MetricProducer* metric : acrtiveMetrics) { + vector activeMetrics; + pair.second->prepForShutDown(currentTimeNs); + pair.second->getActiveMetrics(activeMetrics); + for (MetricProducer* metric : activeMetrics) { if (metric->isActive()) { uint64_t metricToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_ACTIVE_METRIC); diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 77d9a2f5f4188..caf1a713986df 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -195,6 +195,7 @@ private: FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast); FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); + FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2); diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index b362e37cd1941..495138ee9b774 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -107,6 +107,10 @@ void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedT if (it == mEventActivationMap.end()) { return; } + if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT) { + it->second.state = ActivationState::kActiveOnBoot; + return; + } it->second.activation_ns = elapsedTimestampNs; it->second.state = ActivationState::kActive; mIsActive = true; @@ -116,12 +120,19 @@ void MetricProducer::setActiveLocked(int64_t currentTimeNs, int64_t remainingTtl if (mEventActivationMap.size() == 0) { return; } - auto& activation = mEventActivationMap.begin()->second; - activation.activation_ns = currentTimeNs + remainingTtlNs - activation.ttl_ns; - activation.state = kActive; - mIsActive = true; - VLOG("setting new activation time to %lld, %lld, %lld", (long long)activation.activation_ns, - (long long)currentTimeNs, (long long)remainingTtlNs); + for (auto& pair : mEventActivationMap) { + auto& activation = pair.second; + if (activation.ttl_ns >= remainingTtlNs) { + activation.activation_ns = currentTimeNs + remainingTtlNs - activation.ttl_ns; + activation.state = kActive; + mIsActive = true; + VLOG("setting new activation time to %lld, %lld, %lld", + (long long)activation.activation_ns, (long long)currentTimeNs, + (long long)remainingTtlNs); + return; + } + } + ALOGE("Required ttl is longer than all possible activations."); } int64_t MetricProducer::getRemainingTtlNsLocked(int64_t currentTimeNs) const { @@ -135,6 +146,19 @@ int64_t MetricProducer::getRemainingTtlNsLocked(int64_t currentTimeNs) const { return maxTtl; } +void MetricProducer::prepActiveForBootIfNecessaryLocked(int64_t currentTimeNs) { + if (mActivationType != MetricActivation::ACTIVATE_ON_BOOT) { + return; + } + for (auto& activation : mEventActivationMap) { + if (activation.second.state == kActiveOnBoot) { + activation.second.state = kActive; + activation.second.activation_ns = currentTimeNs; + mIsActive = true; + } + } +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index ca37bbb73b45a..849cb76ec392d 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -37,12 +37,13 @@ namespace statsd { // If the metric has no activation requirement, it will be active once the metric producer is // created. // If the metric needs to be activated by atoms, the metric producer will start -// with kNotActive state, turn to kActive when the activation event arrives, become kNotActive -// when it reaches the duration limit (timebomb). If the activation event arrives again before -// or after it expires, the event producer will be re-activated and ttl will be reset. +// with kNotActive state, turn to kActive or kActiveOnBoot when the activation event arrives, become +// kNotActive when it reaches the duration limit (timebomb). If the activation event arrives again +// before or after it expires, the event producer will be re-activated and ttl will be reset. enum ActivationState { kNotActive = 0, kActive = 1, + kActiveOnBoot = 2, }; // A MetricProducer is responsible for compute one single metrics, creating stats log report, and @@ -218,8 +219,17 @@ public: return isActiveLocked(); } + void prepActiveForBootIfNecessary(int64_t currentTimeNs) { + std::lock_guard lock(mMutex); + prepActiveForBootIfNecessaryLocked(currentTimeNs); + } + void addActivation(int activationTrackerIndex, int64_t ttl_seconds); + inline void setActivationType(const MetricActivation::ActivationType& activationType) { + mActivationType = activationType; + } + void flushIfExpire(int64_t elapsedTimestampNs); protected: @@ -243,6 +253,8 @@ protected: return mIsActive; } + void prepActiveForBootIfNecessaryLocked(int64_t currentTimeNs); + int64_t getRemainingTtlNsLocked(int64_t currentTimeNs) const; void setActiveLocked(int64_t currentTimeNs, int64_t remainingTtlNs); @@ -367,9 +379,12 @@ protected: bool mIsActive; + MetricActivation::ActivationType mActivationType; + FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); + FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 80982c37dc6a0..1fa221da335e9 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -128,7 +128,7 @@ public: return mIsActive; } - inline void getActiveMetrics(std::vector& metrics) const { + inline void getActiveMetrics(std::vector& metrics) const { for (const auto& metric : mAllMetricProducers) { if (metric->isActive()) { metrics.push_back(metric.get()); @@ -136,6 +136,12 @@ public: } } + inline void prepForShutDown(int64_t currentTimeNs) { + for (const auto& metric : mAllMetricProducers) { + metric->prepActiveForBootIfNecessary(currentTimeNs); + } + } + void setActiveMetrics(ActiveConfig config, int64_t currentTimeNs); private: @@ -267,6 +273,7 @@ private: FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); + FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 180a1ae07523d..463b5a0975858 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -725,6 +725,8 @@ bool initMetricActivations(const ConfigKey& key, const StatsdConfig& config, ALOGE("Invalid metric tracker index."); return false; } + allMetricProducers[metricTrackerIndex]->setActivationType( + metric_activation.activation_type()); metricsWithActivation.push_back(metricTrackerIndex); for (int j = 0; j < metric_activation.event_activation_size(); ++j) { const EventActivation& activation = metric_activation.event_activation(j); diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 9d3a66902804f..5c6d548ad13ac 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -379,6 +379,13 @@ message EventActivation { message MetricActivation { optional int64 metric_id = 1; + enum ActivationType { + UNKNOWN = 0; + ACTIVATE_IMMEDIATELY = 1; + ACTIVATE_ON_BOOT = 2; + } + optional ActivationType activation_type = 3; + repeated EventActivation event_activation = 2; } diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 64008b50b0138..60df165f102c9 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -574,6 +574,127 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { EXPECT_EQ(timeBase2 + ttl6 - activation1006.ttl_ns, activation1003.activation_ns); } +TEST(StatsLogProcessorTest, TestActivationOnBoot) { + int uid = 1111; + + // Setup a simple config, no activation + StatsdConfig config1; + config1.set_id(12341); + config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); + *config1.add_atom_matcher() = wakelockAcquireMatcher; + + long metricId1 = 1234561; + long metricId2 = 1234562; + auto countMetric1 = config1.add_count_metric(); + countMetric1->set_id(metricId1); + countMetric1->set_what(wakelockAcquireMatcher.id()); + countMetric1->set_bucket(FIVE_MINUTES); + + auto countMetric2 = config1.add_count_metric(); + countMetric2->set_id(metricId2); + countMetric2->set_what(wakelockAcquireMatcher.id()); + countMetric2->set_bucket(FIVE_MINUTES); + + auto metric1Activation = config1.add_metric_activation(); + metric1Activation->set_metric_id(metricId1); + metric1Activation->set_activation_type(MetricActivation::ACTIVATE_ON_BOOT); + auto metric1ActivationTrigger = metric1Activation->add_event_activation(); + metric1ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric1ActivationTrigger->set_ttl_seconds(100); + + ConfigKey cfgKey1(uid, 12341); + long timeBase1 = 1; + sp processor = + CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); + + EXPECT_EQ(1, processor->mMetricsManagers.size()); + auto it = processor->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager1 = it->second; + EXPECT_TRUE(metricsManager1->isActive()); + + auto metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer1 = *metricIt; + EXPECT_FALSE(metricProducer1->isActive()); + + metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer2 = *metricIt; + EXPECT_TRUE(metricProducer2->isActive()); + + const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second; + EXPECT_EQ(100 * NS_PER_SEC, activation1.ttl_ns); + EXPECT_EQ(0, activation1.activation_ns); + EXPECT_EQ(kNotActive, activation1.state); + + std::vector attributions1 = {CreateAttribution(111, "App1")}; + auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1); + processor->OnLogEvent(event.get()); + + EXPECT_FALSE(metricProducer1->isActive()); + EXPECT_EQ(0, activation1.activation_ns); + EXPECT_EQ(kActiveOnBoot, activation1.state); + + int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; + + processor->WriteMetricsActivationToDisk(shutDownTime); + EXPECT_TRUE(metricProducer1->isActive()); + int64_t ttl1 = metricProducer1->getRemainingTtlNs(shutDownTime); + EXPECT_EQ(100 * NS_PER_SEC, ttl1); + + long timeBase2 = 1000; + sp processor2 = + CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); + + EXPECT_EQ(1, processor2->mMetricsManagers.size()); + it = processor2->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor2->mMetricsManagers.end()); + auto& metricsManager1001 = it->second; + EXPECT_TRUE(metricsManager1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1001 = *metricIt; + EXPECT_FALSE(metricProducer1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1002 = *metricIt; + EXPECT_TRUE(metricProducer1002->isActive()); + + const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second; + EXPECT_EQ(100 * NS_PER_SEC, activation1001.ttl_ns); + EXPECT_EQ(0, activation1001.activation_ns); + EXPECT_EQ(kNotActive, activation1001.state); + + processor2->LoadMetricsActivationFromDisk(); + + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_EQ(timeBase2 + ttl1 - activation1001.ttl_ns, activation1001.activation_ns); +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif