diff options
author | Tej Singh <singhtejinder@google.com> | 2019-05-09 18:17:59 -0700 |
---|---|---|
committer | Tej Singh <singhtejinder@google.com> | 2019-05-13 15:37:19 -0700 |
commit | f53d445cef3b2c115a359dec6e888c2ad4dd1694 (patch) | |
tree | 3063c89419d4f43dc8f2a1e147a1da36cef2c77d | |
parent | 0d3acf62ecf5b0e8ec1f6af0321e0831ced2372e (diff) |
Persist active metric status across system server
Previously, all metrics/configs would deactivate on system server death.
Now, active status is restored.
Bug: 129717537
Test: bit statsd_test:*
Test: libprotoutil_test:ProtoOutputStream*
Change-Id: Idf372457f60a931a2d00176a5eab58c534a25e41
-rw-r--r-- | cmds/statsd/src/StatsLogProcessor.cpp | 45 | ||||
-rw-r--r-- | cmds/statsd/src/StatsLogProcessor.h | 25 | ||||
-rw-r--r-- | cmds/statsd/src/StatsService.cpp | 13 | ||||
-rw-r--r-- | cmds/statsd/src/StatsService.h | 1 | ||||
-rw-r--r-- | cmds/statsd/src/active_config_list.proto | 11 | ||||
-rw-r--r-- | cmds/statsd/src/metrics/MetricProducer.cpp | 37 | ||||
-rw-r--r-- | cmds/statsd/src/metrics/MetricProducer.h | 17 | ||||
-rw-r--r-- | cmds/statsd/src/metrics/MetricsManager.cpp | 4 | ||||
-rw-r--r-- | cmds/statsd/src/metrics/MetricsManager.h | 3 | ||||
-rw-r--r-- | cmds/statsd/tests/StatsLogProcessor_test.cpp | 237 | ||||
-rw-r--r-- | libs/protoutil/include/android/util/ProtoOutputStream.h | 1 | ||||
-rw-r--r-- | libs/protoutil/src/ProtoOutputStream.cpp | 17 | ||||
-rw-r--r-- | libs/protoutil/tests/ProtoOutputStream_test.cpp | 85 |
13 files changed, 454 insertions, 42 deletions
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index ec02b121d0dd..4e0a8ebdf380 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -611,11 +611,8 @@ void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key, void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) { std::lock_guard<std::mutex> lock(mMetricsMutex); - const int64_t timeNs = getElapsedRealtimeNs(); // Do not write to disk if we already have in the last few seconds. - // This is to avoid overwriting files that would have the same name if we - // write twice in the same second. if (static_cast<unsigned long long> (timeNs) < mLastActiveMetricsWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) { ALOGI("Statsd skipping writing active metrics to disk. Already wrote data in last %d seconds", @@ -625,13 +622,7 @@ void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) { mLastActiveMetricsWriteNs = timeNs; ProtoOutputStream proto; - for (const auto& pair : mMetricsManagers) { - const sp<MetricsManager>& metricsManager = pair.second; - uint64_t configToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG); - metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, &proto); - proto.end(configToken); - } + WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, DEVICE_SHUTDOWN, &proto); string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR); StorageManager::deleteFile(file_name.c_str()); @@ -644,9 +635,24 @@ void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) { proto.flush(fd.get()); } -void StatsLogProcessor::LoadActiveConfigsFromDisk() { +void StatsLogProcessor::WriteActiveConfigsToProtoOutputStream( + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { std::lock_guard<std::mutex> lock(mMetricsMutex); + WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, reason, proto); +} +void StatsLogProcessor::WriteActiveConfigsToProtoOutputStreamLocked( + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { + for (const auto& pair : mMetricsManagers) { + const sp<MetricsManager>& metricsManager = pair.second; + uint64_t configToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG); + metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, reason, proto); + proto->end(configToken); + } +} +void StatsLogProcessor::LoadActiveConfigsFromDisk() { + std::lock_guard<std::mutex> lock(mMetricsMutex); string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR); int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); if (-1 == fd) { @@ -670,6 +676,19 @@ void StatsLogProcessor::LoadActiveConfigsFromDisk() { StorageManager::deleteFile(file_name.c_str()); return; } + // Passing in mTimeBaseNs only works as long as we only load from disk is when statsd starts. + SetConfigsActiveStateLocked(activeConfigList, mTimeBaseNs); + StorageManager::deleteFile(file_name.c_str()); +} + +void StatsLogProcessor::SetConfigsActiveState(const ActiveConfigList& activeConfigList, + int64_t currentTimeNs) { + std::lock_guard<std::mutex> lock(mMetricsMutex); + SetConfigsActiveStateLocked(activeConfigList, currentTimeNs); +} + +void StatsLogProcessor::SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList, + int64_t currentTimeNs) { for (int i = 0; i < activeConfigList.config_size(); i++) { const auto& config = activeConfigList.config(i); ConfigKey key(config.uid(), config.id()); @@ -679,11 +698,9 @@ void StatsLogProcessor::LoadActiveConfigsFromDisk() { continue; } VLOG("Setting active config %s", key.ToString().c_str()); - it->second->loadActiveConfig(config, mTimeBaseNs); + it->second->loadActiveConfig(config, currentTimeNs); } VLOG("Successfully loaded %d active configs.", activeConfigList.config_size()); - - StorageManager::deleteFile(file_name.c_str()); } void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason, diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 0dc597b4cb02..92aa425a731d 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -31,17 +31,6 @@ namespace android { namespace os { namespace statsd { -// Keep this in sync with DumpReportReason enum in stats_log.proto -enum DumpReportReason { - DEVICE_SHUTDOWN = 1, - CONFIG_UPDATED = 2, - CONFIG_REMOVED = 3, - GET_DATA_CALLED = 4, - ADB_DUMP = 5, - CONFIG_RESET = 6, - STATSCOMPANION_DIED = 7, - TERMINATION_SIGNAL_RECEIVED = 8 -}; class StatsLogProcessor : public ConfigListener { public: @@ -92,9 +81,16 @@ public: /* Persist configs containing metrics with active activations to disk. */ void SaveActiveConfigsToDisk(int64_t currentTimeNs); + /* Writes the current active status/ttl for all configs and metrics to ProtoOutputStream. */ + void WriteActiveConfigsToProtoOutputStream( + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); + /* Load configs containing metrics with active activations from disk. */ void LoadActiveConfigsFromDisk(); + /* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */ + void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs); + // Reset all configs. void resetConfigs(); @@ -158,6 +154,12 @@ private: void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs); + void WriteActiveConfigsToProtoOutputStreamLocked( + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); + + void SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList, + int64_t currentTimeNs); + void WriteDataToDiskLocked(const DumpReportReason dumpReportReason, const DumpLatency dumpLatency); void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs, @@ -224,6 +226,7 @@ private: FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes); + FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 623a1f2cdafb..8191d37bb603 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1478,8 +1478,21 @@ void StatsService::binderDied(const wp <IBinder>& who) { StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec()); if (mProcessor != nullptr) { ALOGW("Reset statsd upon system server restarts."); + int64_t systemServerRestartNs = getElapsedRealtimeNs(); + ProtoOutputStream proto; + mProcessor->WriteActiveConfigsToProtoOutputStream(systemServerRestartNs, + STATSCOMPANION_DIED, &proto); + mProcessor->WriteDataToDisk(STATSCOMPANION_DIED, FAST); mProcessor->resetConfigs(); + + std::string serializedActiveConfigs; + if (proto.serializeToString(&serializedActiveConfigs)) { + ActiveConfigList activeConfigs; + if (activeConfigs.ParseFromString(serializedActiveConfigs)) { + mProcessor->SetConfigsActiveState(activeConfigs, systemServerRestartNs); + } + } } mAnomalyAlarmMonitor->setStatsCompanionService(nullptr); mPeriodicAlarmMonitor->setStatsCompanionService(nullptr); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 936f7db52c38..a4e6d7fec4cc 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -427,6 +427,7 @@ private: std::shared_ptr<LogEventQueue> mEventQueue; + FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); FRIEND_TEST(StatsServiceTest, TestAddConfig_simple); FRIEND_TEST(StatsServiceTest, TestAddConfig_empty); FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid); diff --git a/cmds/statsd/src/active_config_list.proto b/cmds/statsd/src/active_config_list.proto index ef8e50bb2596..992983358ae6 100644 --- a/cmds/statsd/src/active_config_list.proto +++ b/cmds/statsd/src/active_config_list.proto @@ -26,7 +26,18 @@ message ActiveEventActivation { // Time left in activation. When this proto is loaded after device boot, // the activation should be set to active for this duration. + // This field will only be set when the state is ACTIVE optional int64 remaining_ttl_nanos = 2; + + enum State { + UNNKNOWN = 0; + // This metric should activate for remaining_ttl_nanos when we load the activations. + ACTIVE = 1; + // When we load the activations, this metric should activate on next boot for the tll + // specified in the config. + ACTIVATE_ON_BOOT = 2; + } + optional State state = 3; } message ActiveMetric { diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 9ad7f09ab512..d913427a05dc 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -19,6 +19,7 @@ #include "MetricProducer.h" using android::util::FIELD_COUNT_REPEATED; +using android::util::FIELD_TYPE_ENUM; using android::util::FIELD_TYPE_INT32; using android::util::FIELD_TYPE_INT64; using android::util::FIELD_TYPE_MESSAGE; @@ -37,6 +38,7 @@ const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2; // for ActiveEventActivation const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1; const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2; +const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3; void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { if (!mIsActive) { @@ -165,17 +167,21 @@ void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric, continue; } auto& activation = it->second; - // We don't want to change the ttl for future activations, so we set the start_ns - // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos - activation->start_ns = - currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns; - activation->state = ActivationState::kActive; - mIsActive = true; + if (activeEventActivation.state() == ActiveEventActivation::ACTIVE) { + // We don't want to change the ttl for future activations, so we set the start_ns + // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos + activation->start_ns = + currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns; + activation->state = ActivationState::kActive; + mIsActive = true; + } else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) { + activation->state = ActivationState::kActiveOnBoot; + } } } void MetricProducer::writeActiveMetricToProtoOutputStream( - int64_t currentTimeNs, ProtoOutputStream* proto) { + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId); for (auto& it : mEventActivationMap) { const int atom_matcher_index = it.first; @@ -196,9 +202,22 @@ void MetricProducer::writeActiveMetricToProtoOutputStream( activation->start_ns + activation->ttl_ns - currentTimeNs; proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS, (long long)remainingTtlNs); + proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE, + ActiveEventActivation::ACTIVE); + } else if (ActivationState::kActiveOnBoot == activation->state) { - proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS, - (long long)activation->ttl_ns); + if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) { + proto->write( + FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS, + (long long)activation->ttl_ns); + proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE, + ActiveEventActivation::ACTIVE); + } else if (reason == STATSCOMPANION_DIED) { + // We are saving because of system server death, not due to a device shutdown. + // Next time we load, we do not want to activate metrics that activate on boot. + proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE, + ActiveEventActivation::ACTIVATE_ON_BOOT); + } } proto->end(activationToken); } diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index ec3484c80209..3ddbef44718d 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -35,6 +35,18 @@ namespace android { namespace os { namespace statsd { +// Keep this in sync with DumpReportReason enum in stats_log.proto +enum DumpReportReason { + DEVICE_SHUTDOWN = 1, + CONFIG_UPDATED = 2, + CONFIG_REMOVED = 3, + GET_DATA_CALLED = 4, + ADB_DUMP = 5, + CONFIG_RESET = 6, + STATSCOMPANION_DIED = 7, + TERMINATION_SIGNAL_RECEIVED = 8 +}; + // 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 @@ -244,7 +256,7 @@ public: void flushIfExpire(int64_t elapsedTimestampNs); void writeActiveMetricToProtoOutputStream( - int64_t currentTimeNs, ProtoOutputStream* proto); + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); protected: virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0; virtual void onSlicedConditionMayChangeLocked(bool overallCondition, @@ -268,8 +280,6 @@ protected: return mIsActive; } - void prepActiveForBootIfNecessaryLocked(int64_t currentTimeNs); - void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs); virtual void prepareFirstBucketLocked() {}; @@ -412,6 +422,7 @@ protected: FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes); + FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 947f37782fcc..207a7dd87215 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -528,14 +528,14 @@ void MetricsManager::loadActiveConfig(const ActiveConfig& config, int64_t curren } void MetricsManager::writeActiveConfigToProtoOutputStream( - int64_t currentTimeNs, ProtoOutputStream* proto) { + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId()); proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid()); for (int metricIndex : mMetricIndexesWithActivation) { const auto& metric = mAllMetricProducers[metricIndex]; const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_ACTIVE_CONFIG_METRIC); - metric->writeActiveMetricToProtoOutputStream(currentTimeNs, proto); + metric->writeActiveMetricToProtoOutputStream(currentTimeNs, reason, proto); proto->end(metricToken); } } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 818131efe138..da3be061cb57 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -141,7 +141,7 @@ public: void loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs); void writeActiveConfigToProtoOutputStream( - int64_t currentTimeNs, ProtoOutputStream* proto); + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); private: // For test only. @@ -290,6 +290,7 @@ private: FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes); + FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); }; } // namespace statsd diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 49b4e904d93c..fe25a257aa67 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -13,12 +13,14 @@ // limitations under the License. #include "StatsLogProcessor.h" +#include "StatsService.h" #include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "packages/UidMap.h" +#include "storage/StorageManager.h" #include "statslog.h" #include <gmock/gmock.h> @@ -97,7 +99,8 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { ConfigKey key(100, 12345); EXPECT_CALL(mockMetricsManager, byteSize()) .Times(1) - .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95))); + .WillRepeatedly(::testing::Return(int( + StatsdStats::kMaxMetricsBytesPerConfig * .95))); // Expect only one broadcast despite always returning a size that should trigger broadcast. p.flushIfNecessaryLocked(1, key, mockMetricsManager); @@ -128,7 +131,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { ConfigKey key(100, 12345); EXPECT_CALL(mockMetricsManager, byteSize()) .Times(1) - .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2))); + .WillRepeatedly(::testing::Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2))); EXPECT_CALL(mockMetricsManager, dropData(_)).Times(1); @@ -1482,6 +1485,236 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi // }}}--------------------------------------------------------------------------- } +TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { + int uid = 9876; + long configId = 12341; + + // Create config with 3 metrics: + // Metric 1: Activate on 2 activations, 1 on boot, 1 immediate. + // Metric 2: Activate on 2 activations, 1 on boot, 1 immediate. + // Metric 3: Always active + StatsdConfig config1; + config1.set_id(configId); + config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + auto jobStartMatcher = CreateStartScheduledJobAtomMatcher(); + auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher(); + *config1.add_atom_matcher() = wakelockAcquireMatcher; + *config1.add_atom_matcher() = screenOnMatcher; + *config1.add_atom_matcher() = jobStartMatcher; + *config1.add_atom_matcher() = jobFinishMatcher; + + long metricId1 = 1234561; + long metricId2 = 1234562; + long metricId3 = 1234563; + + 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 countMetric3 = config1.add_count_metric(); + countMetric3->set_id(metricId3); + countMetric3->set_what(wakelockAcquireMatcher.id()); + countMetric3->set_bucket(FIVE_MINUTES); + + // Metric 1 activates on boot for wakelock acquire, immediately for screen on. + auto metric1Activation = config1.add_metric_activation(); + metric1Activation->set_metric_id(metricId1); + auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); + metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric1ActivationTrigger1->set_ttl_seconds(100); + metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT); + auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); + metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); + metric1ActivationTrigger2->set_ttl_seconds(200); + metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); + + // Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish. + auto metric2Activation = config1.add_metric_activation(); + metric2Activation->set_metric_id(metricId2); + auto metric2ActivationTrigger1 = metric2Activation->add_event_activation(); + metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id()); + metric2ActivationTrigger1->set_ttl_seconds(100); + metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT); + auto metric2ActivationTrigger2 = metric2Activation->add_event_activation(); + metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id()); + metric2ActivationTrigger2->set_ttl_seconds(200); + metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); + + // Send the config. + StatsService service(nullptr, nullptr); + string serialized = config1.SerializeAsString(); + service.addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()}); + + // Make sure the config is stored on disk. Otherwise, we will not reset on system server death. + StatsdConfig tmpConfig; + ConfigKey cfgKey1(uid, configId); + EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig)); + + // Metric 1 is not active. + // Metric 2 is not active. + // Metric 3 is active. + // {{{--------------------------------------------------------------------------- + sp<StatsLogProcessor> processor = service.mProcessor; + 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()); + EXPECT_EQ(3, metricsManager1->mAllMetricProducers.size()); + + auto& metricProducer1 = metricsManager1->mAllMetricProducers[0]; + EXPECT_EQ(metricId1, metricProducer1->getMetricId()); + EXPECT_FALSE(metricProducer1->isActive()); + + auto& metricProducer2 = metricsManager1->mAllMetricProducers[1]; + EXPECT_EQ(metricId2, metricProducer2->getMetricId()); + EXPECT_FALSE(metricProducer2->isActive()); + + auto& metricProducer3 = metricsManager1->mAllMetricProducers[2]; + EXPECT_EQ(metricId3, metricProducer3->getMetricId()); + EXPECT_TRUE(metricProducer3->isActive()); + + // Check event activations. + EXPECT_EQ(metricsManager1->mAllAtomMatchers.size(), 4); + EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(), + metric1ActivationTrigger1->atom_matcher_id()); + const auto& activation1 = metricProducer1->mEventActivationMap.at(0); + EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kNotActive, activation1->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType); + + EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(), + metric1ActivationTrigger2->atom_matcher_id()); + const auto& activation2 = metricProducer1->mEventActivationMap.at(1); + EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns); + EXPECT_EQ(0, activation2->start_ns); + EXPECT_EQ(kNotActive, activation2->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType); + + EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(), + metric2ActivationTrigger1->atom_matcher_id()); + const auto& activation3 = metricProducer2->mEventActivationMap.at(2); + EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns); + EXPECT_EQ(0, activation3->start_ns); + EXPECT_EQ(kNotActive, activation3->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType); + + EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(), + metric2ActivationTrigger2->atom_matcher_id()); + const auto& activation4 = metricProducer2->mEventActivationMap.at(3); + EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns); + EXPECT_EQ(0, activation4->start_ns); + EXPECT_EQ(kNotActive, activation4->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType); + // }}}------------------------------------------------------------------------------ + + // Trigger Activation 1 for Metric 1. Should activate on boot. + // Trigger Activation 4 for Metric 2. Should activate immediately. + long configAddedTimeNs = metricsManager1->mLastReportTimeNs; + std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; + auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 1 + configAddedTimeNs); + processor->OnLogEvent(event.get()); + + event = CreateFinishScheduledJobEvent(attributions1, "finish1", 2 + configAddedTimeNs); + processor->OnLogEvent(event.get()); + + // Metric 1 is not active; Activation 1 set to kActiveOnBoot + // Metric 2 is active. Activation 4 set to kActive + // Metric 3 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_FALSE(metricProducer1->isActive()); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kActiveOnBoot, activation1->state); + EXPECT_EQ(0, activation2->start_ns); + EXPECT_EQ(kNotActive, activation2->state); + + EXPECT_TRUE(metricProducer2->isActive()); + EXPECT_EQ(0, activation3->start_ns); + EXPECT_EQ(kNotActive, activation3->state); + EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns); + EXPECT_EQ(kActive, activation4->state); + + EXPECT_TRUE(metricProducer3->isActive()); + // }}}----------------------------------------------------------------------------- + + // Can't fake time with StatsService. + // Lets get a time close to the system server death time and make sure it's sane. + int64_t approximateSystemServerDeath = getElapsedRealtimeNs(); + EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs); + EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs); + + // System server dies. + service.binderDied(nullptr); + + // We should have a new metrics manager. Lets get it and ensure activation status is restored. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor->mMetricsManagers.size()); + it = processor->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager2 = it->second; + EXPECT_TRUE(metricsManager2->isActive()); + EXPECT_EQ(3, metricsManager2->mAllMetricProducers.size()); + + auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0]; + EXPECT_EQ(metricId1, metricProducer1001->getMetricId()); + EXPECT_FALSE(metricProducer1001->isActive()); + + auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1]; + EXPECT_EQ(metricId2, metricProducer1002->getMetricId()); + EXPECT_TRUE(metricProducer1002->isActive()); + + auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2]; + EXPECT_EQ(metricId3, metricProducer1003->getMetricId()); + EXPECT_TRUE(metricProducer1003->isActive()); + + // Check event activations. + // Activation 1 is kActiveOnBoot. + // Activation 2 and 3 are not active. + // Activation 4 is active. + EXPECT_EQ(metricsManager2->mAllAtomMatchers.size(), 4); + EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(), + metric1ActivationTrigger1->atom_matcher_id()); + const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0); + EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns); + EXPECT_EQ(0, activation1001->start_ns); + EXPECT_EQ(kActiveOnBoot, activation1001->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType); + + EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(), + metric1ActivationTrigger2->atom_matcher_id()); + const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1); + EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns); + EXPECT_EQ(0, activation1002->start_ns); + EXPECT_EQ(kNotActive, activation1002->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType); + + EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(), + metric2ActivationTrigger1->atom_matcher_id()); + const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2); + EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns); + EXPECT_EQ(0, activation1003->start_ns); + EXPECT_EQ(kNotActive, activation1003->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType); + + EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(), + metric2ActivationTrigger2->atom_matcher_id()); + const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3); + EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns); + EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns); + EXPECT_EQ(kActive, activation1004->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType); + // }}}------------------------------------------------------------------------------ +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h index 360e8d3e8c71..60d03180c09d 100644 --- a/libs/protoutil/include/android/util/ProtoOutputStream.h +++ b/libs/protoutil/include/android/util/ProtoOutputStream.h @@ -123,6 +123,7 @@ public: size_t size(); // Get the size of the serialized protobuf. sp<ProtoReader> data(); // Get the reader apis of the data. bool flush(int fd); // Flush data directly to a file descriptor. + bool serializeToString(std::string* out); // Serializes the proto to a string. /** * Clears the ProtoOutputStream so the buffer can be reused instead of deallocation/allocation again. diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp index ccbb83b2d342..98a68c6482b5 100644 --- a/libs/protoutil/src/ProtoOutputStream.cpp +++ b/libs/protoutil/src/ProtoOutputStream.cpp @@ -448,6 +448,23 @@ ProtoOutputStream::flush(int fd) return true; } +bool +ProtoOutputStream::serializeToString(std::string* out) +{ + if (out == nullptr) return false; + if (!compact()) return false; + + + sp<ProtoReader> reader = mBuffer->read(); + out->reserve(reader->size()); + while (reader->hasNext()) { + out->append(static_cast<const char*>(static_cast<const void*>(reader->readBuffer())), + reader->currentToRead()); + reader->move(reader->currentToRead()); + } + return true; +} + sp<ProtoReader> ProtoOutputStream::data() { diff --git a/libs/protoutil/tests/ProtoOutputStream_test.cpp b/libs/protoutil/tests/ProtoOutputStream_test.cpp index 9d357f3c3363..6282fd553eae 100644 --- a/libs/protoutil/tests/ProtoOutputStream_test.cpp +++ b/libs/protoutil/tests/ProtoOutputStream_test.cpp @@ -88,6 +88,50 @@ TEST(ProtoOutputStreamTest, Primitives) { EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO); } +TEST(ProtoOutputStreamTest, SerializeToStringPrimitives) { + std::string s = "hello"; + const char b[5] = { 'a', 'p', 'p', 'l', 'e' }; + + ProtoOutputStream proto; + EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | PrimitiveProto::kValInt32FieldNumber, 123)); + EXPECT_TRUE(proto.write(FIELD_TYPE_INT64 | PrimitiveProto::kValInt64FieldNumber, -1LL)); + EXPECT_TRUE(proto.write(FIELD_TYPE_FLOAT | PrimitiveProto::kValFloatFieldNumber, -23.5f)); + EXPECT_TRUE(proto.write(FIELD_TYPE_DOUBLE | PrimitiveProto::kValDoubleFieldNumber, 324.5)); + EXPECT_TRUE(proto.write(FIELD_TYPE_UINT32 | PrimitiveProto::kValUint32FieldNumber, 3424)); + EXPECT_TRUE(proto.write(FIELD_TYPE_UINT64 | PrimitiveProto::kValUint64FieldNumber, 57LL)); + EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED32 | PrimitiveProto::kValFixed32FieldNumber, -20)); + EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED64 | PrimitiveProto::kValFixed64FieldNumber, -37LL)); + EXPECT_TRUE(proto.write(FIELD_TYPE_BOOL | PrimitiveProto::kValBoolFieldNumber, true)); + EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | PrimitiveProto::kValStringFieldNumber, s)); + EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | PrimitiveProto::kValBytesFieldNumber, b, 5)); + EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED32 | PrimitiveProto::kValSfixed32FieldNumber, 63)); + EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED64 | PrimitiveProto::kValSfixed64FieldNumber, -54)); + EXPECT_TRUE(proto.write(FIELD_TYPE_SINT32 | PrimitiveProto::kValSint32FieldNumber, -533)); + EXPECT_TRUE(proto.write(FIELD_TYPE_SINT64 | PrimitiveProto::kValSint64FieldNumber, -61224762453LL)); + EXPECT_TRUE(proto.write(FIELD_TYPE_ENUM | PrimitiveProto::kValEnumFieldNumber, 2)); + + PrimitiveProto primitives; + std::string serialized; + ASSERT_TRUE(proto.serializeToString(&serialized)); + ASSERT_TRUE(primitives.ParseFromString(serialized)); + EXPECT_EQ(primitives.val_int32(), 123); + EXPECT_EQ(primitives.val_int64(), -1); + EXPECT_EQ(primitives.val_float(), -23.5f); + EXPECT_EQ(primitives.val_double(), 324.5f); + EXPECT_EQ(primitives.val_uint32(), 3424); + EXPECT_EQ(primitives.val_uint64(), 57); + EXPECT_EQ(primitives.val_fixed32(), -20); + EXPECT_EQ(primitives.val_fixed64(), -37); + EXPECT_EQ(primitives.val_bool(), true); + EXPECT_THAT(primitives.val_string(), StrEq(s.c_str())); + EXPECT_THAT(primitives.val_bytes(), StrEq("apple")); + EXPECT_EQ(primitives.val_sfixed32(), 63); + EXPECT_EQ(primitives.val_sfixed64(), -54); + EXPECT_EQ(primitives.val_sint32(), -533); + EXPECT_EQ(primitives.val_sint64(), -61224762453LL); + EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO); +} + TEST(ProtoOutputStreamTest, Complex) { std::string name1 = "cat"; std::string name2 = "dog"; @@ -127,6 +171,47 @@ TEST(ProtoOutputStreamTest, Complex) { EXPECT_THAT(log2.data(), StrEq("food")); } +TEST(ProtoOutputStreamTest, SerializeToStringComplex) { + std::string name1 = "cat"; + std::string name2 = "dog"; + const char data1[6] = { 'f', 'u', 'n', 'n', 'y', '!' }; + const char data2[4] = { 'f', 'o', 'o', 'd' }; + + ProtoOutputStream proto; + EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 23)); + EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 101)); + EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, -72)); + uint64_t token1 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber); + EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 12)); + EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name1)); + // specify the length to test the write(id, bytes, length) function. + EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data1, 5)); + proto.end(token1); + uint64_t token2 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber); + EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 98)); + EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name2)); + EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data2, 4)); + proto.end(token2); + + ComplexProto complex; + std::string serialized; + ASSERT_TRUE(proto.serializeToString(&serialized)); + ASSERT_TRUE(complex.ParseFromString(serialized)); + EXPECT_EQ(complex.ints_size(), 3); + EXPECT_EQ(complex.ints(0), 23); + EXPECT_EQ(complex.ints(1), 101); + EXPECT_EQ(complex.ints(2), -72); + EXPECT_EQ(complex.logs_size(), 2); + ComplexProto::Log log1 = complex.logs(0); + EXPECT_EQ(log1.id(), 12); + EXPECT_THAT(log1.name(), StrEq(name1.c_str())); + EXPECT_THAT(log1.data(), StrEq("funny")); // should not contain '!' + ComplexProto::Log log2 = complex.logs(1); + EXPECT_EQ(log2.id(), 98); + EXPECT_THAT(log2.name(), StrEq(name2.c_str())); + EXPECT_THAT(log2.data(), StrEq("food")); +} + TEST(ProtoOutputStreamTest, Reusability) { ProtoOutputStream proto; EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 32)); |