diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-02-02 22:49:33 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-02-02 22:49:33 +0000 |
commit | 64eead096a4af38f33357cf109225dac463788c6 (patch) | |
tree | 0832182584e6cef6347a0268e5cfea1aa7ee47ab | |
parent | 5c1c48fe8cd99076df016e78fece7d1a30cba376 (diff) | |
parent | fe94a87a6437fe218c8e0dc58aa608ce197f2f68 (diff) | |
download | StatsD-64eead096a4af38f33357cf109225dac463788c6.tar.gz |
Snap for 9556825 from fe94a87a6437fe218c8e0dc58aa608ce197f2f68 to mainline-adservices-releaseaml_ads_331611190
Change-Id: Ibbe245821d0de8caf3f1ac3b6b9f3c9248240c55
93 files changed, 5996 insertions, 2030 deletions
diff --git a/apex/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java b/apex/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java index 6108a324..028652b9 100644 --- a/apex/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java +++ b/apex/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java @@ -248,6 +248,32 @@ public class LibStatsPullTests { } } + /** + * Tests puller registration/unregistration is processed in order + */ + @Test + public void testPullAtomCallbacksProcessedInOrder() throws Exception { + StatsManager statsManager = mContext.getSystemService(StatsManager.class); + // Upload a config that captures that pulled atom. + createAndAddConfigToStatsd(statsManager); + for(int i = 0; i < 20; i++) { + // Adding & removing the puller back to back to detect any potentional + // threads scheduling dependencies + setStatsPuller(PULL_ATOM_TAG, sPullTimeoutMillis, sCoolDownMillis, sPullReturnValue, + sPullLatencyMillis, sAtomsPerPull); + clearStatsPuller(PULL_ATOM_TAG); + } + // with out-of-order registration - there is a chance that the puller callback is still + // registered - this is wrong and we would like to test it + Thread.sleep(SHORT_SLEEP_MILLIS); + StatsLog.logStart(APP_BREADCRUMB_LABEL); + + // Let the current bucket finish. + Thread.sleep(LONG_SLEEP_MILLIS); + List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId); + assertThat(data.size()).isEqualTo(0); + } + private void createAndAddConfigToStatsd(StatsManager statsManager) throws Exception { sConfigId = System.currentTimeMillis(); long triggerMatcherId = sConfigId + 10; diff --git a/framework/Android.bp b/framework/Android.bp index daf93cb6..c1a01f90 100644 --- a/framework/Android.bp +++ b/framework/Android.bp @@ -91,6 +91,15 @@ java_sdk_library { min_sdk_version: "30", } +java_api_contribution { + name: "framework-statsd-public-stubs", + api_surface: "public", + api_file: "api/current.txt", + visibility: [ + "//build/orchestrator/apis", + ], +} + // JNI library for StatsLog.write cc_library_shared { name: "libstats_jni", diff --git a/framework/test/unittests/AndroidTest.xml b/framework/test/unittests/AndroidTest.xml index e32f16ec..4ab7b646 100644 --- a/framework/test/unittests/AndroidTest.xml +++ b/framework/test/unittests/AndroidTest.xml @@ -14,7 +14,7 @@ limitations under the License. --> <configuration description="Runs Tests for Statsd."> - <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="test-file-name" value="FrameworkStatsdTest.apk" /> <option name="install-arg" value="-g" /> </target_preparer> diff --git a/lib/libkll/Android.bp b/lib/libkll/Android.bp index 94fb0ca2..f17d04e0 100644 --- a/lib/libkll/Android.bp +++ b/lib/libkll/Android.bp @@ -24,6 +24,7 @@ package { cc_library { name: "libkll", host_supported: true, + vendor_available: true, srcs: [ "compactor_stack.cpp", "kll.cpp", @@ -52,6 +53,9 @@ cc_library { cc_test { name: "libkll_test", + tidy_timeout_srcs: [ + "tests/sampler_test.cpp", + ], srcs: [ "tests/**/*.cpp", ], diff --git a/lib/libkll/encoding/Android.bp b/lib/libkll/encoding/Android.bp index b8d3f0a7..54729790 100644 --- a/lib/libkll/encoding/Android.bp +++ b/lib/libkll/encoding/Android.bp @@ -24,6 +24,7 @@ package { cc_library_static { name: "libkll-encoder", host_supported: true, + vendor_available: true, srcs: [ "encoder.cpp", "varint.cpp", diff --git a/lib/libkll/proto/Android.bp b/lib/libkll/proto/Android.bp index a979f648..a827e15f 100644 --- a/lib/libkll/proto/Android.bp +++ b/lib/libkll/proto/Android.bp @@ -24,6 +24,7 @@ package { cc_library_static { name: "libkll-protos", host_supported: true, + vendor_available: true, srcs: ["*.proto"], proto: { export_proto_headers: true, diff --git a/lib/libstatspull/Android.bp b/lib/libstatspull/Android.bp index 60f237c2..0d6a7868 100644 --- a/lib/libstatspull/Android.bp +++ b/lib/libstatspull/Android.bp @@ -48,6 +48,7 @@ cc_defaults { }, }, } + cc_library { name: "libstatspull", defaults: [ @@ -120,7 +121,10 @@ cc_test { "libstatspull", "libstatssocket", ], - test_suites: ["general-tests", "mts-statsd"], + test_suites: [ + "general-tests", + "mts-statsd", + ], test_config: "libstatspull_test.xml", //TODO(b/153588990): Remove when the build system properly separates @@ -143,4 +147,5 @@ cc_test { "-Wno-unused-parameter", ], require_root: true, + min_sdk_version: "30", } diff --git a/lib/libstatspull/stats_pull_atom_callback.cpp b/lib/libstatspull/stats_pull_atom_callback.cpp index 8a1c89b5..8c72944f 100644 --- a/lib/libstatspull/stats_pull_atom_callback.cpp +++ b/lib/libstatspull/stats_pull_atom_callback.cpp @@ -25,6 +25,7 @@ #include <stats_pull_atom_callback.h> #include <map> +#include <queue> #include <thread> #include <vector> @@ -179,6 +180,10 @@ public: } std::shared_ptr<IStatsd> getStatsService() { + // There are host unit tests which are using libstatspull + // Since we do not have statsd on host - the getStatsService() is no-op and + // should return nullptr +#ifdef __ANDROID__ std::lock_guard<std::mutex> lock(mStatsdMutex); if (!mStatsd) { // Fetch statsd @@ -188,6 +193,7 @@ public: AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), this); } } +#endif // __ANDROID__ return mStatsd; } @@ -259,6 +265,114 @@ void unregisterStatsPullAtomCallbackBlocking(int32_t atomTag, statsService->unregisterNativePullAtomCallback(atomTag); } +class CallbackOperationsHandler { + struct Cmd { + enum Type { CMD_REGISTER, CMD_UNREGISTER }; + + Type type; + int atomTag; + std::shared_ptr<StatsPullAtomCallbackInternal> callback; + }; + +public: + ~CallbackOperationsHandler() { + for (auto& workThread : mWorkThreads) { + if (workThread.joinable()) { + mCondition.notify_one(); + workThread.join(); + } + } + } + + static CallbackOperationsHandler& getInstance() { + static CallbackOperationsHandler handler; + return handler; + } + + void registerCallback(int atomTag, std::shared_ptr<StatsPullAtomCallbackInternal> callback) { + auto registerCmd = std::make_unique<Cmd>(); + registerCmd->type = Cmd::CMD_REGISTER; + registerCmd->atomTag = atomTag; + registerCmd->callback = std::move(callback); + pushToQueue(std::move(registerCmd)); + + std::thread registerThread(&CallbackOperationsHandler::processCommands, this, + statsProvider); + mWorkThreads.push_back(std::move(registerThread)); + } + + void unregisterCallback(int atomTag) { + auto unregisterCmd = std::make_unique<Cmd>(); + unregisterCmd->type = Cmd::CMD_UNREGISTER; + unregisterCmd->atomTag = atomTag; + pushToQueue(std::move(unregisterCmd)); + + std::thread unregisterThread(&CallbackOperationsHandler::processCommands, this, + statsProvider); + mWorkThreads.push_back(std::move(unregisterThread)); + } + +private: + std::vector<std::thread> mWorkThreads; + + std::condition_variable mCondition; + std::mutex mMutex; + std::queue<std::unique_ptr<Cmd>> mCmdQueue; + + CallbackOperationsHandler() { + } + + void pushToQueue(std::unique_ptr<Cmd> cmd) { + { + std::unique_lock<std::mutex> lock(mMutex); + mCmdQueue.push(std::move(cmd)); + } + mCondition.notify_one(); + } + + void processCommands(std::shared_ptr<StatsdProvider> statsProvider) { + /** + * First trying to obtain stats service instance + * This is a blocking call, which waits on service readiness + */ + const std::shared_ptr<IStatsd> statsService = statsProvider->getStatsService(); + + /** + * To guarantee sequential commands processing we need to lock mutex queue + */ + std::unique_lock<std::mutex> lock(mMutex); + /** + * This should never really block in practice, since the command was already queued + * from the main thread by registerCallback or unregisterCallback. + * We are putting command to the queue, and only after a worker thread is created, + * which will pop a single command from a queue and will be terminated after processing. + * It makes producer/consumer as 1:1 match + */ + if (mCmdQueue.empty()) { + mCondition.wait(lock, [this] { return !this->mCmdQueue.empty(); }); + } + + std::unique_ptr<Cmd> cmd = std::move(mCmdQueue.front()); + mCmdQueue.pop(); + + if (!statsService) { + // Statsd not available - dropping command request + return; + } + + switch (cmd->type) { + case Cmd::CMD_REGISTER: { + registerStatsPullAtomCallbackBlocking(cmd->atomTag, statsProvider, cmd->callback); + break; + } + case Cmd::CMD_UNREGISTER: { + unregisterStatsPullAtomCallbackBlocking(cmd->atomTag, statsProvider); + break; + } + } + } +}; + void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata* metadata, AStatsManager_PullAtomCallback callback, void* cookie) { int64_t coolDownMillis = @@ -280,9 +394,7 @@ void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomM pullers[atom_tag] = callbackBinder; } - std::thread registerThread(registerStatsPullAtomCallbackBlocking, atom_tag, statsProvider, - callbackBinder); - registerThread.detach(); + CallbackOperationsHandler::getInstance().registerCallback(atom_tag, callbackBinder); } void AStatsManager_clearPullAtomCallback(int32_t atom_tag) { @@ -293,6 +405,5 @@ void AStatsManager_clearPullAtomCallback(int32_t atom_tag) { pullers.erase(atom_tag); } - std::thread unregisterThread(unregisterStatsPullAtomCallbackBlocking, atom_tag, statsProvider); - unregisterThread.detach(); + CallbackOperationsHandler::getInstance().unregisterCallback(atom_tag); } diff --git a/lib/libstatssocket/Android.bp b/lib/libstatssocket/Android.bp index 43694591..6a3c3a9c 100644 --- a/lib/libstatssocket/Android.bp +++ b/lib/libstatssocket/Android.bp @@ -111,7 +111,10 @@ cc_test { shared_libs: [ "libutils", ], - test_suites: ["device-tests", "mts-statsd"], + test_suites: [ + "device-tests", + "mts-statsd", + ], test_config: "libstatssocket_test.xml", //TODO(b/153588990): Remove when the build system properly separates. //32bit and 64bit architectures. @@ -125,4 +128,5 @@ cc_test { }, }, require_root: true, + min_sdk_version: "30", } diff --git a/statsd/Android.bp b/statsd/Android.bp index 50ee9f95..d8cb209e 100644 --- a/statsd/Android.bp +++ b/statsd/Android.bp @@ -79,6 +79,7 @@ cc_defaults { "src/statscompanion_util.cpp", "src/statsd_config.proto", "src/statsd_metadata.proto", + "src/guardrail/invalid_config_reason_enum.proto", "src/StatsLogProcessor.cpp", "src/StatsService.cpp", "src/storage/StorageManager.cpp", @@ -86,6 +87,7 @@ cc_defaults { "src/subscriber/SubscriberReporter.cpp", "src/uid_data.proto", "src/utils/MultiConditionTrigger.cpp", + "src/utils/ShardOffsetProvider.cpp", ], local_include_dirs: [ @@ -238,7 +240,10 @@ cc_binary { cc_test { name: "statsd_test", defaults: ["statsd_defaults"], - test_suites: ["device-tests", "mts-statsd"], + test_suites: [ + "device-tests", + "mts-statsd", + ], test_config: "statsd_test.xml", //TODO(b/153588990): Remove when the build system properly separates @@ -265,6 +270,42 @@ cc_test { require_root: true, + tidy_timeout_srcs: [ + "tests/condition/SimpleConditionTracker_test.cpp", + "tests/ConfigManager_test.cpp", + "tests/e2e/Anomaly_count_e2e_test.cpp", + "tests/e2e/Anomaly_duration_sum_e2e_test.cpp", + "tests/e2e/ConfigUpdate_e2e_ab_test.cpp", + "tests/e2e/ConfigUpdate_e2e_test.cpp", + "tests/e2e/CountMetric_e2e_test.cpp", + "tests/e2e/DurationMetric_e2e_test.cpp", + "tests/e2e/GaugeMetric_e2e_pull_test.cpp", + "tests/e2e/MetricActivation_e2e_test.cpp", + "tests/e2e/PartialBucket_e2e_test.cpp", + "tests/e2e/ValueMetric_pull_e2e_test.cpp", + "tests/e2e/WakelockDuration_e2e_test.cpp", + "tests/external/puller_util_test.cpp", + "tests/external/StatsPuller_test.cpp", + "tests/FieldValue_test.cpp", + "tests/guardrail/StatsdStats_test.cpp", + "tests/LogEvent_test.cpp", + "tests/metrics/CountMetricProducer_test.cpp", + "tests/metrics/DurationMetricProducer_test.cpp", + "tests/metrics/EventMetricProducer_test.cpp", + "tests/metrics/GaugeMetricProducer_test.cpp", + "tests/metrics/KllMetricProducer_test.cpp", + "tests/metrics/MaxDurationTracker_test.cpp", + "tests/metrics/NumericValueMetricProducer_test.cpp", + "tests/metrics/OringDurationTracker_test.cpp", + "tests/MetricsManager_test.cpp", + "tests/metrics/parsing_utils/config_update_utils_test.cpp", + "tests/metrics/parsing_utils/metrics_manager_util_test.cpp", + "tests/state/StateTracker_test.cpp", + "tests/statsd_test_util.cpp", + "tests/StatsLogProcessor_test.cpp", + "tests/UidMap_test.cpp", + ], + srcs: [ ":libstats_internal_protos", @@ -345,6 +386,7 @@ cc_test { "external/protobuf/src", ], }, + min_sdk_version: "30", } @@ -425,6 +467,7 @@ java_library { "src/shell/shell_data.proto", "src/stats_log.proto", "src/statsd_config.proto", + "src/guardrail/invalid_config_reason_enum.proto", ], static_libs: [ @@ -453,6 +496,7 @@ java_library { "src/shell/shell_data.proto", "src/stats_log.proto", "src/statsd_config.proto", + "src/guardrail/invalid_config_reason_enum.proto", ], static_libs: [ "platformprotosnano", @@ -481,5 +525,6 @@ filegroup { "src/statsd_metadata.proto", "src/stats_log.proto", "src/uid_data.proto", + "src/guardrail/invalid_config_reason_enum.proto", ], } diff --git a/statsd/src/FieldValue.cpp b/statsd/src/FieldValue.cpp index 406c19f0..9e1f21f2 100644 --- a/statsd/src/FieldValue.cpp +++ b/statsd/src/FieldValue.cpp @@ -16,8 +16,11 @@ #define STATSD_DEBUG false #include "Log.h" + #include "FieldValue.h" + #include "HashableDimensionKey.h" +#include "hash.h" #include "math.h" namespace android { @@ -521,6 +524,38 @@ size_t getSize(const std::vector<FieldValue>& fieldValues) { return totalSize; } +bool shouldKeepSample(const FieldValue& sampleFieldValue, int shardOffset, int shardCount) { + int hashValue = 0; + switch (sampleFieldValue.mValue.type) { + case INT: + hashValue = Hash32(reinterpret_cast<const char*>(&sampleFieldValue.mValue.int_value), + sizeof(sampleFieldValue.mValue.int_value)); + break; + case LONG: + hashValue = Hash32(reinterpret_cast<const char*>(&sampleFieldValue.mValue.long_value), + sizeof(sampleFieldValue.mValue.long_value)); + break; + case FLOAT: + hashValue = Hash32(reinterpret_cast<const char*>(&sampleFieldValue.mValue.float_value), + sizeof(sampleFieldValue.mValue.float_value)); + break; + case DOUBLE: + hashValue = Hash32(reinterpret_cast<const char*>(&sampleFieldValue.mValue.double_value), + sizeof(sampleFieldValue.mValue.double_value)); + break; + case STRING: + hashValue = Hash32(sampleFieldValue.mValue.str_value); + break; + case STORAGE: + hashValue = Hash32((const char*)sampleFieldValue.mValue.storage_value.data(), + sampleFieldValue.mValue.storage_value.size()); + break; + default: + return true; + } + return (hashValue + shardOffset) % shardCount == 0; +} + } // namespace statsd } // namespace os } // namespace android diff --git a/statsd/src/FieldValue.h b/statsd/src/FieldValue.h index 979264c6..963e2c0d 100644 --- a/statsd/src/FieldValue.h +++ b/statsd/src/FieldValue.h @@ -241,7 +241,7 @@ struct Matcher { } bool hasAllPositionMatcher() const { - return mMatcher.getDepth() >= 1 && getRawMaskAtDepth(1) == 0x7f; + return mMatcher.getDepth() >= 1 && mMatcher.getRawPosAtDepth(1) == 0; } inline bool operator!=(const Matcher& that) const { @@ -465,6 +465,9 @@ bool subsetDimensions(const std::vector<Matcher>& dimension_a, // Estimate the memory size of the FieldValues. This is different from sizeof(FieldValue) because // the size is computed at runtime using the actual contents stored in the FieldValue. size_t getSize(const std::vector<FieldValue>& fieldValues); + +bool shouldKeepSample(const FieldValue& sampleFieldValue, int shardOffset, int shardCount); + } // namespace statsd } // namespace os } // namespace android diff --git a/statsd/src/HashableDimensionKey.cpp b/statsd/src/HashableDimensionKey.cpp index c1b250a1..447a7311 100644 --- a/statsd/src/HashableDimensionKey.cpp +++ b/statsd/src/HashableDimensionKey.cpp @@ -135,6 +135,16 @@ android::hash_t hashDimension(const HashableDimensionKey& value) { android::hash_type(fieldValue.mValue.float_value)); break; } + case DOUBLE: { + hash = android::JenkinsHashMix(hash, + android::hash_type(fieldValue.mValue.double_value)); + break; + } + case STORAGE: { + hash = android::JenkinsHashMixBytes(hash, fieldValue.mValue.storage_value.data(), + fieldValue.mValue.storage_value.size()); + break; + } default: break; } @@ -144,6 +154,9 @@ android::hash_t hashDimension(const HashableDimensionKey& value) { bool filterValues(const Matcher& matcherField, const vector<FieldValue>& values, FieldValue* output) { + if (matcherField.hasAllPositionMatcher()) { + return false; + } for (const auto& value : values) { if (value.mField.matches(matcherField)) { (*output) = value; diff --git a/statsd/src/HashableDimensionKey.h b/statsd/src/HashableDimensionKey.h index 19a47dfd..b5df4561 100644 --- a/statsd/src/HashableDimensionKey.h +++ b/statsd/src/HashableDimensionKey.h @@ -166,7 +166,8 @@ android::hash_t hashDimension(const HashableDimensionKey& key); /** * Returns true if a FieldValue field matches the matcher field. - * The value of the FieldValue is output. + * This function can only be used to match one field (i.e. matcher with position ALL will return + * false). The value of the FieldValue is output. */ bool filterValues(const Matcher& matcherField, const std::vector<FieldValue>& values, FieldValue* output); diff --git a/statsd/src/StatsLogProcessor.h b/statsd/src/StatsLogProcessor.h index 231b4118..d8476cca 100644 --- a/statsd/src/StatsLogProcessor.h +++ b/statsd/src/StatsLogProcessor.h @@ -329,15 +329,16 @@ private: FRIEND_TEST(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsNoCondition); FRIEND_TEST(GaugeMetricE2ePulledTest, TestConditionChangeToTrueSamplePulledEvents); - FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_partial_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); + FRIEND_TEST(AnomalyCountDetectionE2eTest, TestSlicedCountMetric_single_bucket); + FRIEND_TEST(AnomalyCountDetectionE2eTest, TestSlicedCountMetric_multiple_buckets); + FRIEND_TEST(AnomalyCountDetectionE2eTest, + TestCountMetric_save_refractory_to_disk_no_data_written); + FRIEND_TEST(AnomalyCountDetectionE2eTest, TestCountMetric_save_refractory_to_disk); + FRIEND_TEST(AnomalyCountDetectionE2eTest, TestCountMetric_load_refractory_from_disk); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_single_bucket); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_partial_bucket); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric); diff --git a/statsd/src/StatsService.h b/statsd/src/StatsService.h index d0f13254..4cbd66f6 100644 --- a/statsd/src/StatsService.h +++ b/statsd/src/StatsService.h @@ -398,6 +398,8 @@ private: ScopedAIBinder_DeathRecipient mStatsCompanionServiceDeathRecipient; + friend class StatsServiceConfigTest; + FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); FRIEND_TEST(StatsServiceTest, TestAddConfig_simple); FRIEND_TEST(StatsServiceTest, TestAddConfig_empty); @@ -419,10 +421,10 @@ private: FRIEND_TEST(ConfigUpdateE2eTest, TestAnomalyDurationMetric); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_partial_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_single_bucket); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_partial_bucket); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); }; } // namespace statsd diff --git a/statsd/src/anomaly/AnomalyTracker.cpp b/statsd/src/anomaly/AnomalyTracker.cpp index 68dc0aa0..fe390ca2 100644 --- a/statsd/src/anomaly/AnomalyTracker.cpp +++ b/statsd/src/anomaly/AnomalyTracker.cpp @@ -255,13 +255,15 @@ bool AnomalyTracker::isInRefractoryPeriod(const int64_t& timestampNs, return false; } -std::pair<bool, uint64_t> AnomalyTracker::getProtoHash() const { +std::pair<optional<InvalidConfigReason>, uint64_t> AnomalyTracker::getProtoHash() const { string serializedAlert; if (!mAlert.SerializeToString(&serializedAlert)) { ALOGW("Unable to serialize alert %lld", (long long)mAlert.id()); - return {false, 0}; + return {createInvalidConfigReasonWithAlert(INVALID_CONFIG_REASON_ALERT_SERIALIZATION_FAILED, + mAlert.metric_id(), mAlert.id()), + 0}; } - return {true, Hash64(serializedAlert)}; + return {nullopt, Hash64(serializedAlert)}; } void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id, diff --git a/statsd/src/anomaly/AnomalyTracker.h b/statsd/src/anomaly/AnomalyTracker.h index feb6bd9f..281c1f6b 100644 --- a/statsd/src/anomaly/AnomalyTracker.h +++ b/statsd/src/anomaly/AnomalyTracker.h @@ -22,15 +22,17 @@ #include "AlarmMonitor.h" #include "config/ConfigKey.h" +#include "guardrail/StatsdStats.h" +#include "hash.h" #include "src/statsd_config.pb.h" // Alert #include "src/statsd_metadata.pb.h" // AlertMetadata -#include "hash.h" -#include "stats_util.h" // HashableDimensionKey and DimToValMap +#include "stats_util.h" // HashableDimensionKey and DimToValMap namespace android { namespace os { namespace statsd { +using std::optional; using std::shared_ptr; using std::unordered_map; @@ -109,7 +111,7 @@ public: return mNumOfPastBuckets; } - std::pair<bool, uint64_t> getProtoHash() const; + std::pair<optional<InvalidConfigReason>, uint64_t> getProtoHash() const; // Sets an alarm for the given timestamp. // Replaces previous alarm if one already exists. @@ -217,10 +219,10 @@ protected: FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets); FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection); FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_partial_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_single_bucket); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_partial_bucket); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts); }; diff --git a/statsd/src/condition/CombinationConditionTracker.cpp b/statsd/src/condition/CombinationConditionTracker.cpp index 7a97ab39..829ae06f 100644 --- a/statsd/src/condition/CombinationConditionTracker.cpp +++ b/statsd/src/condition/CombinationConditionTracker.cpp @@ -35,11 +35,11 @@ CombinationConditionTracker::~CombinationConditionTracker() { VLOG("~CombinationConditionTracker() %lld", (long long)mConditionId); } -bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig, - const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionIdIndexMap, - vector<bool>& stack, - vector<ConditionState>& conditionCache) { +optional<InvalidConfigReason> CombinationConditionTracker::init( + const vector<Predicate>& allConditionConfig, + const vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<int64_t, int>& conditionIdIndexMap, vector<bool>& stack, + vector<ConditionState>& conditionCache) { VLOG("Combination predicate init() %lld", (long long)mConditionId); if (mInitialized) { // All the children are guaranteed to be initialized, but the recursion is needed to @@ -56,21 +56,24 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf } conditionCache[mIndex] = evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache); - return true; + return nullopt; } // mark this node as visited in the recursion stack. stack[mIndex] = true; Predicate_Combination combinationCondition = allConditionConfig[mIndex].combination(); + optional<InvalidConfigReason> invalidConfigReason; if (!combinationCondition.has_operation()) { - return false; + return createInvalidConfigReasonWithPredicate(INVALID_CONFIG_REASON_CONDITION_NO_OPERATION, + mConditionId); } mLogicalOperation = combinationCondition.operation(); if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.predicate_size() != 1) { - return false; + return createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_NOT_OPERATION_IS_NOT_UNARY, mConditionId); } for (auto child : combinationCondition.predicate()) { @@ -78,7 +81,10 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf if (it == conditionIdIndexMap.end()) { ALOGW("Predicate %lld not found in the config", (long long)child); - return false; + invalidConfigReason = createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_CHILD_NOT_FOUND, mConditionId); + invalidConfigReason->conditionIds.push_back(child); + return invalidConfigReason; } int childIndex = it->second; @@ -86,15 +92,19 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf // if the child is a visited node in the recursion -> circle detected. if (stack[childIndex]) { ALOGW("Circle detected!!!"); - return false; + invalidConfigReason = createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_CYCLE, mConditionId); + invalidConfigReason->conditionIds.push_back(child); + return invalidConfigReason; } - bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers, - conditionIdIndexMap, stack, conditionCache); + invalidConfigReason = childTracker->init(allConditionConfig, allConditionTrackers, + conditionIdIndexMap, stack, conditionCache); - if (!initChildSucceeded) { + if (invalidConfigReason.has_value()) { ALOGW("Child initialization failed %lld ", (long long)child); - return false; + invalidConfigReason->conditionIds.push_back(mConditionId); + return invalidConfigReason; } else { VLOG("Child initialization success %lld ", (long long)child); } @@ -120,10 +130,10 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf mInitialized = true; - return true; + return nullopt; } -bool CombinationConditionTracker::onConfigUpdated( +optional<InvalidConfigReason> CombinationConditionTracker::onConfigUpdated( const vector<Predicate>& allConditionProtos, const int index, const vector<sp<ConditionTracker>>& allConditionTrackers, const unordered_map<int64_t, int>& atomMatchingTrackerMap, @@ -135,23 +145,30 @@ bool CombinationConditionTracker::onConfigUpdated( mUnSlicedChildren.clear(); mSlicedChildren.clear(); Predicate_Combination combinationCondition = allConditionProtos[mIndex].combination(); + optional<InvalidConfigReason> invalidConfigReason; for (const int64_t child : combinationCondition.predicate()) { const auto& it = conditionTrackerMap.find(child); if (it == conditionTrackerMap.end()) { ALOGW("Predicate %lld not found in the config", (long long)child); - return false; + invalidConfigReason = createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_CHILD_NOT_FOUND, mConditionId); + invalidConfigReason->conditionIds.push_back(child); + return invalidConfigReason; } int childIndex = it->second; const sp<ConditionTracker>& childTracker = allConditionTrackers[childIndex]; // Ensures that the child's tracker indices are updated. - if (!childTracker->onConfigUpdated(allConditionProtos, childIndex, allConditionTrackers, - atomMatchingTrackerMap, conditionTrackerMap)) { + invalidConfigReason = + childTracker->onConfigUpdated(allConditionProtos, childIndex, allConditionTrackers, + atomMatchingTrackerMap, conditionTrackerMap); + if (invalidConfigReason.has_value()) { ALOGW("Child update failed %lld ", (long long)child); - return false; + invalidConfigReason->conditionIds.push_back(child); + return invalidConfigReason; } if (allConditionTrackers[childIndex]->isSliced()) { @@ -163,7 +180,7 @@ bool CombinationConditionTracker::onConfigUpdated( mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(), childTracker->getAtomMatchingTrackerIndex().end()); } - return true; + return nullopt; } void CombinationConditionTracker::isConditionMet( diff --git a/statsd/src/condition/CombinationConditionTracker.h b/statsd/src/condition/CombinationConditionTracker.h index 3889e568..82463f07 100644 --- a/statsd/src/condition/CombinationConditionTracker.h +++ b/statsd/src/condition/CombinationConditionTracker.h @@ -30,15 +30,17 @@ public: ~CombinationConditionTracker(); - bool init(const std::vector<Predicate>& allConditionConfig, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, - std::vector<ConditionState>& conditionCache) override; - - bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& conditionTrackerMap) override; + optional<InvalidConfigReason> init( + const std::vector<Predicate>& allConditionConfig, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, + std::vector<ConditionState>& conditionCache) override; + + optional<InvalidConfigReason> onConfigUpdated( + const std::vector<Predicate>& allConditionProtos, const int index, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& conditionTrackerMap) override; void evaluateCondition(const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues, diff --git a/statsd/src/condition/ConditionTracker.h b/statsd/src/condition/ConditionTracker.h index 4ce8cb13..2aa3ff06 100644 --- a/statsd/src/condition/ConditionTracker.h +++ b/statsd/src/condition/ConditionTracker.h @@ -54,10 +54,11 @@ public: // stack: a bit map to keep track which nodes have been visited on the stack in the recursion. // conditionCache: tracks initial conditions of all ConditionTrackers. returns the // current condition if called on a config update. - virtual bool init(const std::vector<Predicate>& allConditionConfig, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionIdIndexMap, - std::vector<bool>& stack, std::vector<ConditionState>& conditionCache) = 0; + virtual optional<InvalidConfigReason> init( + const std::vector<Predicate>& allConditionConfig, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, + std::vector<ConditionState>& conditionCache) = 0; // Update appropriate state on config updates. Primarily, all indices need to be updated. // This predicate and all of its children are guaranteed to be preserved across the update. @@ -71,12 +72,13 @@ public: // atomMatchingTrackerMap: map of atom matcher id to index after the config update. // conditionTrackerMap: map of condition tracker id to index after the config update. // returns whether or not the update is successful. - virtual bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& conditionTrackerMap) { + virtual optional<InvalidConfigReason> onConfigUpdated( + const std::vector<Predicate>& allConditionProtos, const int index, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& conditionTrackerMap) { mIndex = index; - return true; + return nullopt; } // evaluate current condition given the new event. diff --git a/statsd/src/condition/SimpleConditionTracker.cpp b/statsd/src/condition/SimpleConditionTracker.cpp index 66ce961f..5d7d3858 100644 --- a/statsd/src/condition/SimpleConditionTracker.cpp +++ b/statsd/src/condition/SimpleConditionTracker.cpp @@ -56,10 +56,11 @@ SimpleConditionTracker::~SimpleConditionTracker() { VLOG("~SimpleConditionTracker()"); } -bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig, - const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<int64_t, int>& conditionIdIndexMap, - vector<bool>& stack, vector<ConditionState>& conditionCache) { +optional<InvalidConfigReason> SimpleConditionTracker::init( + const vector<Predicate>& allConditionConfig, + const vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<int64_t, int>& conditionIdIndexMap, vector<bool>& stack, + vector<ConditionState>& conditionCache) { // SimpleConditionTracker does not have dependency on other conditions, thus we just return // if the initialization was successful. ConditionKey conditionKey; @@ -67,10 +68,14 @@ bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig, conditionKey[mConditionId] = DEFAULT_DIMENSION_KEY; } isConditionMet(conditionKey, allConditionTrackers, mSliced, conditionCache); - return mInitialized; + if (!mInitialized) { + return createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_TRACKER_NOT_INITIALIZED, mConditionId); + } + return nullopt; } -bool SimpleConditionTracker::onConfigUpdated( +optional<InvalidConfigReason> SimpleConditionTracker::onConfigUpdated( const vector<Predicate>& allConditionProtos, const int index, const vector<sp<ConditionTracker>>& allConditionTrackers, const unordered_map<int64_t, int>& atomMatchingTrackerMap, @@ -78,7 +83,7 @@ bool SimpleConditionTracker::onConfigUpdated( ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers, atomMatchingTrackerMap, conditionTrackerMap); setMatcherIndices(allConditionProtos[index].simple_predicate(), atomMatchingTrackerMap); - return true; + return nullopt; } void SimpleConditionTracker::setMatcherIndices( diff --git a/statsd/src/condition/SimpleConditionTracker.h b/statsd/src/condition/SimpleConditionTracker.h index 4dc56607..4687d8e1 100644 --- a/statsd/src/condition/SimpleConditionTracker.h +++ b/statsd/src/condition/SimpleConditionTracker.h @@ -35,15 +35,17 @@ public: ~SimpleConditionTracker(); - bool init(const std::vector<Predicate>& allConditionConfig, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, - std::vector<ConditionState>& conditionCache) override; - - bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index, - const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - const std::unordered_map<int64_t, int>& conditionTrackerMap) override; + optional<InvalidConfigReason> init( + const std::vector<Predicate>& allConditionConfig, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, + std::vector<ConditionState>& conditionCache) override; + + optional<InvalidConfigReason> onConfigUpdated( + const std::vector<Predicate>& allConditionProtos, const int index, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& conditionTrackerMap) override; void evaluateCondition(const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues, diff --git a/statsd/src/external/PullDataReceiver.h b/statsd/src/external/PullDataReceiver.h index 8e3a8a13..47e13773 100644 --- a/statsd/src/external/PullDataReceiver.h +++ b/statsd/src/external/PullDataReceiver.h @@ -23,17 +23,22 @@ namespace android { namespace os { namespace statsd { +// Determine if pull was needed and if so, whether the pull was successful +enum PullResult { PULL_RESULT_SUCCESS = 1, PULL_RESULT_FAIL = 2, PULL_NOT_NEEDED = 3 }; + class PullDataReceiver : virtual public RefBase{ public: virtual ~PullDataReceiver() {} /** * @param data The pulled data. - * @param pullSuccess Whether the pull succeeded. If the pull does not succeed, the data for the - * bucket should be invalidated. + * @param pullResult Whether the pull succeeded and was needed. If the pull does not succeed, + * the data for the bucket should be invalidated. * @param originalPullTimeNs This is when all the pulls have been initiated (elapsed time). */ - virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, bool pullSuccess, - int64_t originalPullTimeNs) = 0; + virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, + PullResult pullResult, int64_t originalPullTimeNs) = 0; + + virtual bool isPullNeeded() const = 0; }; } // namespace statsd diff --git a/statsd/src/external/StatsPullerManager.cpp b/statsd/src/external/StatsPullerManager.cpp index 90e1cd0e..ca5ee5b8 100644 --- a/statsd/src/external/StatsPullerManager.cpp +++ b/statsd/src/external/StatsPullerManager.cpp @@ -32,6 +32,7 @@ #include "../statscompanion_util.h" #include "StatsCallbackPuller.h" #include "TrainInfoPuller.h" +#include "flags/FlagProvider.h" #include "statslog_statsd.h" using std::shared_ptr; @@ -49,7 +50,8 @@ StatsPullerManager::StatsPullerManager() // TrainInfo. {{.atomTag = util::TRAIN_INFO, .uid = AID_STATSD}, new TrainInfoPuller()}, }), - mNextPullTimeNs(NO_ALARM_UPDATE) { + mNextPullTimeNs(NO_ALARM_UPDATE), + mLimitPull(FlagProvider::getInstance().getBootFlagBool(LIMIT_PULL_FLAG, FLAG_FALSE)) { } bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs, @@ -227,11 +229,32 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) { vector<ReceiverInfo*> receivers; if (pair.second.size() != 0) { for (ReceiverInfo& receiverInfo : pair.second) { - if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) { - receivers.push_back(&receiverInfo); + // If mLimitPull is true, check if metric needs to pull data (pullNecessary). + // If pullNecessary and enough time has passed for the next bucket, then add + // receiver to the list that will pull on this alarm. + // If pullNecessary is false, check if next pull time needs to be updated. + if (mLimitPull) { + sp<PullDataReceiver> receiverPtr = receiverInfo.receiver.promote(); + const bool pullNecessary = + receiverPtr != nullptr && receiverPtr->isPullNeeded(); + if (receiverInfo.nextPullTimeNs <= elapsedTimeNs && pullNecessary) { + receivers.push_back(&receiverInfo); + } else { + if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) { + receiverPtr->onDataPulled({}, PullResult::PULL_NOT_NEEDED, + elapsedTimeNs); + int numBucketsAhead = (elapsedTimeNs - receiverInfo.nextPullTimeNs) / + receiverInfo.intervalNs; + receiverInfo.nextPullTimeNs += + (numBucketsAhead + 1) * receiverInfo.intervalNs; + } + minNextPullTimeNs = min(receiverInfo.nextPullTimeNs, minNextPullTimeNs); + } } else { - if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) { - minNextPullTimeNs = receiverInfo.nextPullTimeNs; + if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) { + receivers.push_back(&receiverInfo); + } else { + minNextPullTimeNs = min(receiverInfo.nextPullTimeNs, minNextPullTimeNs); } } } @@ -242,9 +265,11 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) { } for (const auto& pullInfo : needToPull) { vector<shared_ptr<LogEvent>> data; - bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey, - elapsedTimeNs, &data); - if (!pullSuccess) { + PullResult pullResult = + PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey, elapsedTimeNs, &data) + ? PullResult::PULL_RESULT_SUCCESS + : PullResult::PULL_RESULT_FAIL; + if (pullResult == PullResult::PULL_RESULT_FAIL) { VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs); } @@ -263,14 +288,12 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) { for (const auto& receiverInfo : pullInfo.second) { sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote(); if (receiverPtr != nullptr) { - receiverPtr->onDataPulled(data, pullSuccess, elapsedTimeNs); + receiverPtr->onDataPulled(data, pullResult, elapsedTimeNs); // We may have just come out of a coma, compute next pull time. int numBucketsAhead = (elapsedTimeNs - receiverInfo->nextPullTimeNs) / receiverInfo->intervalNs; receiverInfo->nextPullTimeNs += (numBucketsAhead + 1) * receiverInfo->intervalNs; - if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) { - minNextPullTimeNs = receiverInfo->nextPullTimeNs; - } + minNextPullTimeNs = min(receiverInfo->nextPullTimeNs, minNextPullTimeNs); } else { VLOG("receiver already gone."); } diff --git a/statsd/src/external/StatsPullerManager.h b/statsd/src/external/StatsPullerManager.h index b503aa83..6e65f31a 100644 --- a/statsd/src/external/StatsPullerManager.h +++ b/statsd/src/external/StatsPullerManager.h @@ -164,6 +164,8 @@ private: int64_t mNextPullTimeNs; + const bool mLimitPull; + FRIEND_TEST(GaugeMetricE2ePulledTest, TestRandomSamplePulledEvents); FRIEND_TEST(GaugeMetricE2ePulledTest, TestRandomSamplePulledEvent_LateAlarm); FRIEND_TEST(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsWithActivation); diff --git a/statsd/src/flags/FlagProvider.h b/statsd/src/flags/FlagProvider.h index 6684ba4d..e948911f 100644 --- a/statsd/src/flags/FlagProvider.h +++ b/statsd/src/flags/FlagProvider.h @@ -39,6 +39,8 @@ const std::string STATSD_NATIVE_BOOT_NAMESPACE = "statsd_native_boot"; const std::string OPTIMIZATION_ATOM_MATCHER_MAP_FLAG = "optimization_atom_matcher_map"; +const std::string LIMIT_PULL_FLAG = "limit_pull"; + const std::string FLAG_TRUE = "true"; const std::string FLAG_FALSE = "false"; const std::string FLAG_EMPTY = ""; @@ -103,6 +105,7 @@ private: friend class ConfigUpdateE2eTest; friend class ConfigUpdateTest; friend class EventMetricE2eTest; + friend class ValueMetricE2eTest; friend class GaugeMetricE2ePulledTest; friend class GaugeMetricE2ePushedTest; friend class EventMetricProducerTest; diff --git a/statsd/src/guardrail/StatsdStats.cpp b/statsd/src/guardrail/StatsdStats.cpp index 0e660190..f18497a6 100644 --- a/statsd/src/guardrail/StatsdStats.cpp +++ b/statsd/src/guardrail/StatsdStats.cpp @@ -29,6 +29,7 @@ namespace statsd { using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_BOOL; +using android::util::FIELD_TYPE_ENUM; using android::util::FIELD_TYPE_FLOAT; using android::util::FIELD_TYPE_INT32; using android::util::FIELD_TYPE_INT64; @@ -81,6 +82,7 @@ const int FIELD_ID_CONFIG_STATS_CONDITION_COUNT = 6; const int FIELD_ID_CONFIG_STATS_MATCHER_COUNT = 7; const int FIELD_ID_CONFIG_STATS_ALERT_COUNT = 8; const int FIELD_ID_CONFIG_STATS_VALID = 9; +const int FIELD_ID_CONFIG_STATS_INVALID_CONFIG_REASON = 24; const int FIELD_ID_CONFIG_STATS_BROADCAST = 10; const int FIELD_ID_CONFIG_STATS_DATA_DROP_TIME = 11; const int FIELD_ID_CONFIG_STATS_DATA_DROP_BYTES = 21; @@ -97,6 +99,15 @@ const int FIELD_ID_CONFIG_STATS_DEACTIVATION = 23; const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT64 = 1; const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT32 = 2; +const int FIELD_ID_INVALID_CONFIG_REASON_ENUM = 1; +const int FIELD_ID_INVALID_CONFIG_REASON_METRIC_ID = 2; +const int FIELD_ID_INVALID_CONFIG_REASON_STATE_ID = 3; +const int FIELD_ID_INVALID_CONFIG_REASON_ALERT_ID = 4; +const int FIELD_ID_INVALID_CONFIG_REASON_ALARM_ID = 5; +const int FIELD_ID_INVALID_CONFIG_REASON_SUBSCRIPTION_ID = 6; +const int FIELD_ID_INVALID_CONFIG_REASON_MATCHER_ID = 7; +const int FIELD_ID_INVALID_CONFIG_REASON_CONDITION_ID = 8; + const int FIELD_ID_MATCHER_STATS_ID = 1; const int FIELD_ID_MATCHER_STATS_COUNT = 2; const int FIELD_ID_CONDITION_STATS_ID = 1; @@ -141,7 +152,7 @@ void StatsdStats::addToIceBoxLocked(shared_ptr<ConfigStats>& stats) { void StatsdStats::noteConfigReceived( const ConfigKey& key, int metricsCount, int conditionsCount, int matchersCount, int alertsCount, const std::list<std::pair<const int64_t, const int32_t>>& annotations, - bool isValid) { + const optional<InvalidConfigReason>& reason) { lock_guard<std::mutex> lock(mLock); int32_t nowTimeSec = getWallClockSec(); @@ -156,12 +167,13 @@ void StatsdStats::noteConfigReceived( configStats->condition_count = conditionsCount; configStats->matcher_count = matchersCount; configStats->alert_count = alertsCount; - configStats->is_valid = isValid; + configStats->is_valid = !reason.has_value(); + configStats->reason = reason; for (auto& v : annotations) { configStats->annotations.emplace_back(v); } - if (isValid) { + if (!reason.has_value()) { mConfigStats[key] = configStats; } else { configStats->deletion_time_sec = nowTimeSec; @@ -678,12 +690,17 @@ void StatsdStats::dumpStats(int out) const { for (const auto& configStats : mIceBox) { dprintf(out, "Config {%d_%lld}: creation=%d, deletion=%d, reset=%d, #metric=%d, #condition=%d, " - "#matcher=%d, #alert=%d, valid=%d\n", + "#matcher=%d, #alert=%d, valid=%d\n", configStats->uid, (long long)configStats->id, configStats->creation_time_sec, configStats->deletion_time_sec, configStats->reset_time_sec, configStats->metric_count, configStats->condition_count, configStats->matcher_count, configStats->alert_count, configStats->is_valid); + if (!configStats->is_valid) { + dprintf(out, "\tinvalid config reason: %s\n", + InvalidConfigReasonEnum_Name(configStats->reason->reason).c_str()); + } + for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) { dprintf(out, "\tbroadcast time: %d\n", broadcastTime); } @@ -709,11 +726,17 @@ void StatsdStats::dumpStats(int out) const { auto& configStats = pair.second; dprintf(out, "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " - "#matcher=%d, #alert=%d, valid=%d\n", + "#matcher=%d, #alert=%d, valid=%d\n", configStats->uid, (long long)configStats->id, configStats->creation_time_sec, configStats->deletion_time_sec, configStats->metric_count, configStats->condition_count, configStats->matcher_count, configStats->alert_count, configStats->is_valid); + + if (!configStats->is_valid) { + dprintf(out, "\tinvalid config reason: %s\n", + InvalidConfigReasonEnum_Name(configStats->reason->reason).c_str()); + } + for (const auto& annotation : configStats->annotations) { dprintf(out, "\tannotation: %lld, %d\n", (long long)annotation.first, annotation.second); @@ -878,6 +901,44 @@ void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* pr proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ALERT_COUNT, configStats.alert_count); proto->write(FIELD_TYPE_BOOL | FIELD_ID_CONFIG_STATS_VALID, configStats.is_valid); + if (!configStats.is_valid) { + uint64_t tmpToken = + proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_STATS_INVALID_CONFIG_REASON); + proto->write(FIELD_TYPE_ENUM | FIELD_ID_INVALID_CONFIG_REASON_ENUM, + configStats.reason->reason); + if (configStats.reason->metricId.has_value()) { + proto->write(FIELD_TYPE_INT64 | FIELD_ID_INVALID_CONFIG_REASON_METRIC_ID, + configStats.reason->metricId.value()); + } + if (configStats.reason->stateId.has_value()) { + proto->write(FIELD_TYPE_INT64 | FIELD_ID_INVALID_CONFIG_REASON_STATE_ID, + configStats.reason->stateId.value()); + } + if (configStats.reason->alertId.has_value()) { + proto->write(FIELD_TYPE_INT64 | FIELD_ID_INVALID_CONFIG_REASON_ALERT_ID, + configStats.reason->alertId.value()); + } + if (configStats.reason->alarmId.has_value()) { + proto->write(FIELD_TYPE_INT64 | FIELD_ID_INVALID_CONFIG_REASON_ALARM_ID, + configStats.reason->alarmId.value()); + } + if (configStats.reason->subscriptionId.has_value()) { + proto->write(FIELD_TYPE_INT64 | FIELD_ID_INVALID_CONFIG_REASON_SUBSCRIPTION_ID, + configStats.reason->subscriptionId.value()); + } + for (const auto& matcherId : configStats.reason->matcherIds) { + proto->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | + FIELD_ID_INVALID_CONFIG_REASON_MATCHER_ID, + matcherId); + } + for (const auto& conditionId : configStats.reason->conditionIds) { + proto->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | + FIELD_ID_INVALID_CONFIG_REASON_CONDITION_ID, + conditionId); + } + proto->end(tmpToken); + } + for (const auto& broadcast : configStats.broadcast_sent_time_sec) { proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_BROADCAST | FIELD_COUNT_REPEATED, broadcast); @@ -1106,6 +1167,89 @@ std::pair<size_t, size_t> StatsdStats::getAtomDimensionKeySizeLimits(const int a kDimensionKeySizeHardLimit); } +InvalidConfigReason createInvalidConfigReasonWithMatcher(const InvalidConfigReasonEnum reason, + const int64_t matcherId) { + InvalidConfigReason invalidConfigReason(reason); + invalidConfigReason.matcherIds.push_back(matcherId); + return invalidConfigReason; +} + +InvalidConfigReason createInvalidConfigReasonWithMatcher(const InvalidConfigReasonEnum reason, + const int64_t metricId, + const int64_t matcherId) { + InvalidConfigReason invalidConfigReason(reason, metricId); + invalidConfigReason.matcherIds.push_back(matcherId); + return invalidConfigReason; +} + +InvalidConfigReason createInvalidConfigReasonWithPredicate(const InvalidConfigReasonEnum reason, + const int64_t conditionId) { + InvalidConfigReason invalidConfigReason(reason); + invalidConfigReason.conditionIds.push_back(conditionId); + return invalidConfigReason; +} + +InvalidConfigReason createInvalidConfigReasonWithPredicate(const InvalidConfigReasonEnum reason, + const int64_t metricId, + const int64_t conditionId) { + InvalidConfigReason invalidConfigReason(reason, metricId); + invalidConfigReason.conditionIds.push_back(conditionId); + return invalidConfigReason; +} + +InvalidConfigReason createInvalidConfigReasonWithState(const InvalidConfigReasonEnum reason, + const int64_t metricId, + const int64_t stateId) { + InvalidConfigReason invalidConfigReason(reason, metricId); + invalidConfigReason.stateId = stateId; + return invalidConfigReason; +} + +InvalidConfigReason createInvalidConfigReasonWithAlert(const InvalidConfigReasonEnum reason, + const int64_t alertId) { + InvalidConfigReason invalidConfigReason(reason); + invalidConfigReason.alertId = alertId; + return invalidConfigReason; +} + +InvalidConfigReason createInvalidConfigReasonWithAlert(const InvalidConfigReasonEnum reason, + const int64_t metricId, + const int64_t alertId) { + InvalidConfigReason invalidConfigReason(reason, metricId); + invalidConfigReason.alertId = alertId; + return invalidConfigReason; +} + +InvalidConfigReason createInvalidConfigReasonWithAlarm(const InvalidConfigReasonEnum reason, + const int64_t alarmId) { + InvalidConfigReason invalidConfigReason(reason); + invalidConfigReason.alarmId = alarmId; + return invalidConfigReason; +} + +InvalidConfigReason createInvalidConfigReasonWithSubscription(const InvalidConfigReasonEnum reason, + const int64_t subscriptionId) { + InvalidConfigReason invalidConfigReason(reason); + invalidConfigReason.subscriptionId = subscriptionId; + return invalidConfigReason; +} + +InvalidConfigReason createInvalidConfigReasonWithSubscriptionAndAlarm( + const InvalidConfigReasonEnum reason, const int64_t subscriptionId, const int64_t alarmId) { + InvalidConfigReason invalidConfigReason(reason); + invalidConfigReason.subscriptionId = subscriptionId; + invalidConfigReason.alarmId = alarmId; + return invalidConfigReason; +} + +InvalidConfigReason createInvalidConfigReasonWithSubscriptionAndAlert( + const InvalidConfigReasonEnum reason, const int64_t subscriptionId, const int64_t alertId) { + InvalidConfigReason invalidConfigReason(reason); + invalidConfigReason.subscriptionId = subscriptionId; + invalidConfigReason.alertId = alertId; + return invalidConfigReason; +} + } // namespace statsd } // namespace os } // namespace android diff --git a/statsd/src/guardrail/StatsdStats.h b/statsd/src/guardrail/StatsdStats.h index 1393e56f..40283b13 100644 --- a/statsd/src/guardrail/StatsdStats.h +++ b/statsd/src/guardrail/StatsdStats.h @@ -15,20 +15,43 @@ */ #pragma once -#include "config/ConfigKey.h" - #include <gtest/gtest_prod.h> #include <log/log_time.h> +#include <src/guardrail/invalid_config_reason_enum.pb.h> + #include <list> #include <mutex> #include <string> -#include <vector> #include <unordered_map> +#include <vector> + +#include "config/ConfigKey.h" namespace android { namespace os { namespace statsd { +struct InvalidConfigReason { + InvalidConfigReasonEnum reason; + std::optional<int64_t> metricId; + std::optional<int64_t> stateId; + std::optional<int64_t> alertId; + std::optional<int64_t> alarmId; + std::optional<int64_t> subscriptionId; + std::vector<int64_t> matcherIds; + std::vector<int64_t> conditionIds; + InvalidConfigReason(){}; + InvalidConfigReason(InvalidConfigReasonEnum reason) : reason(reason){}; + InvalidConfigReason(InvalidConfigReasonEnum reason, int64_t metricId) + : reason(reason), metricId(metricId){}; + bool operator==(const InvalidConfigReason& other) const { + return (this->reason == other.reason) && (this->metricId == other.metricId) && + (this->stateId == other.stateId) && (this->alertId == other.alertId) && + (this->alarmId == other.alarmId) && (this->subscriptionId == other.subscriptionId) && + (this->matcherIds == other.matcherIds) && (this->conditionIds == other.conditionIds); + } +}; + struct ConfigStats { int32_t uid; int64_t id; @@ -41,6 +64,9 @@ struct ConfigStats { int32_t alert_count; bool is_valid; + // Stores reasons for why config is valid or not + std::optional<InvalidConfigReason> reason; + std::list<int32_t> broadcast_sent_time_sec; // Times at which this config is activated. @@ -200,7 +226,7 @@ public: void noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount, int matchersCount, int alertCount, const std::list<std::pair<const int64_t, const int32_t>>& annotations, - bool isValid); + const std::optional<InvalidConfigReason>& reason); /** * Report a config has been removed. */ @@ -663,6 +689,8 @@ private: FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd); FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd); + FRIEND_TEST(StatsdStatsTest, TestInvalidConfigMissingMetricId); + FRIEND_TEST(StatsdStatsTest, TestInvalidConfigOnlyMetricId); FRIEND_TEST(StatsdStatsTest, TestConfigRemove); FRIEND_TEST(StatsdStatsTest, TestSubStats); FRIEND_TEST(StatsdStatsTest, TestAtomLog); @@ -678,6 +706,43 @@ private: FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved); }; +InvalidConfigReason createInvalidConfigReasonWithMatcher(const InvalidConfigReasonEnum reason, + const int64_t matcherId); + +InvalidConfigReason createInvalidConfigReasonWithMatcher(const InvalidConfigReasonEnum reason, + const int64_t metricId, + const int64_t matcherId); + +InvalidConfigReason createInvalidConfigReasonWithPredicate(const InvalidConfigReasonEnum reason, + const int64_t conditionId); + +InvalidConfigReason createInvalidConfigReasonWithPredicate(const InvalidConfigReasonEnum reason, + const int64_t metricId, + const int64_t conditionId); + +InvalidConfigReason createInvalidConfigReasonWithState(const InvalidConfigReasonEnum reason, + const int64_t metricId, + const int64_t stateId); + +InvalidConfigReason createInvalidConfigReasonWithAlert(const InvalidConfigReasonEnum reason, + const int64_t alertId); + +InvalidConfigReason createInvalidConfigReasonWithAlert(const InvalidConfigReasonEnum reason, + const int64_t metricId, + const int64_t alertId); + +InvalidConfigReason createInvalidConfigReasonWithAlarm(const InvalidConfigReasonEnum reason, + const int64_t alarmId); + +InvalidConfigReason createInvalidConfigReasonWithSubscription(const InvalidConfigReasonEnum reason, + const int64_t subscriptionId); + +InvalidConfigReason createInvalidConfigReasonWithSubscriptionAndAlarm( + const InvalidConfigReasonEnum reason, const int64_t subscriptionId, const int64_t alarmId); + +InvalidConfigReason createInvalidConfigReasonWithSubscriptionAndAlert( + const InvalidConfigReasonEnum reason, const int64_t subscriptionId, const int64_t alertId); + } // namespace statsd } // namespace os } // namespace android diff --git a/statsd/src/guardrail/invalid_config_reason_enum.proto b/statsd/src/guardrail/invalid_config_reason_enum.proto new file mode 100644 index 00000000..c56e39c1 --- /dev/null +++ b/statsd/src/guardrail/invalid_config_reason_enum.proto @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + syntax = "proto2"; + + package android.os.statsd; + + option java_package = "com.android.os"; + option java_outer_classname = "InvalidConfigReason"; + +enum InvalidConfigReasonEnum { + INVALID_CONFIG_REASON_UNKNOWN = 0; + INVALID_CONFIG_REASON_LOG_SOURCE_ALLOWLIST_EMPTY = 1; + INVALID_CONFIG_REASON_TOO_MANY_LOG_SOURCES = 2; + INVALID_CONFIG_REASON_DEFAULT_PULL_PACKAGES_NOT_IN_MAP = 3; + INVALID_CONFIG_REASON_TOO_MANY_SOURCES_IN_PULL_PACKAGES = 4; + INVALID_CONFIG_REASON_TOO_MANY_METRICS = 5; + INVALID_CONFIG_REASON_TOO_MANY_CONDITIONS = 6; + INVALID_CONFIG_REASON_TOO_MANY_MATCHERS = 7; + INVALID_CONFIG_REASON_TOO_MANY_ALERTS = 8; + INVALID_CONFIG_REASON_PACKAGE_CERT_HASH_SIZE_TOO_LARGE = 9; + INVALID_CONFIG_REASON_NO_REPORT_METRIC_NOT_FOUND = 10; + INVALID_CONFIG_REASON_METRIC_NOT_IN_PREV_CONFIG = 11; + INVALID_CONFIG_REASON_METRIC_UPDATE_STATUS_UNKNOWN = 12; + INVALID_CONFIG_REASON_METRIC_HAS_MULTIPLE_ACTIVATIONS = 13; + INVALID_CONFIG_REASON_METRIC_SLICED_STATE_ATOM_ALLOWED_FROM_ANY_UID = 14; + INVALID_CONFIG_REASON_METRIC_MISSING_ID_OR_WHAT = 15; + INVALID_CONFIG_REASON_METRIC_CONDITIONLINK_NO_CONDITION = 16; + INVALID_CONFIG_REASON_METRIC_STATELINK_NO_STATE = 17; + INVALID_CONFIG_REASON_METRIC_BAD_THRESHOLD = 18; + INVALID_CONFIG_REASON_METRIC_MATCHER_NOT_FOUND = 19; + INVALID_CONFIG_REASON_METRIC_MATCHER_MORE_THAN_ONE_ATOM = 20; + INVALID_CONFIG_REASON_METRIC_CONDITION_NOT_FOUND = 21; + INVALID_CONFIG_REASON_METRIC_CONDITION_LINK_NOT_FOUND = 22; + INVALID_CONFIG_REASON_METRIC_STATE_NOT_FOUND = 23; + INVALID_CONFIG_REASON_METRIC_STATELINKS_NOT_SUBSET_DIM_IN_WHAT = 24; + INVALID_CONFIG_REASON_METRIC_ACTIVATION_MATCHER_NOT_FOUND = 25; + INVALID_CONFIG_REASON_METRIC_DEACTIVATION_MATCHER_NOT_FOUND = 26; + INVALID_CONFIG_REASON_METRIC_ACTIVATION_MATCHER_NOT_FOUND_NEW = 27; + INVALID_CONFIG_REASON_METRIC_ACTIVATION_MATCHER_NOT_FOUND_EXISTING = 28; + INVALID_CONFIG_REASON_METRIC_ACTIVATION_NOT_FOUND_EXISTING = 29; + INVALID_CONFIG_REASON_METRIC_DEACTIVATION_MATCHER_NOT_FOUND_NEW = 30; + INVALID_CONFIG_REASON_METRIC_SERIALIZATION_FAILED = 31; + INVALID_CONFIG_REASON_METRIC_ACTIVATION_SERIALIZATION_FAILED = 32; + INVALID_CONFIG_REASON_DURATION_METRIC_WHAT_NOT_SIMPLE = 33; + INVALID_CONFIG_REASON_DURATION_METRIC_WHAT_NOT_FOUND = 34; + INVALID_CONFIG_REASON_DURATION_METRIC_MISSING_START = 35; + INVALID_CONFIG_REASON_DURATION_METRIC_PRODUCER_INVALID = 36; + INVALID_CONFIG_REASON_DURATION_METRIC_MAX_SPARSE_HAS_SLICE_BY_STATE = 37; + INVALID_CONFIG_REASON_VALUE_METRIC_MISSING_VALUE_FIELD = 38; + INVALID_CONFIG_REASON_VALUE_METRIC_VALUE_FIELD_HAS_POSITION_ALL = 39; + INVALID_CONFIG_REASON_VALUE_METRIC_HAS_INCORRECT_VALUE_FIELD = 40; + INVALID_CONFIG_REASON_KLL_METRIC_MISSING_KLL_FIELD = 41; + INVALID_CONFIG_REASON_KLL_METRIC_KLL_FIELD_HAS_POSITION_ALL = 42; + INVALID_CONFIG_REASON_KLL_METRIC_HAS_INCORRECT_KLL_FIELD = 43; + INVALID_CONFIG_REASON_GAUGE_METRIC_INCORRECT_FIELD_FILTER = 44; + INVALID_CONFIG_REASON_GAUGE_METRIC_TRIGGER_NO_PULL_ATOM = 45; + INVALID_CONFIG_REASON_GAUGE_METRIC_TRIGGER_NO_FIRST_N_SAMPLES = 46; + INVALID_CONFIG_REASON_GAUGE_METRIC_FIRST_N_SAMPLES_WITH_WRONG_EVENT = 47; + INVALID_CONFIG_REASON_MATCHER_NOT_IN_PREV_CONFIG = 48; + INVALID_CONFIG_REASON_MATCHER_UPDATE_STATUS_UNKNOWN = 49; + INVALID_CONFIG_REASON_MATCHER_DUPLICATE = 50; + INVALID_CONFIG_REASON_MATCHER_SERIALIZATION_FAILED = 51; + INVALID_CONFIG_REASON_MATCHER_MALFORMED_CONTENTS_CASE = 52; + INVALID_CONFIG_REASON_MATCHER_TRACKER_NOT_INITIALIZED = 53; + INVALID_CONFIG_REASON_MATCHER_NO_OPERATION = 54; + INVALID_CONFIG_REASON_MATCHER_NOT_OPERATION_IS_NOT_UNARY = 55; + INVALID_CONFIG_REASON_MATCHER_CYCLE = 56; + INVALID_CONFIG_REASON_MATCHER_CHILD_NOT_FOUND = 57; + INVALID_CONFIG_REASON_CONDITION_NOT_IN_PREV_CONFIG = 58; + INVALID_CONFIG_REASON_CONDITION_UPDATE_STATUS_UNKNOWN = 59; + INVALID_CONFIG_REASON_CONDITION_DUPLICATE = 60; + INVALID_CONFIG_REASON_CONDITION_SERIALIZATION_FAILED = 61; + INVALID_CONFIG_REASON_CONDITION_MALFORMED_CONTENTS_CASE = 62; + INVALID_CONFIG_REASON_CONDITION_TRACKER_NOT_INITIALIZED = 63; + INVALID_CONFIG_REASON_CONDITION_NO_OPERATION = 64; + INVALID_CONFIG_REASON_CONDITION_NOT_OPERATION_IS_NOT_UNARY = 65; + INVALID_CONFIG_REASON_CONDITION_CYCLE = 66; + INVALID_CONFIG_REASON_CONDITION_CHILD_NOT_FOUND = 67; + INVALID_CONFIG_REASON_STATE_SERIALIZATION_FAILED = 68; + INVALID_CONFIG_REASON_ALERT_METRIC_NOT_FOUND = 69; + INVALID_CONFIG_REASON_ALERT_THRESHOLD_MISSING = 70; + INVALID_CONFIG_REASON_ALERT_INVALID_TRIGGER_OR_NUM_BUCKETS = 71; + INVALID_CONFIG_REASON_ALERT_CANNOT_ADD_ANOMALY = 72; + INVALID_CONFIG_REASON_ALERT_NOT_IN_PREV_CONFIG = 73; + INVALID_CONFIG_REASON_ALERT_UPDATE_STATUS_UNKNOWN = 74; + INVALID_CONFIG_REASON_ALERT_SERIALIZATION_FAILED = 75; + INVALID_CONFIG_REASON_ALARM_OFFSET_LESS_THAN_OR_EQUAL_ZERO = 76; + INVALID_CONFIG_REASON_ALARM_PERIOD_LESS_THAN_OR_EQUAL_ZERO = 77; + INVALID_CONFIG_REASON_SUBSCRIPTION_SUBSCRIBER_INFO_MISSING = 78; + INVALID_CONFIG_REASON_SUBSCRIPTION_RULE_NOT_FOUND = 79; + INVALID_CONFIG_REASON_METRIC_DIMENSIONAL_SAMPLING_INFO_INCORRECT_SHARD_COUNT = 80; + INVALID_CONFIG_REASON_METRIC_DIMENSIONAL_SAMPLING_INFO_MISSING_SAMPLED_FIELD = 81; + INVALID_CONFIG_REASON_METRIC_SAMPLED_FIELD_INCORRECT_SIZE = 82; + INVALID_CONFIG_REASON_METRIC_SAMPLED_FIELDS_NOT_SUBSET_DIM_IN_WHAT = 83; +}; diff --git a/statsd/src/main.cpp b/statsd/src/main.cpp index cd64fe7f..02dbbe84 100644 --- a/statsd/src/main.cpp +++ b/statsd/src/main.cpp @@ -76,7 +76,8 @@ int main(int /*argc*/, char** /*argv*/) { std::make_shared<LogEventQueue>(4000 /*buffer limit. Buffer is NOT pre-allocated*/); // Initialize boot flags - FlagProvider::getInstance().initBootFlags({OPTIMIZATION_ATOM_MATCHER_MAP_FLAG}); + FlagProvider::getInstance().initBootFlags( + {OPTIMIZATION_ATOM_MATCHER_MAP_FLAG, LIMIT_PULL_FLAG}); sp<UidMap> uidMap = UidMap::getInstance(); diff --git a/statsd/src/matchers/AtomMatchingTracker.h b/statsd/src/matchers/AtomMatchingTracker.h index cdb17165..3e35d9bf 100644 --- a/statsd/src/matchers/AtomMatchingTracker.h +++ b/statsd/src/matchers/AtomMatchingTracker.h @@ -17,16 +17,17 @@ #ifndef ATOM_MATCHING_TRACKER_H #define ATOM_MATCHING_TRACKER_H -#include "src/statsd_config.pb.h" -#include "logd/LogEvent.h" -#include "matchers/matcher_util.h" - #include <utils/RefBase.h> #include <set> #include <unordered_map> #include <vector> +#include "guardrail/StatsdStats.h" +#include "logd/LogEvent.h" +#include "matchers/matcher_util.h" +#include "src/statsd_config.pb.h" + namespace android { namespace os { namespace statsd { @@ -47,17 +48,17 @@ public: // CombinationAtomMatchingTrackers using DFS. // stack: a bit map to record which matcher has been visited on the stack. This is for detecting // circle dependency. - virtual bool init(const std::vector<AtomMatcher>& allAtomMatchers, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& matcherMap, - std::vector<bool>& stack) = 0; + virtual optional<InvalidConfigReason> init( + const std::vector<AtomMatcher>& allAtomMatchers, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack) = 0; // Update appropriate state on config updates. Primarily, all indices need to be updated. // This matcher and all of its children are guaranteed to be preserved across the update. // matcher: the AtomMatcher proto from the config. // index: the index of this matcher in mAllAtomMatchingTrackers. // atomMatchingTrackerMap: map from matcher id to index in mAllAtomMatchingTrackers - virtual bool onConfigUpdated( + virtual optional<InvalidConfigReason> onConfigUpdated( const AtomMatcher& matcher, const int index, const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) = 0; @@ -87,6 +88,10 @@ public: return mProtoHash; } + bool isInitialized() { + return mInitialized; + } + protected: // Name of this matching. We don't really need the name, but it makes log message easy to debug. const int64_t mId; @@ -106,8 +111,8 @@ protected: // Used to determine if the definition of this matcher has changed across a config update. const uint64_t mProtoHash; - FRIEND_TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerSimple); - FRIEND_TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination); + FRIEND_TEST(MetricsManagerUtilTest, TestCreateAtomMatchingTrackerSimple); + FRIEND_TEST(MetricsManagerUtilTest, TestCreateAtomMatchingTrackerCombination); FRIEND_TEST(ConfigUpdateTest, TestUpdateMatchers); }; diff --git a/statsd/src/matchers/CombinationAtomMatchingTracker.cpp b/statsd/src/matchers/CombinationAtomMatchingTracker.cpp index 45685ce5..846a5308 100644 --- a/statsd/src/matchers/CombinationAtomMatchingTracker.cpp +++ b/statsd/src/matchers/CombinationAtomMatchingTracker.cpp @@ -36,12 +36,12 @@ CombinationAtomMatchingTracker::CombinationAtomMatchingTracker(const int64_t& id CombinationAtomMatchingTracker::~CombinationAtomMatchingTracker() { } -bool CombinationAtomMatchingTracker::init( +optional<InvalidConfigReason> CombinationAtomMatchingTracker::init( const vector<AtomMatcher>& allAtomMatchers, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const unordered_map<int64_t, int>& matcherMap, vector<bool>& stack) { if (mInitialized) { - return true; + return nullopt; } // mark this node as visited in the recursion stack. @@ -51,20 +51,26 @@ bool CombinationAtomMatchingTracker::init( // LogicalOperation is missing in the config if (!matcher.has_operation()) { - return false; + return createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_MATCHER_NO_OPERATION, + mId); } mLogicalOperation = matcher.operation(); if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) { - return false; + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_NOT_OPERATION_IS_NOT_UNARY, mId); } for (const auto& child : matcher.matcher()) { auto pair = matcherMap.find(child); if (pair == matcherMap.end()) { ALOGW("Matcher %lld not found in the config", (long long)child); - return false; + optional<InvalidConfigReason> invalidConfigReason = + createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_CHILD_NOT_FOUND, mId); + invalidConfigReason->matcherIds.push_back(child); + return invalidConfigReason; } int childIndex = pair->second; @@ -72,13 +78,18 @@ bool CombinationAtomMatchingTracker::init( // if the child is a visited node in the recursion -> circle detected. if (stack[childIndex]) { ALOGE("Circle detected in matcher config"); - return false; + optional<InvalidConfigReason> invalidConfigReason = + createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_MATCHER_CYCLE, mId); + invalidConfigReason->matcherIds.push_back(child); + return invalidConfigReason; } - - if (!allAtomMatchingTrackers[childIndex]->init(allAtomMatchers, allAtomMatchingTrackers, - matcherMap, stack)) { + optional<InvalidConfigReason> invalidConfigReason = + allAtomMatchingTrackers[childIndex]->init(allAtomMatchers, allAtomMatchingTrackers, + matcherMap, stack); + if (invalidConfigReason.has_value()) { ALOGW("child matcher init failed %lld", (long long)child); - return false; + invalidConfigReason->matcherIds.push_back(mId); + return invalidConfigReason; } mChildren.push_back(childIndex); @@ -90,10 +101,10 @@ bool CombinationAtomMatchingTracker::init( mInitialized = true; // unmark this node in the recursion stack. stack[mIndex] = false; - return true; + return nullopt; } -bool CombinationAtomMatchingTracker::onConfigUpdated( +optional<InvalidConfigReason> CombinationAtomMatchingTracker::onConfigUpdated( const AtomMatcher& matcher, const int index, const unordered_map<int64_t, int>& atomMatchingTrackerMap) { mIndex = index; @@ -103,11 +114,15 @@ bool CombinationAtomMatchingTracker::onConfigUpdated( const auto& pair = atomMatchingTrackerMap.find(child); if (pair == atomMatchingTrackerMap.end()) { ALOGW("Matcher %lld not found in the config", (long long)child); - return false; + optional<InvalidConfigReason> invalidConfigReason = + createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_CHILD_NOT_FOUND, matcher.id()); + invalidConfigReason->matcherIds.push_back(child); + return invalidConfigReason; } mChildren.push_back(pair->second); } - return true; + return nullopt; } void CombinationAtomMatchingTracker::onLogEvent( diff --git a/statsd/src/matchers/CombinationAtomMatchingTracker.h b/statsd/src/matchers/CombinationAtomMatchingTracker.h index cf02086b..edcbb49c 100644 --- a/statsd/src/matchers/CombinationAtomMatchingTracker.h +++ b/statsd/src/matchers/CombinationAtomMatchingTracker.h @@ -31,12 +31,14 @@ class CombinationAtomMatchingTracker : public AtomMatchingTracker { public: CombinationAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash); - bool init(const std::vector<AtomMatcher>& allAtomMatchers, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack); - - bool onConfigUpdated(const AtomMatcher& matcher, const int index, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override; + optional<InvalidConfigReason> init( + const std::vector<AtomMatcher>& allAtomMatchers, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack); + + optional<InvalidConfigReason> onConfigUpdated( + const AtomMatcher& matcher, const int index, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override; ~CombinationAtomMatchingTracker(); diff --git a/statsd/src/matchers/SimpleAtomMatchingTracker.cpp b/statsd/src/matchers/SimpleAtomMatchingTracker.cpp index 76bd4761..a0e30b24 100644 --- a/statsd/src/matchers/SimpleAtomMatchingTracker.cpp +++ b/statsd/src/matchers/SimpleAtomMatchingTracker.cpp @@ -42,20 +42,28 @@ SimpleAtomMatchingTracker::SimpleAtomMatchingTracker(const int64_t& id, const in SimpleAtomMatchingTracker::~SimpleAtomMatchingTracker() { } -bool SimpleAtomMatchingTracker::init(const vector<AtomMatcher>& allAtomMatchers, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& matcherMap, - vector<bool>& stack) { +optional<InvalidConfigReason> SimpleAtomMatchingTracker::init( + const vector<AtomMatcher>& allAtomMatchers, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& matcherMap, vector<bool>& stack) { // no need to do anything. - return mInitialized; + if (!mInitialized) { + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_TRACKER_NOT_INITIALIZED, mId); + } + return nullopt; } -bool SimpleAtomMatchingTracker::onConfigUpdated( +optional<InvalidConfigReason> SimpleAtomMatchingTracker::onConfigUpdated( const AtomMatcher& matcher, const int index, const unordered_map<int64_t, int>& atomMatchingTrackerMap) { mIndex = index; // Do not need to update mMatcher since the matcher must be identical across the update. - return mInitialized; + if (!mInitialized) { + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_TRACKER_NOT_INITIALIZED, mId); + } + return nullopt; } void SimpleAtomMatchingTracker::onLogEvent( diff --git a/statsd/src/matchers/SimpleAtomMatchingTracker.h b/statsd/src/matchers/SimpleAtomMatchingTracker.h index 13ef344d..70585f9b 100644 --- a/statsd/src/matchers/SimpleAtomMatchingTracker.h +++ b/statsd/src/matchers/SimpleAtomMatchingTracker.h @@ -35,13 +35,14 @@ public: ~SimpleAtomMatchingTracker(); - bool init(const std::vector<AtomMatcher>& allAtomMatchers, - const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& matcherMap, - std::vector<bool>& stack) override; - - bool onConfigUpdated(const AtomMatcher& matcher, const int index, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override; + optional<InvalidConfigReason> init( + const std::vector<AtomMatcher>& allAtomMatchers, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack) override; + + optional<InvalidConfigReason> onConfigUpdated( + const AtomMatcher& matcher, const int index, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override; void onLogEvent(const LogEvent& event, const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, diff --git a/statsd/src/metrics/CountMetricProducer.cpp b/statsd/src/metrics/CountMetricProducer.cpp index cb141a22..db40b02e 100644 --- a/statsd/src/metrics/CountMetricProducer.cpp +++ b/statsd/src/metrics/CountMetricProducer.cpp @@ -119,7 +119,7 @@ CountMetricProducer::CountMetricProducer( // Adjust start for partial bucket mCurrentBucketStartTimeNs = startTimeNs; - VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), + VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)mMetricId, (long long)mBucketSizeNs, (long long)mTimeBaseNs); } @@ -127,7 +127,7 @@ CountMetricProducer::~CountMetricProducer() { VLOG("~CountMetricProducer() called"); } -bool CountMetricProducer::onConfigUpdatedLocked( +optional<InvalidConfigReason> CountMetricProducer::onConfigUpdatedLocked( const StatsdConfig& config, const int configIndex, const int metricIndex, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, @@ -141,31 +141,35 @@ bool CountMetricProducer::onConfigUpdatedLocked( unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, vector<int>& metricsWithActivation) { - if (!MetricProducer::onConfigUpdatedLocked( - config, configIndex, metricIndex, allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, - trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { - return false; + optional<InvalidConfigReason> invalidConfigReason = MetricProducer::onConfigUpdatedLocked( + config, configIndex, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap, + wizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } const CountMetric& metric = config.count_metric(configIndex); int trackerIndex; // Update appropriate indices, specifically mConditionIndex and MetricsManager maps. - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false, - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, trackerIndex)) { - return false; + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + metric.what(), mMetricId, metricIndex, false, allAtomMatchingTrackers, + newAtomMatchingTrackerMap, trackerToMetricMap, trackerIndex); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } - if (metric.has_condition() && - !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, mConditionTrackerIndex, - conditionToMetricMap)) { - return false; + if (metric.has_condition()) { + invalidConfigReason = handleMetricWithConditions( + metric.condition(), mMetricId, metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, mConditionTrackerIndex, conditionToMetricMap); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; + } } - return true; + return nullopt; } void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId, diff --git a/statsd/src/metrics/CountMetricProducer.h b/statsd/src/metrics/CountMetricProducer.h index 93984670..830a1146 100644 --- a/statsd/src/metrics/CountMetricProducer.h +++ b/statsd/src/metrics/CountMetricProducer.h @@ -97,7 +97,7 @@ private: void flushCurrentBucketLocked(const int64_t& eventTimeNs, const int64_t& nextBucketStartTimeNs) override; - bool onConfigUpdatedLocked( + optional<InvalidConfigReason> onConfigUpdatedLocked( const StatsdConfig& config, const int configIndex, const int metricIndex, const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, diff --git a/statsd/src/metrics/DurationMetricProducer.cpp b/statsd/src/metrics/DurationMetricProducer.cpp index ec0af844..04cecbb4 100644 --- a/statsd/src/metrics/DurationMetricProducer.cpp +++ b/statsd/src/metrics/DurationMetricProducer.cpp @@ -114,6 +114,7 @@ DurationMetricProducer::DurationMetricProducer( // Dimensions in what must be subset of internal dimensions if (!subsetDimensions(mDimensionsInWhat, mInternalDimensions)) { ALOGE("Dimensions in what must be a subset of the internal dimensions"); + // TODO: Add invalidConfigReason mValid = false; } @@ -127,6 +128,7 @@ DurationMetricProducer::DurationMetricProducer( translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); if (!subsetDimensions(mc.metricFields, mInternalDimensions)) { ALOGE(("Condition links must be a subset of the internal dimensions")); + // TODO: Add invalidConfigReason mValid = false; } mMetric2ConditionLinks.push_back(mc); @@ -142,6 +144,7 @@ DurationMetricProducer::DurationMetricProducer( translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields); if (!subsetDimensions(ms.metricFields, mInternalDimensions)) { ALOGE(("State links must be a subset of the dimensions in what internal dimensions")); + // TODO: Add invalidConfigReason mValid = false; } mMetric2StateLinks.push_back(ms); @@ -156,7 +159,7 @@ DurationMetricProducer::DurationMetricProducer( flushIfNeededLocked(startTimeNs); // Adjust start for partial bucket mCurrentBucketStartTimeNs = startTimeNs; - VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), + VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)mMetricId, (long long)mBucketSizeNs, (long long)mTimeBaseNs); initTrueDimensions(whatIndex, startTimeNs); @@ -166,7 +169,7 @@ DurationMetricProducer::~DurationMetricProducer() { VLOG("~DurationMetric() called"); } -bool DurationMetricProducer::onConfigUpdatedLocked( +optional<InvalidConfigReason> DurationMetricProducer::onConfigUpdatedLocked( const StatsdConfig& config, const int configIndex, const int metricIndex, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, @@ -180,68 +183,76 @@ bool DurationMetricProducer::onConfigUpdatedLocked( unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, vector<int>& metricsWithActivation) { - if (!MetricProducer::onConfigUpdatedLocked( - config, configIndex, metricIndex, allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, - trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { - return false; + optional<InvalidConfigReason> invalidConfigReason = MetricProducer::onConfigUpdatedLocked( + config, configIndex, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap, + wizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } const DurationMetric& metric = config.duration_metric(configIndex); const auto& what_it = conditionTrackerMap.find(metric.what()); if (what_it == conditionTrackerMap.end()) { ALOGE("DurationMetric's \"what\" is not present in the config"); - return false; + return createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_DURATION_METRIC_WHAT_NOT_FOUND, mMetricId, metric.what()); } const Predicate& durationWhat = config.predicate(what_it->second); if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) { ALOGE("DurationMetric's \"what\" must be a simple condition"); - return false; + return createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_DURATION_METRIC_WHAT_NOT_SIMPLE, mMetricId, metric.what()); } const SimplePredicate& simplePredicate = durationWhat.simple_predicate(); // Update indices: mStartIndex, mStopIndex, mStopAllIndex, mConditionIndex and MetricsManager // maps. - if (!handleMetricWithAtomMatchingTrackers(simplePredicate.start(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, mStartIndex)) { + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + simplePredicate.start(), mMetricId, metricIndex, metric.has_dimensions_in_what(), + allAtomMatchingTrackers, newAtomMatchingTrackerMap, trackerToMetricMap, mStartIndex); + if (invalidConfigReason.has_value()) { ALOGE("Duration metrics must specify a valid start event matcher"); - return false; + return invalidConfigReason; } - if (simplePredicate.has_stop() && - !handleMetricWithAtomMatchingTrackers(simplePredicate.stop(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, mStopIndex)) { - return false; + if (simplePredicate.has_stop()) { + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + simplePredicate.stop(), mMetricId, metricIndex, metric.has_dimensions_in_what(), + allAtomMatchingTrackers, newAtomMatchingTrackerMap, trackerToMetricMap, mStopIndex); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; + } } - if (simplePredicate.has_stop_all() && - !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, mStopAllIndex)) { - return false; + if (simplePredicate.has_stop_all()) { + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + simplePredicate.stop_all(), mMetricId, metricIndex, metric.has_dimensions_in_what(), + allAtomMatchingTrackers, newAtomMatchingTrackerMap, trackerToMetricMap, + mStopAllIndex); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; + } } - if (metric.has_condition() && - !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, mConditionTrackerIndex, - conditionToMetricMap)) { - return false; + if (metric.has_condition()) { + invalidConfigReason = handleMetricWithConditions( + metric.condition(), mMetricId, metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, mConditionTrackerIndex, conditionToMetricMap); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; + } } for (const auto& it : mCurrentSlicedDurationTrackerMap) { it.second->onConfigUpdated(wizard, mConditionTrackerIndex); } - return true; + return nullopt; } void DurationMetricProducer::initTrueDimensions(const int whatIndex, const int64_t startTimeNs) { @@ -702,6 +713,10 @@ void DurationMetricProducer::handleMatchedLogEventValuesLocked(const size_t matc return; } + if (!passesSampleCheckLocked(values)) { + return; + } + HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY; if (!mDimensionsInWhat.empty()) { filterValues(mDimensionsInWhat, values, &dimensionInWhat); diff --git a/statsd/src/metrics/DurationMetricProducer.h b/statsd/src/metrics/DurationMetricProducer.h index ba5fe958..831388b0 100644 --- a/statsd/src/metrics/DurationMetricProducer.h +++ b/statsd/src/metrics/DurationMetricProducer.h @@ -123,7 +123,7 @@ private: void flushCurrentBucketLocked(const int64_t& eventTimeNs, const int64_t& nextBucketStartTimeNs) override; - bool onConfigUpdatedLocked( + optional<InvalidConfigReason> onConfigUpdatedLocked( const StatsdConfig& config, const int configIndex, const int metricIndex, const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, diff --git a/statsd/src/metrics/EventMetricProducer.cpp b/statsd/src/metrics/EventMetricProducer.cpp index 502ea8ad..dd679193 100644 --- a/statsd/src/metrics/EventMetricProducer.cpp +++ b/statsd/src/metrics/EventMetricProducer.cpp @@ -78,7 +78,7 @@ EventMetricProducer::EventMetricProducer( mConditionSliced = true; } mTotalSize = 0; - VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), + VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)mMetricId, (long long)mBucketSizeNs, (long long)mTimeBaseNs); } @@ -86,7 +86,7 @@ EventMetricProducer::~EventMetricProducer() { VLOG("~EventMetricProducer() called"); } -bool EventMetricProducer::onConfigUpdatedLocked( +optional<InvalidConfigReason> EventMetricProducer::onConfigUpdatedLocked( const StatsdConfig& config, const int configIndex, const int metricIndex, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, @@ -100,31 +100,35 @@ bool EventMetricProducer::onConfigUpdatedLocked( unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, vector<int>& metricsWithActivation) { - if (!MetricProducer::onConfigUpdatedLocked( - config, configIndex, metricIndex, allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, - trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { - return false; + optional<InvalidConfigReason> invalidConfigReason = MetricProducer::onConfigUpdatedLocked( + config, configIndex, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap, + wizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } const EventMetric& metric = config.event_metric(configIndex); int trackerIndex; // Update appropriate indices, specifically mConditionIndex and MetricsManager maps. - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false, - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, trackerIndex)) { - return false; + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + metric.what(), mMetricId, metricIndex, false, allAtomMatchingTrackers, + newAtomMatchingTrackerMap, trackerToMetricMap, trackerIndex); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } - if (metric.has_condition() && - !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, mConditionTrackerIndex, - conditionToMetricMap)) { - return false; + if (metric.has_condition()) { + invalidConfigReason = handleMetricWithConditions( + metric.condition(), mMetricId, metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, mConditionTrackerIndex, conditionToMetricMap); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; + } } - return true; + return nullopt; } void EventMetricProducer::dropDataLocked(const int64_t dropTimeNs) { diff --git a/statsd/src/metrics/EventMetricProducer.h b/statsd/src/metrics/EventMetricProducer.h index 76747b67..fc3e79db 100644 --- a/statsd/src/metrics/EventMetricProducer.h +++ b/statsd/src/metrics/EventMetricProducer.h @@ -70,7 +70,7 @@ private: // Internal interface to handle sliced condition change. void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override; - bool onConfigUpdatedLocked( + optional<InvalidConfigReason> onConfigUpdatedLocked( const StatsdConfig& config, const int configIndex, const int metricIndex, const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, diff --git a/statsd/src/metrics/GaugeMetricProducer.cpp b/statsd/src/metrics/GaugeMetricProducer.cpp index f8b9de1e..cb615124 100644 --- a/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/statsd/src/metrics/GaugeMetricProducer.cpp @@ -141,8 +141,7 @@ GaugeMetricProducer::GaugeMetricProducer( mCurrentBucketStartTimeNs = startTimeNs; VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d", - (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs, - mConditionSliced); + (long long)mMetricId, (long long)mBucketSizeNs, (long long)mTimeBaseNs, mConditionSliced); } GaugeMetricProducer::~GaugeMetricProducer() { @@ -152,7 +151,7 @@ GaugeMetricProducer::~GaugeMetricProducer() { } } -bool GaugeMetricProducer::onConfigUpdatedLocked( +optional<InvalidConfigReason> GaugeMetricProducer::onConfigUpdatedLocked( const StatsdConfig& config, const int configIndex, const int metricIndex, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, @@ -166,38 +165,45 @@ bool GaugeMetricProducer::onConfigUpdatedLocked( unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, vector<int>& metricsWithActivation) { - if (!MetricProducer::onConfigUpdatedLocked( - config, configIndex, metricIndex, allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, - trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { - return false; + optional<InvalidConfigReason> invalidConfigReason = MetricProducer::onConfigUpdatedLocked( + config, configIndex, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap, + wizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } const GaugeMetric& metric = config.gauge_metric(configIndex); // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps. - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, /*enforceOneAtom=*/false, - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, mWhatMatcherIndex)) { - return false; + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + metric.what(), mMetricId, metricIndex, /*enforceOneAtom=*/false, + allAtomMatchingTrackers, newAtomMatchingTrackerMap, trackerToMetricMap, + mWhatMatcherIndex); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } // Need to update maps since the index changed, but mTriggerAtomId will not change. int triggerTrackerIndex; - if (metric.has_trigger_event() && - !handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex, - /*enforceOneAtom=*/true, allAtomMatchingTrackers, - newAtomMatchingTrackerMap, trackerToMetricMap, - triggerTrackerIndex)) { - return false; + if (metric.has_trigger_event()) { + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + metric.trigger_event(), mMetricId, metricIndex, + /*enforceOneAtom=*/true, allAtomMatchingTrackers, newAtomMatchingTrackerMap, + trackerToMetricMap, triggerTrackerIndex); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; + } } - if (metric.has_condition() && - !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, mConditionTrackerIndex, - conditionToMetricMap)) { - return false; + if (metric.has_condition()) { + invalidConfigReason = handleMetricWithConditions( + metric.condition(), mMetricId, metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, mConditionTrackerIndex, conditionToMetricMap); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; + } } sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard; mEventMatcherWizard = matcherWizard; @@ -208,7 +214,7 @@ bool GaugeMetricProducer::onConfigUpdatedLocked( mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { pullAndMatchEventsLocked(mCurrentBucketStartTimeNs); } - return true; + return nullopt; } void GaugeMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { @@ -477,9 +483,9 @@ std::shared_ptr<vector<FieldValue>> GaugeMetricProducer::getGaugeFields(const Lo } void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData, - bool pullSuccess, int64_t originalPullTimeNs) { + PullResult pullResult, int64_t originalPullTimeNs) { std::lock_guard<std::mutex> lock(mMutex); - if (!pullSuccess || allData.size() == 0) { + if (pullResult != PullResult::PULL_RESULT_SUCCESS || allData.size() == 0) { return; } const int64_t pullDelayNs = getElapsedRealtimeNs() - originalPullTimeNs; diff --git a/statsd/src/metrics/GaugeMetricProducer.h b/statsd/src/metrics/GaugeMetricProducer.h index af995359..f0a59ce1 100644 --- a/statsd/src/metrics/GaugeMetricProducer.h +++ b/statsd/src/metrics/GaugeMetricProducer.h @@ -76,8 +76,14 @@ public: virtual ~GaugeMetricProducer(); // Handles when the pulled data arrives. - void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, - bool pullSuccess, int64_t originalPullTimeNs) override; + void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, PullResult pullResult, + int64_t originalPullTimeNs) override; + + // Determine if metric needs to pull + bool isPullNeeded() const override { + std::lock_guard<std::mutex> lock(mMutex); + return mIsActive && (mCondition == ConditionState::kTrue); + }; // GaugeMetric needs to immediately trigger another pull when we create the partial bucket. void notifyAppUpgradeInternalLocked(const int64_t eventTimeNs) override { @@ -143,7 +149,7 @@ private: // Only call if mCondition == ConditionState::kTrue && metric is active. void pullAndMatchEventsLocked(const int64_t timestampNs); - bool onConfigUpdatedLocked( + optional<InvalidConfigReason> onConfigUpdatedLocked( const StatsdConfig& config, const int configIndex, const int metricIndex, const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, @@ -221,6 +227,7 @@ private: FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket); FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger); FRIEND_TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput); + FRIEND_TEST(GaugeMetricProducerTest, TestPullDimensionalSampling); FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents); FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled); diff --git a/statsd/src/metrics/KllMetricProducer.cpp b/statsd/src/metrics/KllMetricProducer.cpp index e5658a93..91f22f41 100644 --- a/statsd/src/metrics/KllMetricProducer.cpp +++ b/statsd/src/metrics/KllMetricProducer.cpp @@ -76,7 +76,7 @@ KllMetricProducer::DumpProtoFields KllMetricProducer::getDumpProtoFields() const } void KllMetricProducer::writePastBucketAggregateToProto( - const int aggIndex, const unique_ptr<KllQuantile>& kll, + const int aggIndex, const unique_ptr<KllQuantile>& kll, const int sampleSize, ProtoOutputStream* const protoOutput) const { uint64_t sketchesToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKETCHES); diff --git a/statsd/src/metrics/KllMetricProducer.h b/statsd/src/metrics/KllMetricProducer.h index b1c59f27..c1421139 100644 --- a/statsd/src/metrics/KllMetricProducer.h +++ b/statsd/src/metrics/KllMetricProducer.h @@ -99,6 +99,7 @@ private: void writePastBucketAggregateToProto(const int aggIndex, const std::unique_ptr<KllQuantile>& kll, + const int sampleSize, ProtoOutputStream* const protoOutput) const override; bool aggregateFields(const int64_t eventTimeNs, const MetricDimensionKey& eventKey, diff --git a/statsd/src/metrics/MetricProducer.cpp b/statsd/src/metrics/MetricProducer.cpp index 93f044b9..137510e6 100644 --- a/statsd/src/metrics/MetricProducer.cpp +++ b/statsd/src/metrics/MetricProducer.cpp @@ -74,10 +74,12 @@ MetricProducer::MetricProducer( mSlicedStateAtoms(slicedStateAtoms), mStateGroupMap(stateGroupMap), mSplitBucketForAppUpgrade(splitBucketForAppUpgrade), - mHasHitGuardrail(false) { + mHasHitGuardrail(false), + mSampledWhatFields({}), + mShardCount(0) { } -bool MetricProducer::onConfigUpdatedLocked( +optional<InvalidConfigReason> MetricProducer::onConfigUpdatedLocked( const StatsdConfig& config, const int configIndex, const int metricIndex, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, @@ -96,17 +98,18 @@ bool MetricProducer::onConfigUpdatedLocked( unordered_map<int, shared_ptr<Activation>> newEventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> newEventDeactivationMap; - if (!handleMetricActivationOnConfigUpdate( - config, mMetricId, metricIndex, metricToActivationMap, oldAtomMatchingTrackerMap, - newAtomMatchingTrackerMap, mEventActivationMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, newEventActivationMap, - newEventDeactivationMap)) { - return false; + optional<InvalidConfigReason> invalidConfigReason = handleMetricActivationOnConfigUpdate( + config, mMetricId, metricIndex, metricToActivationMap, oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, mEventActivationMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, newEventActivationMap, + newEventDeactivationMap); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } mEventActivationMap = newEventActivationMap; mEventDeactivationMap = newEventDeactivationMap; mAnomalyTrackers.clear(); - return true; + return nullopt; } void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { @@ -119,6 +122,10 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo return; } + if (!passesSampleCheckLocked(event.getValues())) { + return; + } + bool condition; ConditionKey conditionKey; if (mConditionSliced) { @@ -361,6 +368,21 @@ bool MetricProducer::maxDropEventsReached() const { return mCurrentSkippedBucket.dropEvents.size() >= StatsdStats::kMaxLoggedBucketDropEvents; } +bool MetricProducer::passesSampleCheckLocked(const vector<FieldValue>& values) const { + // Only perform sampling if shard count is correct and there is a sampled what field. + if (mShardCount <= 1 || mSampledWhatFields.size() == 0) { + return true; + } + // If filtering fails, don't perform sampling. Event could be a gauge trigger event or stop all + // event. + FieldValue sampleFieldValue; + if (!filterValues(mSampledWhatFields[0], values, &sampleFieldValue)) { + return true; + } + return shouldKeepSample(sampleFieldValue, ShardOffsetProvider::getInstance().getShardOffset(), + mShardCount); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/statsd/src/metrics/MetricProducer.h b/statsd/src/metrics/MetricProducer.h index 7d21cd0a..b8bd6fa3 100644 --- a/statsd/src/metrics/MetricProducer.h +++ b/statsd/src/metrics/MetricProducer.h @@ -26,11 +26,13 @@ #include "anomaly/AnomalyTracker.h" #include "condition/ConditionWizard.h" #include "config/ConfigKey.h" +#include "guardrail/StatsdStats.h" #include "matchers/EventMatcherWizard.h" #include "matchers/matcher_util.h" #include "packages/PackageInfoListener.h" #include "state/StateListener.h" #include "state/StateManager.h" +#include "utils/ShardOffsetProvider.h" namespace android { namespace os { @@ -132,6 +134,13 @@ struct SkippedBucket { } }; +struct SamplingInfo { + // Matchers for sampled fields. Currently only one sampled dimension is supported. + std::vector<Matcher> sampledWhatFields; + + int shardCount = 0; +}; + template <class T> optional<bool> getAppUpgradeBucketSplit(const T& metric) { return metric.has_split_bucket_for_app_upgrade() @@ -166,7 +175,7 @@ public: // This metric and all of its dependencies are guaranteed to be preserved across the update. // This function also updates several maps used by metricsManager. // This function clears all anomaly trackers. All anomaly trackers need to be added again. - bool onConfigUpdated( + optional<InvalidConfigReason> onConfigUpdated( const StatsdConfig& config, const int configIndex, const int metricIndex, const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, @@ -253,7 +262,7 @@ public: dumpLatency, str_set, protoOutput); } - virtual bool onConfigUpdatedLocked( + virtual optional<InvalidConfigReason> onConfigUpdatedLocked( const StatsdConfig& config, const int configIndex, const int metricIndex, const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, @@ -371,6 +380,12 @@ public: std::lock_guard<std::mutex> lock(mMutex); mAnomalyTrackers.push_back(anomalyTracker); } + + void setSamplingInfo(SamplingInfo samplingInfo) { + std::lock_guard<std::mutex> lock(mMutex); + mSampledWhatFields.swap(samplingInfo.sampledWhatFields); + mShardCount = samplingInfo.shardCount; + } // End: getters/setters protected: /** @@ -485,6 +500,8 @@ protected: // exceeded the maximum number allowed, which is currently capped at 10. bool maxDropEventsReached() const; + bool passesSampleCheckLocked(const vector<FieldValue>& values) const; + const int64_t mMetricId; // Hash of the Metric's proto bytes from StatsdConfig, including any activations. @@ -566,6 +583,11 @@ protected: // If hard dimension guardrail is hit, do not spam logcat bool mHasHitGuardrail; + // Matchers for sampled fields. Currently only one sampled dimension is supported. + std::vector<Matcher> mSampledWhatFields; + + int mShardCount; + FRIEND_TEST(CountMetricE2eTest, TestSlicedState); FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap); FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates); @@ -603,7 +625,8 @@ protected: FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges); - FRIEND_TEST(MetricsManagerTest, TestInitialConditions); + FRIEND_TEST(MetricsManagerUtilTest, TestInitialConditions); + FRIEND_TEST(MetricsManagerUtilTest, TestSampledMetrics); FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricActivations); FRIEND_TEST(ConfigUpdateTest, TestUpdateCountMetrics); diff --git a/statsd/src/metrics/MetricsManager.cpp b/statsd/src/metrics/MetricsManager.cpp index d192476b..156d7c21 100644 --- a/statsd/src/metrics/MetricsManager.cpp +++ b/statsd/src/metrics/MetricsManager.cpp @@ -82,7 +82,7 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, // Init the ttl end timestamp. refreshTtl(timeBaseNs); - mConfigValid = initStatsdConfig( + mInvalidConfigReason = initStatsdConfig( key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, currentTimeNs, mTagIdsToMatchersMap, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap, mAllConditionTrackers, mConditionTrackerMap, @@ -139,7 +139,7 @@ bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t time mDeactivationAtomTrackerToMetricMap.clear(); mMetricIndexesWithActivation.clear(); mNoReportMetricIds.clear(); - mConfigValid = updateStatsdConfig( + mInvalidConfigReason = updateStatsdConfig( mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap, mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap, @@ -188,15 +188,16 @@ bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t time verifyGuardrailsAndUpdateStatsdStats(); initializeConfigActiveStatus(); - return mConfigValid; + return !mInvalidConfigReason.has_value(); } void MetricsManager::createAllLogSourcesFromConfig(const StatsdConfig& config) { // Init allowed pushed atom uids. if (config.allowed_log_source_size() == 0) { - mConfigValid = false; ALOGE("Log source allowlist is empty! This config won't get any data. Suggest adding at " "least AID_SYSTEM and AID_STATSD to the allowed_log_source field."); + mInvalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_LOG_SOURCE_ALLOWLIST_EMPTY); } else { for (const auto& source : config.allowed_log_source()) { auto it = UidMap::sAidToUidMapping.find(source); @@ -209,7 +210,7 @@ void MetricsManager::createAllLogSourcesFromConfig(const StatsdConfig& config) { if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) { ALOGE("Too many log sources. This is likely to be an error in the config."); - mConfigValid = false; + mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_LOG_SOURCES); } else { initAllowedLogSources(); } @@ -224,7 +225,8 @@ void MetricsManager::createAllLogSourcesFromConfig(const StatsdConfig& config) { mDefaultPullUids.insert(it->second); } else { ALOGE("Default pull atom packages must be in sAidToUidMapping"); - mConfigValid = false; + mInvalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_DEFAULT_PULL_PACKAGES_NOT_IN_MAP); } } // Init per-atom pull atom packages. @@ -243,7 +245,8 @@ void MetricsManager::createAllLogSourcesFromConfig(const StatsdConfig& config) { if (numPullPackages > StatsdStats::kMaxPullAtomPackages) { ALOGE("Too many sources in default_pull_packages and pull_atom_packages. This is likely to " "be an error in the config"); - mConfigValid = false; + mInvalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_SOURCES_IN_PULL_PACKAGES); } else { initPullAtomSources(); } @@ -251,21 +254,27 @@ void MetricsManager::createAllLogSourcesFromConfig(const StatsdConfig& config) { void MetricsManager::verifyGuardrailsAndUpdateStatsdStats() { // Guardrail. Reject the config if it's too big. - if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig || - mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig || - mAllAtomMatchingTrackers.size() > StatsdStats::kMaxMatcherCountPerConfig) { - ALOGE("This config is too big! Reject!"); - mConfigValid = false; + if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig) { + ALOGE("This config has too many metrics! Reject!"); + mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_METRICS); + } + if (mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig) { + ALOGE("This config has too many predicates! Reject!"); + mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_CONDITIONS); + } + if (mAllAtomMatchingTrackers.size() > StatsdStats::kMaxMatcherCountPerConfig) { + ALOGE("This config has too many matchers! Reject!"); + mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_MATCHERS); } if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) { ALOGE("This config has too many alerts! Reject!"); - mConfigValid = false; + mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_ALERTS); } // no matter whether this config is valid, log it in the stats. StatsdStats::getInstance().noteConfigReceived( mConfigKey, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchingTrackers.size(), mAllAnomalyTrackers.size(), mAnnotations, - mConfigValid); + mInvalidConfigReason); } void MetricsManager::initializeConfigActiveStatus() { @@ -309,7 +318,7 @@ void MetricsManager::initPullAtomSources() { } bool MetricsManager::isConfigValid() const { - return mConfigValid; + return !mInvalidConfigReason.has_value(); } void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, @@ -526,7 +535,7 @@ bool MetricsManager::eventSanityCheck(const LogEvent& event) { // Consume the stats log if it's interesting to this metric. void MetricsManager::onLogEvent(const LogEvent& event) { - if (!mConfigValid) { + if (!isConfigValid()) { return; } diff --git a/statsd/src/metrics/MetricsManager.h b/statsd/src/metrics/MetricsManager.h index c855a050..94879f15 100644 --- a/statsd/src/metrics/MetricsManager.h +++ b/statsd/src/metrics/MetricsManager.h @@ -16,20 +16,21 @@ #pragma once +#include <unordered_map> + #include "anomaly/AlarmMonitor.h" #include "anomaly/AlarmTracker.h" #include "anomaly/AnomalyTracker.h" #include "condition/ConditionTracker.h" #include "config/ConfigKey.h" #include "external/StatsPullerManager.h" -#include "src/statsd_config.pb.h" -#include "src/statsd_metadata.pb.h" +#include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "matchers/AtomMatchingTracker.h" #include "metrics/MetricProducer.h" #include "packages/UidMap.h" - -#include <unordered_map> +#include "src/statsd_config.pb.h" +#include "src/statsd_metadata.pb.h" namespace android { namespace os { @@ -168,8 +169,6 @@ private: sp<UidMap> mUidMap; - bool mConfigValid = false; - bool mHashStringsInReport = false; bool mVersionStringsInReport = false; bool mInstallerInReport = false; @@ -181,6 +180,8 @@ private: int64_t mLastReportTimeNs; int64_t mLastReportWallClockNs; + optional<InvalidConfigReason> mInvalidConfigReason; + sp<StatsPullerManager> mPullerManager; // The uid log sources from StatsdConfig. @@ -294,11 +295,12 @@ private: void initPullAtomSources(); // Only called on config creation/update to initialize log sources from the config. - // Calls initAllowedLogSources and initPullAtomSources. Sets mConfigValid to false on error. + // Calls initAllowedLogSources and initPullAtomSources. Sets up mInvalidConfigReason on + // error. void createAllLogSourcesFromConfig(const StatsdConfig& config); // Verifies the config meets guardrails and updates statsdStats. - // Sets mConfigValid to false on error. Should be called on config creation/update + // Sets up mInvalidConfigReason on error. Should be called on config creation/update void verifyGuardrailsAndUpdateStatsdStats(); // Initializes mIsAlwaysActive and mIsActive. @@ -328,15 +330,16 @@ private: FRIEND_TEST(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsNoCondition); FRIEND_TEST(GaugeMetricE2ePulledTest, TestConditionChangeToTrueSamplePulledEvents); - FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_partial_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); + FRIEND_TEST(AnomalyCountDetectionE2eTest, TestSlicedCountMetric_single_bucket); + FRIEND_TEST(AnomalyCountDetectionE2eTest, TestSlicedCountMetric_multiple_buckets); + FRIEND_TEST(AnomalyCountDetectionE2eTest, + TestCountMetric_save_refractory_to_disk_no_data_written); + FRIEND_TEST(AnomalyCountDetectionE2eTest, TestCountMetric_save_refractory_to_disk); + FRIEND_TEST(AnomalyCountDetectionE2eTest, TestCountMetric_load_refractory_from_disk); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_single_bucket); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_partial_bucket); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); + FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric); @@ -350,6 +353,7 @@ private: FRIEND_TEST(MetricsManagerTest, TestLogSources); FRIEND_TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate); FRIEND_TEST(MetricsManagerTest_SPlus, TestAtomMatcherOptimizationEnabledFlag); + FRIEND_TEST(MetricsManagerUtilTest, TestSampledMetrics); FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); @@ -384,6 +388,7 @@ private: FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); + FRIEND_TEST(GaugeMetricE2ePushedTest, TestDimensionalSampling); }; } // namespace statsd diff --git a/statsd/src/metrics/NumericValueMetricProducer.cpp b/statsd/src/metrics/NumericValueMetricProducer.cpp index 58abc431..12c8c043 100644 --- a/statsd/src/metrics/NumericValueMetricProducer.cpp +++ b/statsd/src/metrics/NumericValueMetricProducer.cpp @@ -50,6 +50,7 @@ const int FIELD_ID_VALUE_METRICS = 7; const int FIELD_ID_VALUE_INDEX = 1; const int FIELD_ID_VALUE_LONG = 2; const int FIELD_ID_VALUE_DOUBLE = 3; +const int FIELD_ID_VALUE_SAMPLESIZE = 4; const int FIELD_ID_VALUES = 9; const int FIELD_ID_BUCKET_NUM = 4; const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5; @@ -71,6 +72,9 @@ NumericValueMetricProducer::NumericValueMetricProducer( conditionOptions, stateOptions, activationOptions, guardrailOptions), mUseAbsoluteValueOnReset(metric.use_absolute_value_on_reset()), mAggregationType(metric.aggregation_type()), + mIncludeSampleSize(metric.has_include_sample_size() + ? metric.include_sample_size() + : metric.aggregation_type() == ValueMetric_AggregationType_AVG), mUseDiff(metric.has_use_diff() ? metric.use_diff() : isPulled()), mValueDirection(metric.value_direction()), mSkipZeroDiffOutput(metric.skip_zero_diff_output()), @@ -112,10 +116,14 @@ void NumericValueMetricProducer::resetBase() { } void NumericValueMetricProducer::writePastBucketAggregateToProto( - const int aggIndex, const Value& value, ProtoOutputStream* const protoOutput) const { + const int aggIndex, const Value& value, const int sampleSize, + ProtoOutputStream* const protoOutput) const { uint64_t valueToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_VALUES); protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_INDEX, aggIndex); + if (mIncludeSampleSize) { + protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_SAMPLESIZE, sampleSize); + } if (value.getType() == LONG) { protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_LONG, (long long)value.long_value); VLOG("\t\t value %d: %lld", aggIndex, (long long)value.long_value); @@ -175,14 +183,14 @@ int64_t NumericValueMetricProducer::calcPreviousBucketEndTime(const int64_t curr // By design, statsd pulls data at bucket boundaries using AlarmManager. These pulls are likely // to be delayed. Other events like condition changes or app upgrade which are not based on // AlarmManager might have arrived earlier and close the bucket. -void NumericValueMetricProducer::onDataPulled(const vector<shared_ptr<LogEvent>>& allData, - bool pullSuccess, int64_t originalPullTimeNs) { +void NumericValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData, + PullResult pullResult, int64_t originalPullTimeNs) { lock_guard<mutex> lock(mMutex); if (mCondition == ConditionState::kTrue) { // If the pull failed, we won't be able to compute a diff. - if (!pullSuccess) { + if (pullResult == PullResult::PULL_RESULT_FAIL) { invalidateCurrentBucket(originalPullTimeNs, BucketDropReason::PULL_FAILED); - } else { + } else if (pullResult == PullResult::PULL_RESULT_SUCCESS) { bool isEventLate = originalPullTimeNs < getCurrentBucketEndTimeNs(); if (isEventLate) { // If the event is late, we are in the middle of a bucket. Just @@ -527,6 +535,9 @@ PastBucket<Value> NumericValueMetricProducer::buildPartialBucket(int64_t bucketE bucket.aggIndex.push_back(interval.aggIndex); bucket.aggregates.push_back(getFinalValue(interval)); + if (mIncludeSampleSize) { + bucket.sampleSizes.push_back(interval.sampleSize); + } } return bucket; } diff --git a/statsd/src/metrics/NumericValueMetricProducer.h b/statsd/src/metrics/NumericValueMetricProducer.h index 0c8b1876..78c414fc 100644 --- a/statsd/src/metrics/NumericValueMetricProducer.h +++ b/statsd/src/metrics/NumericValueMetricProducer.h @@ -39,9 +39,15 @@ public: const GuardrailOptions& guardrailOptions); // Process data pulled on bucket boundary. - void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, bool pullSuccess, + void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData, PullResult pullResult, int64_t originalPullTimeNs) override; + // Determine if metric needs to pull + bool isPullNeeded() const override { + std::lock_guard<std::mutex> lock(mMutex); + return mIsActive && (mCondition == ConditionState::kTrue); + } + inline MetricType getMetricType() const override { return METRIC_TYPE_VALUE; } @@ -130,6 +136,7 @@ private: DumpProtoFields getDumpProtoFields() const override; void writePastBucketAggregateToProto(const int aggIndex, const Value& value, + const int sampleSize, ProtoOutputStream* const protoOutput) const override; // Internal function to calculate the current used bytes. @@ -142,6 +149,8 @@ private: const ValueMetric::AggregationType mAggregationType; + const bool mIncludeSampleSize; + const bool mUseDiff; const ValueMetric::ValueDirection mValueDirection; @@ -204,6 +213,7 @@ private: TestResetBaseOnPullFailAfterConditionChange_EndOfBucket); FRIEND_TEST(NumericValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange); FRIEND_TEST(NumericValueMetricProducerTest, TestResetBaseOnPullTooLate); + FRIEND_TEST(NumericValueMetricProducerTest, TestSampleSize); FRIEND_TEST(NumericValueMetricProducerTest, TestSkipZeroDiffOutput); FRIEND_TEST(NumericValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue); FRIEND_TEST(NumericValueMetricProducerTest, TestSlicedState); diff --git a/statsd/src/metrics/ValueMetricProducer.cpp b/statsd/src/metrics/ValueMetricProducer.cpp index 48a86afc..33a29afb 100644 --- a/statsd/src/metrics/ValueMetricProducer.cpp +++ b/statsd/src/metrics/ValueMetricProducer.cpp @@ -174,7 +174,8 @@ void ValueMetricProducer<AggregatedValue, DimExtras>::notifyAppUpgradeInternalLo } template <typename AggregatedValue, typename DimExtras> -bool ValueMetricProducer<AggregatedValue, DimExtras>::onConfigUpdatedLocked( +optional<InvalidConfigReason> +ValueMetricProducer<AggregatedValue, DimExtras>::onConfigUpdatedLocked( const StatsdConfig& config, const int configIndex, const int metricIndex, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, @@ -188,35 +189,37 @@ bool ValueMetricProducer<AggregatedValue, DimExtras>::onConfigUpdatedLocked( unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, vector<int>& metricsWithActivation) { - if (!MetricProducer::onConfigUpdatedLocked( - config, configIndex, metricIndex, allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, - allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, - trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { - return false; + optional<InvalidConfigReason> invalidConfigReason = MetricProducer::onConfigUpdatedLocked( + config, configIndex, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap, + wizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } - // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps. const int64_t atomMatcherId = getWhatAtomMatcherIdForMetric(config, configIndex); - if (!handleMetricWithAtomMatchingTrackers(atomMatcherId, metricIndex, /*enforceOneAtom=*/false, - allAtomMatchingTrackers, newAtomMatchingTrackerMap, - trackerToMetricMap, mWhatMatcherIndex)) { - return false; + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + atomMatcherId, mMetricId, metricIndex, /*enforceOneAtom=*/false, + allAtomMatchingTrackers, newAtomMatchingTrackerMap, trackerToMetricMap, + mWhatMatcherIndex); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } - const optional<int64_t>& conditionIdOpt = getConditionIdForMetric(config, configIndex); const ConditionLinks& conditionLinks = getConditionLinksForMetric(config, configIndex); - if (conditionIdOpt.has_value() && - !handleMetricWithConditions(conditionIdOpt.value(), metricIndex, conditionTrackerMap, - conditionLinks, allConditionTrackers, mConditionTrackerIndex, - conditionToMetricMap)) { - return false; + if (conditionIdOpt.has_value()) { + invalidConfigReason = handleMetricWithConditions( + conditionIdOpt.value(), mMetricId, metricIndex, conditionTrackerMap, conditionLinks, + allConditionTrackers, mConditionTrackerIndex, conditionToMetricMap); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; + } } - sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard; mEventMatcherWizard = matcherWizard; - return true; + return nullopt; } template <typename AggregatedValue, typename DimExtras> @@ -422,8 +425,9 @@ void ValueMetricProducer<AggregatedValue, DimExtras>::onDumpReportLocked( for (int i = 0; i < (int)bucket.aggIndex.size(); i++) { VLOG("\t bucket [%lld - %lld]", (long long)bucket.mBucketStartNs, (long long)bucket.mBucketEndNs); + int sampleSize = !bucket.sampleSizes.empty() ? bucket.sampleSizes[i] : 0; writePastBucketAggregateToProto(bucket.aggIndex[i], bucket.aggregates[i], - protoOutput); + sampleSize, protoOutput); } protoOutput->end(bucketInfoToken); } diff --git a/statsd/src/metrics/ValueMetricProducer.h b/statsd/src/metrics/ValueMetricProducer.h index bf87c543..1c974b52 100644 --- a/statsd/src/metrics/ValueMetricProducer.h +++ b/statsd/src/metrics/ValueMetricProducer.h @@ -43,6 +43,7 @@ struct PastBucket { int64_t mBucketEndNs; std::vector<int> aggIndex; std::vector<AggregatedValue> aggregates; + std::vector<int> sampleSizes; /** * If the metric has no condition, then this field is just wasted. @@ -117,10 +118,14 @@ public: virtual ~ValueMetricProducer(); // Process data pulled on bucket boundary. - virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, bool pullSuccess, - int64_t originalPullTimeNs) override { + virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, + PullResult pullResult, int64_t originalPullTimeNs) override { } + // Determine if metric needs to pull + virtual bool isPullNeeded() const override { + return false; + } // ValueMetric needs special logic if it's a pulled atom. void onStatsdInitCompleted(const int64_t& eventTimeNs) override; @@ -211,7 +216,7 @@ protected: // causes the bucket to be invalidated will not notify StatsdStats. void skipCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason); - bool onConfigUpdatedLocked( + optional<InvalidConfigReason> onConfigUpdatedLocked( const StatsdConfig& config, const int configIndex, const int metricIndex, const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, @@ -338,6 +343,7 @@ protected: virtual void writePastBucketAggregateToProto(const int aggIndex, const AggregatedValue& aggregate, + const int sampleSize, ProtoOutputStream* const protoOutput) const = 0; static const size_t kBucketSize = sizeof(PastBucket<AggregatedValue>{}); diff --git a/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/statsd/src/metrics/parsing_utils/config_update_utils.cpp index e65e4bc7..c3972d9a 100644 --- a/statsd/src/metrics/parsing_utils/config_update_utils.cpp +++ b/statsd/src/metrics/parsing_utils/config_update_utils.cpp @@ -31,16 +31,16 @@ namespace os { namespace statsd { // Recursive function to determine if a matcher needs to be updated. Populates matcherToUpdate. -// Returns whether the function was successful or not. -bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherIdx, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - vector<UpdateStatus>& matchersToUpdate, - vector<bool>& cycleTracker) { +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> determineMatcherUpdateStatus( + const StatsdConfig& config, const int matcherIdx, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + vector<UpdateStatus>& matchersToUpdate, vector<bool>& cycleTracker) { // Have already examined this matcher. if (matchersToUpdate[matcherIdx] != UPDATE_UNKNOWN) { - return true; + return nullopt; } const AtomMatcher& matcher = config.atom_matcher(matcherIdx); @@ -49,25 +49,27 @@ bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherI const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id); if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) { matchersToUpdate[matcherIdx] = UPDATE_NEW; - return true; + return nullopt; } // This is an existing matcher. Check if it has changed. string serializedMatcher; if (!matcher.SerializeToString(&serializedMatcher)) { ALOGE("Unable to serialize matcher %lld", (long long)id); - return false; + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_SERIALIZATION_FAILED, id); } uint64_t newProtoHash = Hash64(serializedMatcher); if (newProtoHash != oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second]->getProtoHash()) { matchersToUpdate[matcherIdx] = UPDATE_REPLACE; - return true; + return nullopt; } + optional<InvalidConfigReason> invalidConfigReason; switch (matcher.contents_case()) { case AtomMatcher::ContentsCase::kSimpleAtomMatcher: { matchersToUpdate[matcherIdx] = UPDATE_PRESERVE; - return true; + return nullopt; } case AtomMatcher::ContentsCase::kCombination: { // Recurse to check if children have changed. @@ -77,17 +79,25 @@ bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherI const auto& childIt = newAtomMatchingTrackerMap.find(childMatcherId); if (childIt == newAtomMatchingTrackerMap.end()) { ALOGW("Matcher %lld not found in the config", (long long)childMatcherId); - return false; + invalidConfigReason = createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_CHILD_NOT_FOUND, id); + invalidConfigReason->matcherIds.push_back(childMatcherId); + return invalidConfigReason; } const int childIdx = childIt->second; if (cycleTracker[childIdx]) { ALOGE("Cycle detected in matcher config"); - return false; + invalidConfigReason = createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_CYCLE, id); + invalidConfigReason->matcherIds.push_back(childMatcherId); + return invalidConfigReason; } - if (!determineMatcherUpdateStatus( - config, childIdx, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, - newAtomMatchingTrackerMap, matchersToUpdate, cycleTracker)) { - return false; + invalidConfigReason = determineMatcherUpdateStatus( + config, childIdx, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, + newAtomMatchingTrackerMap, matchersToUpdate, cycleTracker); + if (invalidConfigReason.has_value()) { + invalidConfigReason->matcherIds.push_back(id); + return invalidConfigReason; } if (matchersToUpdate[childIdx] == UPDATE_REPLACE) { @@ -97,34 +107,37 @@ bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherI } matchersToUpdate[matcherIdx] = status; cycleTracker[matcherIdx] = false; - return true; + return nullopt; } default: { ALOGE("Matcher \"%lld\" malformed", (long long)id); - return false; + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_MALFORMED_CONTENTS_CASE, id); } } - return true; + return nullopt; } -bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap, - unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, - set<int64_t>& replacedMatchers) { +optional<InvalidConfigReason> updateAtomMatchingTrackers( + const StatsdConfig& config, const sp<UidMap>& uidMap, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap, + unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, set<int64_t>& replacedMatchers) { const int atomMatcherCount = config.atom_matcher_size(); vector<AtomMatcher> matcherProtos; matcherProtos.reserve(atomMatcherCount); newAtomMatchingTrackers.reserve(atomMatcherCount); + optional<InvalidConfigReason> invalidConfigReason; // Maps matcher id to their position in the config. For fast lookup of dependencies. for (int i = 0; i < atomMatcherCount; i++) { const AtomMatcher& matcher = config.atom_matcher(i); if (newAtomMatchingTrackerMap.find(matcher.id()) != newAtomMatchingTrackerMap.end()) { ALOGE("Duplicate atom matcher found for id %lld", (long long)matcher.id()); - return false; + return createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_MATCHER_DUPLICATE, + matcher.id()); } newAtomMatchingTrackerMap[matcher.id()] = i; matcherProtos.push_back(matcher); @@ -134,10 +147,11 @@ bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& ui vector<UpdateStatus> matchersToUpdate(atomMatcherCount, UPDATE_UNKNOWN); vector<bool> cycleTracker(atomMatcherCount, false); for (int i = 0; i < atomMatcherCount; i++) { - if (!determineMatcherUpdateStatus(config, i, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)) { - return false; + invalidConfigReason = determineMatcherUpdateStatus( + config, i, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, + newAtomMatchingTrackerMap, matchersToUpdate, cycleTracker); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } } @@ -151,13 +165,16 @@ bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& ui ALOGE("Could not find AtomMatcher %lld in the previous config, but expected it " "to be there", (long long)id); - return false; + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_NOT_IN_PREV_CONFIG, id); } const sp<AtomMatchingTracker>& tracker = oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second]; - if (!tracker->onConfigUpdated(matcherProtos[i], i, newAtomMatchingTrackerMap)) { + invalidConfigReason = + tracker->onConfigUpdated(matcherProtos[i], i, newAtomMatchingTrackerMap); + if (invalidConfigReason.has_value()) { ALOGW("Config update failed for matcher %lld", (long long)id); - return false; + return invalidConfigReason; } newAtomMatchingTrackers.push_back(tracker); break; @@ -166,9 +183,10 @@ bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& ui replacedMatchers.insert(id); [[fallthrough]]; // Intentionally fallthrough to create the new matcher. case UPDATE_NEW: { - sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, i, uidMap); + sp<AtomMatchingTracker> tracker = + createAtomMatchingTracker(matcher, i, uidMap, invalidConfigReason); if (tracker == nullptr) { - return false; + return invalidConfigReason; } newAtomMatchingTrackers.push_back(tracker); break; @@ -176,7 +194,8 @@ bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& ui default: { ALOGE("Matcher \"%lld\" update state is unknown. This should never happen", (long long)id); - return false; + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_UPDATE_STATUS_UNKNOWN, id); } } } @@ -184,9 +203,10 @@ bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& ui std::fill(cycleTracker.begin(), cycleTracker.end(), false); for (size_t matcherIndex = 0; matcherIndex < newAtomMatchingTrackers.size(); matcherIndex++) { auto& matcher = newAtomMatchingTrackers[matcherIndex]; - if (!matcher->init(matcherProtos, newAtomMatchingTrackers, newAtomMatchingTrackerMap, - cycleTracker)) { - return false; + invalidConfigReason = matcher->init(matcherProtos, newAtomMatchingTrackers, + newAtomMatchingTrackerMap, cycleTracker); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only. @@ -207,21 +227,21 @@ bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& ui } } - return true; + return nullopt; } // Recursive function to determine if a condition needs to be updated. Populates conditionsToUpdate. -// Returns whether the function was successful or not. -bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx, - const unordered_map<int64_t, int>& oldConditionTrackerMap, - const vector<sp<ConditionTracker>>& oldConditionTrackers, - const unordered_map<int64_t, int>& newConditionTrackerMap, - const set<int64_t>& replacedMatchers, - vector<UpdateStatus>& conditionsToUpdate, - vector<bool>& cycleTracker) { +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> determineConditionUpdateStatus( + const StatsdConfig& config, const int conditionIdx, + const unordered_map<int64_t, int>& oldConditionTrackerMap, + const vector<sp<ConditionTracker>>& oldConditionTrackers, + const unordered_map<int64_t, int>& newConditionTrackerMap, + const set<int64_t>& replacedMatchers, vector<UpdateStatus>& conditionsToUpdate, + vector<bool>& cycleTracker) { // Have already examined this condition. if (conditionsToUpdate[conditionIdx] != UPDATE_UNKNOWN) { - return true; + return nullopt; } const Predicate& predicate = config.predicate(conditionIdx); @@ -230,21 +250,23 @@ bool determineConditionUpdateStatus(const StatsdConfig& config, const int condit const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id); if (oldConditionTrackerIt == oldConditionTrackerMap.end()) { conditionsToUpdate[conditionIdx] = UPDATE_NEW; - return true; + return nullopt; } // This is an existing condition. Check if it has changed. string serializedCondition; if (!predicate.SerializeToString(&serializedCondition)) { - ALOGE("Unable to serialize matcher %lld", (long long)id); - return false; + ALOGE("Unable to serialize predicate %lld", (long long)id); + return createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_SERIALIZATION_FAILED, id); } uint64_t newProtoHash = Hash64(serializedCondition); if (newProtoHash != oldConditionTrackers[oldConditionTrackerIt->second]->getProtoHash()) { conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; - return true; + return nullopt; } + optional<InvalidConfigReason> invalidConfigReason; switch (predicate.contents_case()) { case Predicate::ContentsCase::kSimplePredicate: { // Need to check if any of the underlying matchers changed. @@ -252,23 +274,23 @@ bool determineConditionUpdateStatus(const StatsdConfig& config, const int condit if (simplePredicate.has_start()) { if (replacedMatchers.find(simplePredicate.start()) != replacedMatchers.end()) { conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; - return true; + return nullopt; } } if (simplePredicate.has_stop()) { if (replacedMatchers.find(simplePredicate.stop()) != replacedMatchers.end()) { conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; - return true; + return nullopt; } } if (simplePredicate.has_stop_all()) { if (replacedMatchers.find(simplePredicate.stop_all()) != replacedMatchers.end()) { conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; - return true; + return nullopt; } } conditionsToUpdate[conditionIdx] = UPDATE_PRESERVE; - return true; + return nullopt; } case Predicate::ContentsCase::kCombination: { // Need to recurse on the children to see if any of the child predicates changed. @@ -278,18 +300,25 @@ bool determineConditionUpdateStatus(const StatsdConfig& config, const int condit const auto& childIt = newConditionTrackerMap.find(childPredicateId); if (childIt == newConditionTrackerMap.end()) { ALOGW("Predicate %lld not found in the config", (long long)childPredicateId); - return false; + invalidConfigReason = createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_CHILD_NOT_FOUND, id); + invalidConfigReason->conditionIds.push_back(childPredicateId); + return invalidConfigReason; } const int childIdx = childIt->second; if (cycleTracker[childIdx]) { ALOGE("Cycle detected in predicate config"); - return false; + invalidConfigReason = createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_CYCLE, id); + invalidConfigReason->conditionIds.push_back(childPredicateId); + return invalidConfigReason; } - if (!determineConditionUpdateStatus(config, childIdx, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, - cycleTracker)) { - return false; + invalidConfigReason = determineConditionUpdateStatus( + config, childIdx, oldConditionTrackerMap, oldConditionTrackers, + newConditionTrackerMap, replacedMatchers, conditionsToUpdate, cycleTracker); + if (invalidConfigReason.has_value()) { + invalidConfigReason->conditionIds.push_back(id); + return invalidConfigReason; } if (conditionsToUpdate[childIdx] == UPDATE_REPLACE) { @@ -299,37 +328,41 @@ bool determineConditionUpdateStatus(const StatsdConfig& config, const int condit } conditionsToUpdate[conditionIdx] = status; cycleTracker[conditionIdx] = false; - return true; + return nullopt; } default: { ALOGE("Predicate \"%lld\" malformed", (long long)id); - return false; + return createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_MALFORMED_CONTENTS_CASE, id); } } - return true; + return nullopt; } -bool updateConditions(const ConfigKey& key, const StatsdConfig& config, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - const set<int64_t>& replacedMatchers, - const unordered_map<int64_t, int>& oldConditionTrackerMap, - const vector<sp<ConditionTracker>>& oldConditionTrackers, - unordered_map<int64_t, int>& newConditionTrackerMap, - vector<sp<ConditionTracker>>& newConditionTrackers, - unordered_map<int, vector<int>>& trackerToConditionMap, - vector<ConditionState>& conditionCache, set<int64_t>& replacedConditions) { +optional<InvalidConfigReason> updateConditions( + const ConfigKey& key, const StatsdConfig& config, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, + const set<int64_t>& replacedMatchers, + const unordered_map<int64_t, int>& oldConditionTrackerMap, + const vector<sp<ConditionTracker>>& oldConditionTrackers, + unordered_map<int64_t, int>& newConditionTrackerMap, + vector<sp<ConditionTracker>>& newConditionTrackers, + unordered_map<int, vector<int>>& trackerToConditionMap, + vector<ConditionState>& conditionCache, set<int64_t>& replacedConditions) { vector<Predicate> conditionProtos; const int conditionTrackerCount = config.predicate_size(); conditionProtos.reserve(conditionTrackerCount); newConditionTrackers.reserve(conditionTrackerCount); conditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated); + optional<InvalidConfigReason> invalidConfigReason; for (int i = 0; i < conditionTrackerCount; i++) { const Predicate& condition = config.predicate(i); if (newConditionTrackerMap.find(condition.id()) != newConditionTrackerMap.end()) { ALOGE("Duplicate Predicate found!"); - return false; + return createInvalidConfigReasonWithPredicate(INVALID_CONFIG_REASON_CONDITION_DUPLICATE, + condition.id()); } newConditionTrackerMap[condition.id()] = i; conditionProtos.push_back(condition); @@ -338,10 +371,11 @@ bool updateConditions(const ConfigKey& key, const StatsdConfig& config, vector<UpdateStatus> conditionsToUpdate(conditionTrackerCount, UPDATE_UNKNOWN); vector<bool> cycleTracker(conditionTrackerCount, false); for (int i = 0; i < conditionTrackerCount; i++) { - if (!determineConditionUpdateStatus(config, i, oldConditionTrackerMap, oldConditionTrackers, - newConditionTrackerMap, replacedMatchers, - conditionsToUpdate, cycleTracker)) { - return false; + invalidConfigReason = determineConditionUpdateStatus( + config, i, oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, cycleTracker); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } } @@ -358,7 +392,8 @@ bool updateConditions(const ConfigKey& key, const StatsdConfig& config, ALOGE("Could not find Predicate %lld in the previous config, but expected it " "to be there", (long long)id); - return false; + return createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_NOT_IN_PREV_CONFIG, id); } const int oldIndex = oldConditionTrackerIt->second; newConditionTrackers.push_back(oldConditionTrackers[oldIndex]); @@ -368,10 +403,10 @@ bool updateConditions(const ConfigKey& key, const StatsdConfig& config, replacedConditions.insert(id); [[fallthrough]]; // Intentionally fallthrough to create the new condition tracker. case UPDATE_NEW: { - sp<ConditionTracker> tracker = - createConditionTracker(key, predicate, i, atomMatchingTrackerMap); + sp<ConditionTracker> tracker = createConditionTracker( + key, predicate, i, atomMatchingTrackerMap, invalidConfigReason); if (tracker == nullptr) { - return false; + return invalidConfigReason; } newConditionTrackers.push_back(tracker); break; @@ -379,19 +414,21 @@ bool updateConditions(const ConfigKey& key, const StatsdConfig& config, default: { ALOGE("Condition \"%lld\" update state is unknown. This should never happen", (long long)id); - return false; + return createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_UPDATE_STATUS_UNKNOWN, id); } } } // Update indices of preserved predicates. for (const int conditionIndex : preservedConditions) { - if (!newConditionTrackers[conditionIndex]->onConfigUpdated( - conditionProtos, conditionIndex, newConditionTrackers, atomMatchingTrackerMap, - newConditionTrackerMap)) { + invalidConfigReason = newConditionTrackers[conditionIndex]->onConfigUpdated( + conditionProtos, conditionIndex, newConditionTrackers, atomMatchingTrackerMap, + newConditionTrackerMap); + if (invalidConfigReason.has_value()) { ALOGE("Failed to update condition %lld", (long long)newConditionTrackers[conditionIndex]->getConditionId()); - return false; + return invalidConfigReason; } } @@ -399,25 +436,30 @@ bool updateConditions(const ConfigKey& key, const StatsdConfig& config, for (int conditionIndex = 0; conditionIndex < conditionTrackerCount; conditionIndex++) { const sp<ConditionTracker>& conditionTracker = newConditionTrackers[conditionIndex]; // Calling init on preserved conditions is OK. It is needed to fill the condition cache. - if (!conditionTracker->init(conditionProtos, newConditionTrackers, newConditionTrackerMap, - cycleTracker, conditionCache)) { - return false; + invalidConfigReason = + conditionTracker->init(conditionProtos, newConditionTrackers, + newConditionTrackerMap, cycleTracker, conditionCache); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) { vector<int>& conditionList = trackerToConditionMap[trackerIndex]; conditionList.push_back(conditionIndex); } } - return true; + return nullopt; } -bool updateStates(const StatsdConfig& config, const map<int64_t, uint64_t>& oldStateProtoHashes, - unordered_map<int64_t, int>& stateAtomIdMap, - unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - map<int64_t, uint64_t>& newStateProtoHashes, set<int64_t>& replacedStates) { +optional<InvalidConfigReason> updateStates( + const StatsdConfig& config, const map<int64_t, uint64_t>& oldStateProtoHashes, + unordered_map<int64_t, int>& stateAtomIdMap, + unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, + map<int64_t, uint64_t>& newStateProtoHashes, set<int64_t>& replacedStates) { // Share with metrics_manager_util. - if (!initStates(config, stateAtomIdMap, allStateGroupMaps, newStateProtoHashes)) { - return false; + optional<InvalidConfigReason> invalidConfigReason = + initStates(config, stateAtomIdMap, allStateGroupMaps, newStateProtoHashes); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } for (const auto& [stateId, stateHash] : oldStateProtoHashes) { @@ -426,7 +468,7 @@ bool updateStates(const StatsdConfig& config, const map<int64_t, uint64_t>& oldS replacedStates.insert(stateId); } } - return true; + return nullopt; } // Returns true if any matchers in the metric activation were replaced. bool metricActivationDepsChange(const StatsdConfig& config, @@ -452,7 +494,7 @@ bool metricActivationDepsChange(const StatsdConfig& config, return false; } -bool determineMetricUpdateStatus( +optional<InvalidConfigReason> determineMetricUpdateStatus( const StatsdConfig& config, const MessageLite& metric, const int64_t metricId, const MetricType metricType, const set<int64_t>& matcherDependencies, const set<int64_t>& conditionDependencies, @@ -467,19 +509,21 @@ bool determineMetricUpdateStatus( const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId); if (oldMetricProducerIt == oldMetricProducerMap.end()) { updateStatus = UPDATE_NEW; - return true; + return nullopt; } // This is an existing metric, check if it has changed. uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metricId, metricToActivationMap, metricHash)) { - return false; + optional<InvalidConfigReason> invalidConfigReason = + getMetricProtoHash(config, metric, metricId, metricToActivationMap, metricHash); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } const sp<MetricProducer> oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second]; if (oldMetricProducer->getMetricType() != metricType || oldMetricProducer->getProtoHash() != metricHash) { updateStatus = UPDATE_REPLACE; - return true; + return nullopt; } // Take intersections of the matchers/predicates/states that the metric @@ -491,60 +535,59 @@ bool determineMetricUpdateStatus( inserter(intersection, intersection.begin())); if (intersection.size() > 0) { updateStatus = UPDATE_REPLACE; - return true; + return nullopt; } set_intersection(conditionDependencies.begin(), conditionDependencies.end(), replacedConditions.begin(), replacedConditions.end(), inserter(intersection, intersection.begin())); if (intersection.size() > 0) { updateStatus = UPDATE_REPLACE; - return true; + return nullopt; } set_intersection(stateDependencies.begin(), stateDependencies.end(), replacedStates.begin(), replacedStates.end(), inserter(intersection, intersection.begin())); if (intersection.size() > 0) { updateStatus = UPDATE_REPLACE; - return true; + return nullopt; } for (const auto& metricConditionLink : conditionLinks) { if (replacedConditions.find(metricConditionLink.condition()) != replacedConditions.end()) { updateStatus = UPDATE_REPLACE; - return true; + return nullopt; } } if (metricActivationDepsChange(config, metricToActivationMap, metricId, replacedMatchers)) { updateStatus = UPDATE_REPLACE; - return true; + return nullopt; } updateStatus = UPDATE_PRESERVE; - return true; + return nullopt; } -bool determineAllMetricUpdateStatuses(const StatsdConfig& config, - const unordered_map<int64_t, int>& oldMetricProducerMap, - const vector<sp<MetricProducer>>& oldMetricProducers, - const unordered_map<int64_t, int>& metricToActivationMap, - const set<int64_t>& replacedMatchers, - const set<int64_t>& replacedConditions, - const set<int64_t>& replacedStates, - vector<UpdateStatus>& metricsToUpdate) { +optional<InvalidConfigReason> determineAllMetricUpdateStatuses( + const StatsdConfig& config, const unordered_map<int64_t, int>& oldMetricProducerMap, + const vector<sp<MetricProducer>>& oldMetricProducers, + const unordered_map<int64_t, int>& metricToActivationMap, + const set<int64_t>& replacedMatchers, const set<int64_t>& replacedConditions, + const set<int64_t>& replacedStates, vector<UpdateStatus>& metricsToUpdate) { int metricIndex = 0; + optional<InvalidConfigReason> invalidConfigReason; for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) { const CountMetric& metric = config.count_metric(i); set<int64_t> conditionDependencies; if (metric.has_condition()) { conditionDependencies.insert(metric.condition()); } - if (!determineMetricUpdateStatus( - config, metric, metric.id(), METRIC_TYPE_COUNT, {metric.what()}, - conditionDependencies, metric.slice_by_state(), metric.links(), - oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, replacedStates, - metricsToUpdate[metricIndex])) { - return false; + invalidConfigReason = determineMetricUpdateStatus( + config, metric, metric.id(), METRIC_TYPE_COUNT, {metric.what()}, + conditionDependencies, metric.slice_by_state(), metric.links(), + oldMetricProducerMap, oldMetricProducers, metricToActivationMap, replacedMatchers, + replacedConditions, replacedStates, metricsToUpdate[metricIndex]); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } } for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) { @@ -553,13 +596,13 @@ bool determineAllMetricUpdateStatuses(const StatsdConfig& config, if (metric.has_condition()) { conditionDependencies.insert(metric.condition()); } - if (!determineMetricUpdateStatus( - config, metric, metric.id(), METRIC_TYPE_DURATION, /*matcherDependencies=*/{}, - conditionDependencies, metric.slice_by_state(), metric.links(), - oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, replacedStates, - metricsToUpdate[metricIndex])) { - return false; + invalidConfigReason = determineMetricUpdateStatus( + config, metric, metric.id(), METRIC_TYPE_DURATION, /*matcherDependencies=*/{}, + conditionDependencies, metric.slice_by_state(), metric.links(), + oldMetricProducerMap, oldMetricProducers, metricToActivationMap, replacedMatchers, + replacedConditions, replacedStates, metricsToUpdate[metricIndex]); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } } for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) { @@ -568,13 +611,13 @@ bool determineAllMetricUpdateStatuses(const StatsdConfig& config, if (metric.has_condition()) { conditionDependencies.insert(metric.condition()); } - if (!determineMetricUpdateStatus( - config, metric, metric.id(), METRIC_TYPE_EVENT, {metric.what()}, - conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(), - metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, replacedStates, - metricsToUpdate[metricIndex])) { - return false; + invalidConfigReason = determineMetricUpdateStatus( + config, metric, metric.id(), METRIC_TYPE_EVENT, {metric.what()}, + conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(), metric.links(), + oldMetricProducerMap, oldMetricProducers, metricToActivationMap, replacedMatchers, + replacedConditions, replacedStates, metricsToUpdate[metricIndex]); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } } for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) { @@ -583,13 +626,13 @@ bool determineAllMetricUpdateStatuses(const StatsdConfig& config, if (metric.has_condition()) { conditionDependencies.insert(metric.condition()); } - if (!determineMetricUpdateStatus( - config, metric, metric.id(), METRIC_TYPE_VALUE, {metric.what()}, - conditionDependencies, metric.slice_by_state(), metric.links(), - oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, replacedStates, - metricsToUpdate[metricIndex])) { - return false; + invalidConfigReason = determineMetricUpdateStatus( + config, metric, metric.id(), METRIC_TYPE_VALUE, {metric.what()}, + conditionDependencies, metric.slice_by_state(), metric.links(), + oldMetricProducerMap, oldMetricProducers, metricToActivationMap, replacedMatchers, + replacedConditions, replacedStates, metricsToUpdate[metricIndex]); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } } for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) { @@ -602,13 +645,13 @@ bool determineAllMetricUpdateStatuses(const StatsdConfig& config, if (metric.has_trigger_event()) { matcherDependencies.insert(metric.trigger_event()); } - if (!determineMetricUpdateStatus( - config, metric, metric.id(), METRIC_TYPE_GAUGE, matcherDependencies, - conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(), - metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, replacedStates, - metricsToUpdate[metricIndex])) { - return false; + invalidConfigReason = determineMetricUpdateStatus( + config, metric, metric.id(), METRIC_TYPE_GAUGE, matcherDependencies, + conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(), metric.links(), + oldMetricProducerMap, oldMetricProducers, metricToActivationMap, replacedMatchers, + replacedConditions, replacedStates, metricsToUpdate[metricIndex]); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } } @@ -618,17 +661,17 @@ bool determineAllMetricUpdateStatuses(const StatsdConfig& config, if (metric.has_condition()) { conditionDependencies.insert(metric.condition()); } - if (!determineMetricUpdateStatus( - config, metric, metric.id(), METRIC_TYPE_KLL, {metric.what()}, - conditionDependencies, metric.slice_by_state(), metric.links(), - oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, replacedStates, - metricsToUpdate[metricIndex])) { - return false; + invalidConfigReason = determineMetricUpdateStatus( + config, metric, metric.id(), METRIC_TYPE_KLL, {metric.what()}, + conditionDependencies, metric.slice_by_state(), metric.links(), + oldMetricProducerMap, oldMetricProducers, metricToActivationMap, replacedMatchers, + replacedConditions, replacedStates, metricsToUpdate[metricIndex]); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } } - return true; + return nullopt; } // Called when a metric is preserved during a config update. Finds the metric in oldMetricProducers @@ -648,56 +691,58 @@ optional<sp<MetricProducer>> updateMetric( unordered_map<int, vector<int>>& conditionToMetricMap, unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { + vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason) { const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId); if (oldMetricProducerIt == oldMetricProducerMap.end()) { ALOGE("Could not find Metric %lld in the previous config, but expected it " "to be there", (long long)metricId); + invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_NOT_IN_PREV_CONFIG, metricId); return nullopt; } const int oldIndex = oldMetricProducerIt->second; sp<MetricProducer> producer = oldMetricProducers[oldIndex]; - if (!producer->onConfigUpdated(config, configIndex, metricIndex, allAtomMatchingTrackers, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, - matcherWizard, allConditionTrackers, conditionTrackerMap, wizard, - metricToActivationMap, trackerToMetricMap, conditionToMetricMap, - activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { + invalidConfigReason = producer->onConfigUpdated( + config, configIndex, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap, + wizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation); + if (invalidConfigReason.has_value()) { return nullopt; } return {producer}; } -bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - const set<int64_t>& replacedMatchers, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& conditionTrackerMap, - const set<int64_t>& replacedConditions, - vector<sp<ConditionTracker>>& allConditionTrackers, - const vector<ConditionState>& initialConditionCache, - const unordered_map<int64_t, int>& stateAtomIdMap, - const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - const set<int64_t>& replacedStates, - const unordered_map<int64_t, int>& oldMetricProducerMap, - const vector<sp<MetricProducer>>& oldMetricProducers, - unordered_map<int64_t, int>& newMetricProducerMap, - vector<sp<MetricProducer>>& newMetricProducers, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - set<int64_t>& noReportMetricIds, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation, set<int64_t>& replacedMetrics) { +optional<InvalidConfigReason> updateMetrics( + const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, + const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const set<int64_t>& replacedMatchers, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& conditionTrackerMap, + const set<int64_t>& replacedConditions, vector<sp<ConditionTracker>>& allConditionTrackers, + const vector<ConditionState>& initialConditionCache, + const unordered_map<int64_t, int>& stateAtomIdMap, + const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, + const set<int64_t>& replacedStates, const unordered_map<int64_t, int>& oldMetricProducerMap, + const vector<sp<MetricProducer>>& oldMetricProducers, + unordered_map<int64_t, int>& newMetricProducerMap, + vector<sp<MetricProducer>>& newMetricProducers, + unordered_map<int, vector<int>>& conditionToMetricMap, + unordered_map<int, vector<int>>& trackerToMetricMap, set<int64_t>& noReportMetricIds, + unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation, set<int64_t>& replacedMetrics) { sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers); sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers); const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() + config.event_metric_size() + config.gauge_metric_size() + config.value_metric_size() + config.kll_metric_size(); newMetricProducers.reserve(allMetricsCount); + optional<InvalidConfigReason> invalidConfigReason; // Construct map from metric id to metric activation index. The map will be used to determine // the metric activation corresponding to a metric. @@ -707,16 +752,18 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 int64_t metricId = metricActivation.metric_id(); if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) { ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_HAS_MULTIPLE_ACTIVATIONS, + metricId); } metricToActivationMap.insert({metricId, i}); } vector<UpdateStatus> metricsToUpdate(allMetricsCount, UPDATE_UNKNOWN); - if (!determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, replacedMatchers, - replacedConditions, replacedStates, metricsToUpdate)) { - return false; + invalidConfigReason = determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + replacedMatchers, replacedConditions, replacedStates, metricsToUpdate); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } // Now, perform the update. Must iterate the metric types in the same order @@ -733,7 +780,8 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); + deactivationAtomTrackerToMetricMap, metricsWithActivation, + invalidConfigReason); break; } case UPDATE_REPLACE: @@ -746,17 +794,19 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); + deactivationAtomTrackerToMetricMap, metricsWithActivation, + invalidConfigReason); break; } default: { ALOGE("Metric \"%lld\" update state is unknown. This should never happen", (long long)metric.id()); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_UPDATE_STATUS_UNKNOWN, + metric.id()); } } if (!producer) { - return false; + return invalidConfigReason; } newMetricProducers.push_back(producer.value()); } @@ -772,7 +822,8 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); + deactivationAtomTrackerToMetricMap, metricsWithActivation, + invalidConfigReason); break; } case UPDATE_REPLACE: @@ -785,17 +836,19 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); + deactivationAtomTrackerToMetricMap, metricsWithActivation, + invalidConfigReason); break; } default: { ALOGE("Metric \"%lld\" update state is unknown. This should never happen", (long long)metric.id()); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_UPDATE_STATUS_UNKNOWN, + metric.id()); } } if (!producer) { - return false; + return invalidConfigReason; } newMetricProducers.push_back(producer.value()); } @@ -811,7 +864,8 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); + deactivationAtomTrackerToMetricMap, metricsWithActivation, + invalidConfigReason); break; } case UPDATE_REPLACE: @@ -823,17 +877,19 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); + deactivationAtomTrackerToMetricMap, metricsWithActivation, + invalidConfigReason); break; } default: { ALOGE("Metric \"%lld\" update state is unknown. This should never happen", (long long)metric.id()); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_UPDATE_STATUS_UNKNOWN, + metric.id()); } } if (!producer) { - return false; + return invalidConfigReason; } newMetricProducers.push_back(producer.value()); } @@ -850,7 +906,8 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); + deactivationAtomTrackerToMetricMap, metricsWithActivation, + invalidConfigReason); break; } case UPDATE_REPLACE: @@ -863,17 +920,19 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 conditionTrackerMap, initialConditionCache, wizard, matcherWizard, stateAtomIdMap, allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); + deactivationAtomTrackerToMetricMap, metricsWithActivation, + invalidConfigReason); break; } default: { ALOGE("Metric \"%lld\" update state is unknown. This should never happen", (long long)metric.id()); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_UPDATE_STATUS_UNKNOWN, + metric.id()); } } if (!producer) { - return false; + return invalidConfigReason; } newMetricProducers.push_back(producer.value()); } @@ -890,7 +949,8 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); + deactivationAtomTrackerToMetricMap, metricsWithActivation, + invalidConfigReason); break; } case UPDATE_REPLACE: @@ -903,17 +963,18 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 conditionTrackerMap, initialConditionCache, wizard, matcherWizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation); + metricsWithActivation, invalidConfigReason); break; } default: { ALOGE("Metric \"%lld\" update state is unknown. This should never happen", (long long)metric.id()); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_UPDATE_STATUS_UNKNOWN, + metric.id()); } } if (!producer) { - return false; + return invalidConfigReason; } newMetricProducers.push_back(producer.value()); } @@ -930,7 +991,8 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); + deactivationAtomTrackerToMetricMap, metricsWithActivation, + invalidConfigReason); break; } case UPDATE_REPLACE: @@ -944,17 +1006,19 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 conditionTrackerMap, initialConditionCache, wizard, matcherWizard, stateAtomIdMap, allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); + deactivationAtomTrackerToMetricMap, metricsWithActivation, + invalidConfigReason); break; } default: { ALOGE("Metric \"%lld\" update state is unknown. This should never happen", (long long)metric.id()); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_UPDATE_STATUS_UNKNOWN, + metric.id()); } } if (!producer) { - return false; + return invalidConfigReason; } newMetricProducers.push_back(producer.value()); } @@ -963,7 +1027,8 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 const int64_t noReportMetric = config.no_report_metric(i); if (newMetricProducerMap.find(noReportMetric) == newMetricProducerMap.end()) { ALOGW("no_report_metric %" PRId64 " not exist", noReportMetric); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_NO_REPORT_METRIC_NOT_FOUND, + noReportMetric); } noReportMetricIds.insert(noReportMetric); } @@ -977,7 +1042,9 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 // Using atoms allowed from any uid as a sliced state atom is not allowed. // Redo this check for all metrics in case the atoms allowed from any uid changed. if (atomsAllowedFromAnyUid.find(atomId) != atomsAllowedFromAnyUid.end()) { - return false; + return InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_SLICED_STATE_ATOM_ALLOWED_FROM_ANY_UID, + producer->getMetricId()); // Preserved metrics should've already registered.` } else if (metricsToUpdate[i] != UPDATE_PRESERVE) { StateManager::getInstance().registerListener(atomId, producer); @@ -991,62 +1058,66 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 newMetricProducers[i]->prepareFirstBucket(); } } - return true; + return nullopt; } -bool determineAlertUpdateStatus(const Alert& alert, - const unordered_map<int64_t, int>& oldAlertTrackerMap, - const vector<sp<AnomalyTracker>>& oldAnomalyTrackers, - const set<int64_t>& replacedMetrics, UpdateStatus& updateStatus) { +optional<InvalidConfigReason> determineAlertUpdateStatus( + const Alert& alert, const unordered_map<int64_t, int>& oldAlertTrackerMap, + const vector<sp<AnomalyTracker>>& oldAnomalyTrackers, const set<int64_t>& replacedMetrics, + UpdateStatus& updateStatus) { // Check if new alert. const auto& oldAnomalyTrackerIt = oldAlertTrackerMap.find(alert.id()); if (oldAnomalyTrackerIt == oldAlertTrackerMap.end()) { updateStatus = UPDATE_NEW; - return true; + return nullopt; } // This is an existing alert, check if it has changed. string serializedAlert; if (!alert.SerializeToString(&serializedAlert)) { ALOGW("Unable to serialize alert %lld", (long long)alert.id()); - return false; + return createInvalidConfigReasonWithAlert(INVALID_CONFIG_REASON_ALERT_SERIALIZATION_FAILED, + alert.id()); } uint64_t newProtoHash = Hash64(serializedAlert); - const auto [success, oldProtoHash] = + const auto [invalidConfigReason, oldProtoHash] = oldAnomalyTrackers[oldAnomalyTrackerIt->second]->getProtoHash(); - if (!success) { - return false; + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } if (newProtoHash != oldProtoHash) { updateStatus = UPDATE_REPLACE; - return true; + return nullopt; } // Check if the metric this alert relies on has changed. if (replacedMetrics.find(alert.metric_id()) != replacedMetrics.end()) { updateStatus = UPDATE_REPLACE; - return true; + return nullopt; } updateStatus = UPDATE_PRESERVE; - return true; + return nullopt; } -bool updateAlerts(const StatsdConfig& config, const int64_t currentTimeNs, - const unordered_map<int64_t, int>& metricProducerMap, - const set<int64_t>& replacedMetrics, - const unordered_map<int64_t, int>& oldAlertTrackerMap, - const vector<sp<AnomalyTracker>>& oldAnomalyTrackers, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - vector<sp<MetricProducer>>& allMetricProducers, - unordered_map<int64_t, int>& newAlertTrackerMap, - vector<sp<AnomalyTracker>>& newAnomalyTrackers) { +optional<InvalidConfigReason> updateAlerts(const StatsdConfig& config, const int64_t currentTimeNs, + const unordered_map<int64_t, int>& metricProducerMap, + const set<int64_t>& replacedMetrics, + const unordered_map<int64_t, int>& oldAlertTrackerMap, + const vector<sp<AnomalyTracker>>& oldAnomalyTrackers, + const sp<AlarmMonitor>& anomalyAlarmMonitor, + vector<sp<MetricProducer>>& allMetricProducers, + unordered_map<int64_t, int>& newAlertTrackerMap, + vector<sp<AnomalyTracker>>& newAnomalyTrackers) { int alertCount = config.alert_size(); vector<UpdateStatus> alertUpdateStatuses(alertCount); + optional<InvalidConfigReason> invalidConfigReason; for (int i = 0; i < alertCount; i++) { - if (!determineAlertUpdateStatus(config.alert(i), oldAlertTrackerMap, oldAnomalyTrackers, - replacedMetrics, alertUpdateStatuses[i])) { - return false; + invalidConfigReason = + determineAlertUpdateStatus(config.alert(i), oldAlertTrackerMap, oldAnomalyTrackers, + replacedMetrics, alertUpdateStatuses[i]); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } } @@ -1061,7 +1132,8 @@ bool updateAlerts(const StatsdConfig& config, const int64_t currentTimeNs, ALOGW("Could not find AnomalyTracker %lld in the previous config, but " "expected it to be there", (long long)alert.id()); - return false; + return createInvalidConfigReasonWithAlert( + INVALID_CONFIG_REASON_ALERT_NOT_IN_PREV_CONFIG, alert.id()); } sp<AnomalyTracker> anomalyTracker = oldAnomalyTrackers[oldAnomalyTrackerIt->second]; anomalyTracker->onConfigUpdated(); @@ -1070,7 +1142,9 @@ bool updateAlerts(const StatsdConfig& config, const int64_t currentTimeNs, if (metricProducerIt == metricProducerMap.end()) { ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(), (long long)alert.metric_id()); - return false; + return createInvalidConfigReasonWithAlert( + INVALID_CONFIG_REASON_ALERT_METRIC_NOT_FOUND, alert.metric_id(), + alert.id()); } allMetricProducers[metricProducerIt->second]->addAnomalyTracker(anomalyTracker, currentTimeNs); @@ -1079,11 +1153,11 @@ bool updateAlerts(const StatsdConfig& config, const int64_t currentTimeNs, } case UPDATE_REPLACE: case UPDATE_NEW: { - optional<sp<AnomalyTracker>> anomalyTracker = - createAnomalyTracker(alert, anomalyAlarmMonitor, alertUpdateStatuses[i], - currentTimeNs, metricProducerMap, allMetricProducers); + optional<sp<AnomalyTracker>> anomalyTracker = createAnomalyTracker( + alert, anomalyAlarmMonitor, alertUpdateStatuses[i], currentTimeNs, + metricProducerMap, allMetricProducers, invalidConfigReason); if (!anomalyTracker) { - return false; + return invalidConfigReason; } newAnomalyTrackers.push_back(anomalyTracker.value()); break; @@ -1091,49 +1165,49 @@ bool updateAlerts(const StatsdConfig& config, const int64_t currentTimeNs, default: { ALOGE("Alert \"%lld\" update state is unknown. This should never happen", (long long)alert.id()); - return false; + return createInvalidConfigReasonWithAlert( + INVALID_CONFIG_REASON_ALERT_UPDATE_STATUS_UNKNOWN, alert.id()); } } } - if (!initSubscribersForSubscriptionType(config, Subscription::ALERT, newAlertTrackerMap, - newAnomalyTrackers)) { - return false; + invalidConfigReason = initSubscribersForSubscriptionType( + config, Subscription::ALERT, newAlertTrackerMap, newAnomalyTrackers); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } - return true; + return nullopt; } -bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, - const sp<StatsPullerManager>& pullerManager, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, - const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const vector<sp<ConditionTracker>>& oldConditionTrackers, - const unordered_map<int64_t, int>& oldConditionTrackerMap, - const vector<sp<MetricProducer>>& oldMetricProducers, - const unordered_map<int64_t, int>& oldMetricProducerMap, - const vector<sp<AnomalyTracker>>& oldAnomalyTrackers, - const unordered_map<int64_t, int>& oldAlertTrackerMap, - const map<int64_t, uint64_t>& oldStateProtoHashes, - std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap, - vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, - unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - vector<sp<ConditionTracker>>& newConditionTrackers, - unordered_map<int64_t, int>& newConditionTrackerMap, - vector<sp<MetricProducer>>& newMetricProducers, - unordered_map<int64_t, int>& newMetricProducerMap, - vector<sp<AnomalyTracker>>& newAnomalyTrackers, - unordered_map<int64_t, int>& newAlertTrackerMap, - vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int, vector<int>>& trackerToConditionMap, - unordered_map<int, vector<int>>& activationTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationTrackerToMetricMap, - vector<int>& metricsWithActivation, - map<int64_t, uint64_t>& newStateProtoHashes, - set<int64_t>& noReportMetricIds) { +optional<InvalidConfigReason> updateStatsdConfig( + const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, + const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor, + const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, + const int64_t currentTimeNs, const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const vector<sp<ConditionTracker>>& oldConditionTrackers, + const unordered_map<int64_t, int>& oldConditionTrackerMap, + const vector<sp<MetricProducer>>& oldMetricProducers, + const unordered_map<int64_t, int>& oldMetricProducerMap, + const vector<sp<AnomalyTracker>>& oldAnomalyTrackers, + const unordered_map<int64_t, int>& oldAlertTrackerMap, + const map<int64_t, uint64_t>& oldStateProtoHashes, + std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap, + vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, + unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + vector<sp<ConditionTracker>>& newConditionTrackers, + unordered_map<int64_t, int>& newConditionTrackerMap, + vector<sp<MetricProducer>>& newMetricProducers, + unordered_map<int64_t, int>& newMetricProducerMap, + vector<sp<AnomalyTracker>>& newAnomalyTrackers, + unordered_map<int64_t, int>& newAlertTrackerMap, + vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers, + unordered_map<int, vector<int>>& conditionToMetricMap, + unordered_map<int, vector<int>>& trackerToMetricMap, + unordered_map<int, vector<int>>& trackerToConditionMap, + unordered_map<int, vector<int>>& activationTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationTrackerToMetricMap, + vector<int>& metricsWithActivation, map<int64_t, uint64_t>& newStateProtoHashes, + set<int64_t>& noReportMetricIds) { set<int64_t> replacedMatchers; set<int64_t> replacedConditions; set<int64_t> replacedStates; @@ -1145,56 +1219,63 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const if (config.package_certificate_hash_size_bytes() > UINT8_MAX) { ALOGE("Invalid value for package_certificate_hash_size_bytes: %d", config.package_certificate_hash_size_bytes()); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_PACKAGE_CERT_HASH_SIZE_TOO_LARGE); } - if (!updateAtomMatchingTrackers(config, uidMap, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, allTagIdsToMatchersMap, - newAtomMatchingTrackerMap, newAtomMatchingTrackers, - replacedMatchers)) { + optional<InvalidConfigReason> invalidConfigReason = updateAtomMatchingTrackers( + config, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, + allTagIdsToMatchersMap, newAtomMatchingTrackerMap, newAtomMatchingTrackers, + replacedMatchers); + if (invalidConfigReason.has_value()) { ALOGE("updateAtomMatchingTrackers failed"); - return false; + return invalidConfigReason; } - if (!updateConditions(key, config, newAtomMatchingTrackerMap, replacedMatchers, - oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap, - newConditionTrackers, trackerToConditionMap, conditionCache, - replacedConditions)) { + invalidConfigReason = updateConditions( + key, config, newAtomMatchingTrackerMap, replacedMatchers, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, newConditionTrackers, + trackerToConditionMap, conditionCache, replacedConditions); + if (invalidConfigReason.has_value()) { ALOGE("updateConditions failed"); - return false; + return invalidConfigReason; } - if (!updateStates(config, oldStateProtoHashes, stateAtomIdMap, allStateGroupMaps, - newStateProtoHashes, replacedStates)) { + invalidConfigReason = updateStates(config, oldStateProtoHashes, stateAtomIdMap, + allStateGroupMaps, newStateProtoHashes, replacedStates); + if (invalidConfigReason.has_value()) { ALOGE("updateStates failed"); - return false; - } - if (!updateMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, - newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, - newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, - replacedStates, oldMetricProducerMap, oldMetricProducers, - newMetricProducerMap, newMetricProducers, conditionToMetricMap, - trackerToMetricMap, noReportMetricIds, activationTrackerToMetricMap, - deactivationTrackerToMetricMap, metricsWithActivation, replacedMetrics)) { + return invalidConfigReason; + } + + invalidConfigReason = updateMetrics( + key, config, timeBaseNs, currentTimeNs, pullerManager, oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers, + newConditionTrackerMap, replacedConditions, newConditionTrackers, conditionCache, + stateAtomIdMap, allStateGroupMaps, replacedStates, oldMetricProducerMap, + oldMetricProducers, newMetricProducerMap, newMetricProducers, conditionToMetricMap, + trackerToMetricMap, noReportMetricIds, activationTrackerToMetricMap, + deactivationTrackerToMetricMap, metricsWithActivation, replacedMetrics); + if (invalidConfigReason.has_value()) { ALOGE("updateMetrics failed"); - return false; + return invalidConfigReason; } - if (!updateAlerts(config, currentTimeNs, newMetricProducerMap, replacedMetrics, - oldAlertTrackerMap, oldAnomalyTrackers, anomalyAlarmMonitor, - newMetricProducers, newAlertTrackerMap, newAnomalyTrackers)) { + invalidConfigReason = updateAlerts(config, currentTimeNs, newMetricProducerMap, replacedMetrics, + oldAlertTrackerMap, oldAnomalyTrackers, anomalyAlarmMonitor, + newMetricProducers, newAlertTrackerMap, newAnomalyTrackers); + if (invalidConfigReason.has_value()) { ALOGE("updateAlerts failed"); - return false; + return invalidConfigReason; } + invalidConfigReason = initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs, + newPeriodicAlarmTrackers); // Alarms do not have any state, so we can reuse the initialization logic. - if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs, - newPeriodicAlarmTrackers)) { + if (invalidConfigReason.has_value()) { ALOGE("initAlarms failed"); - return false; + return invalidConfigReason; } - return true; + return nullopt; } } // namespace statsd diff --git a/statsd/src/metrics/parsing_utils/config_update_utils.h b/statsd/src/metrics/parsing_utils/config_update_utils.h index f311661b..3bd1e800 100644 --- a/statsd/src/metrics/parsing_utils/config_update_utils.h +++ b/statsd/src/metrics/parsing_utils/config_update_utils.h @@ -44,8 +44,8 @@ namespace statsd { // [matchersToUpdate]: vector of the update status of each matcher. The matcherIdx index will // be updated from UPDATE_UNKNOWN after this call. // [cycleTracker]: intermediate param used during recursion. -// Returns whether the function was successful or not. -bool determineMatcherUpdateStatus( +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> determineMatcherUpdateStatus( const StatsdConfig& config, const int matcherIdx, const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, @@ -62,13 +62,15 @@ bool determineMatcherUpdateStatus( // [newAtomMatchingTrackerMap]: new matcher id to index mapping // [newAtomMatchingTrackers]: stores the new AtomMatchingTrackers // [replacedMatchers]: set of matcher ids that changed and have been replaced -bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap, - std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, - std::set<int64_t>& replacedMatchers); +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> updateAtomMatchingTrackers( + const StatsdConfig& config, const sp<UidMap>& uidMap, + const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap, + std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, + std::set<int64_t>& replacedMatchers); // Recursive function to determine if a condition needs to be updated. // input: @@ -82,14 +84,14 @@ bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& ui // [conditionsToUpdate]: vector of the update status of each condition. The conditionIdx index will // be updated from UPDATE_UNKNOWN after this call. // [cycleTracker]: intermediate param used during recursion. -// Returns whether the function was successful or not. -bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx, - const std::unordered_map<int64_t, int>& oldConditionTrackerMap, - const std::vector<sp<ConditionTracker>>& oldConditionTrackers, - const std::unordered_map<int64_t, int>& newConditionTrackerMap, - const std::set<int64_t>& replacedMatchers, - std::vector<UpdateStatus>& conditionsToUpdate, - std::vector<bool>& cycleTracker); +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> determineConditionUpdateStatus( + const StatsdConfig& config, const int conditionIdx, + const std::unordered_map<int64_t, int>& oldConditionTrackerMap, + const std::vector<sp<ConditionTracker>>& oldConditionTrackers, + const std::unordered_map<int64_t, int>& newConditionTrackerMap, + const std::set<int64_t>& replacedMatchers, std::vector<UpdateStatus>& conditionsToUpdate, + std::vector<bool>& cycleTracker); // Updates ConditionTrackers // input: @@ -105,23 +107,23 @@ bool determineConditionUpdateStatus(const StatsdConfig& config, const int condit // to indices of condition trackers that use the matcher // [conditionCache]: stores the current conditions for each ConditionTracker // [replacedConditions]: set of condition ids that have changed and have been replaced -bool updateConditions(const ConfigKey& key, const StatsdConfig& config, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - const std::set<int64_t>& replacedMatchers, - const std::unordered_map<int64_t, int>& oldConditionTrackerMap, - const std::vector<sp<ConditionTracker>>& oldConditionTrackers, - std::unordered_map<int64_t, int>& newConditionTrackerMap, - std::vector<sp<ConditionTracker>>& newConditionTrackers, - std::unordered_map<int, std::vector<int>>& trackerToConditionMap, - std::vector<ConditionState>& conditionCache, - std::set<int64_t>& replacedConditions); +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> updateConditions( + const ConfigKey& key, const StatsdConfig& config, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + const std::set<int64_t>& replacedMatchers, + const std::unordered_map<int64_t, int>& oldConditionTrackerMap, + const std::vector<sp<ConditionTracker>>& oldConditionTrackers, + std::unordered_map<int64_t, int>& newConditionTrackerMap, + std::vector<sp<ConditionTracker>>& newConditionTrackers, + std::unordered_map<int, std::vector<int>>& trackerToConditionMap, + std::vector<ConditionState>& conditionCache, std::set<int64_t>& replacedConditions); -bool updateStates(const StatsdConfig& config, - const std::map<int64_t, uint64_t>& oldStateProtoHashes, - std::unordered_map<int64_t, int>& stateAtomIdMap, - std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps, - std::map<int64_t, uint64_t>& newStateProtoHashes, - std::set<int64_t>& replacedStates); +optional<InvalidConfigReason> updateStates( + const StatsdConfig& config, const std::map<int64_t, uint64_t>& oldStateProtoHashes, + std::unordered_map<int64_t, int>& stateAtomIdMap, + std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps, + std::map<int64_t, uint64_t>& newStateProtoHashes, std::set<int64_t>& replacedStates); // Function to determine the update status (preserve/replace/new) of all metrics in the config. // [config]: the input StatsdConfig @@ -133,15 +135,13 @@ bool updateStates(const StatsdConfig& config, // [replacedStates]: set of replaced state ids. metrics using these states must be replaced // output: // [metricsToUpdate]: update status of each metric. Will be changed from UPDATE_UNKNOWN -// Returns whether the function was successful or not. -bool determineAllMetricUpdateStatuses(const StatsdConfig& config, - const unordered_map<int64_t, int>& oldMetricProducerMap, - const vector<sp<MetricProducer>>& oldMetricProducers, - const unordered_map<int64_t, int>& metricToActivationMap, - const set<int64_t>& replacedMatchers, - const set<int64_t>& replacedConditions, - const set<int64_t>& replacedStates, - vector<UpdateStatus>& metricsToUpdate); +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> determineAllMetricUpdateStatuses( + const StatsdConfig& config, const unordered_map<int64_t, int>& oldMetricProducerMap, + const vector<sp<MetricProducer>>& oldMetricProducers, + const unordered_map<int64_t, int>& metricToActivationMap, + const set<int64_t>& replacedMatchers, const set<int64_t>& replacedConditions, + const set<int64_t>& replacedStates, vector<UpdateStatus>& metricsToUpdate); // Update MetricProducers. // input: @@ -162,7 +162,8 @@ bool determineAllMetricUpdateStatuses(const StatsdConfig& config, // [conditionToMetricMap]: contains the mapping from condition tracker index to // the list of MetricProducer index // [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index. -bool updateMetrics( +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> updateMetrics( const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, @@ -194,12 +195,11 @@ bool updateMetrics( // [replacedMetrics]: set of replaced metric ids. alerts using these metrics must be replaced // output: // [updateStatus]: update status of the alert. Will be changed from UPDATE_UNKNOWN -// Returns whether the function was successful or not. -bool determineAlertUpdateStatus(const Alert& alert, - const std::unordered_map<int64_t, int>& oldAlertTrackerMap, - const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers, - const std::set<int64_t>& replacedMetrics, - UpdateStatus& updateStatus); +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> determineAlertUpdateStatus( + const Alert& alert, const std::unordered_map<int64_t, int>& oldAlertTrackerMap, + const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers, + const std::set<int64_t>& replacedMetrics, UpdateStatus& updateStatus); // Update MetricProducers. // input: @@ -217,50 +217,51 @@ bool determineAlertUpdateStatus(const Alert& alert, // output: // [newAlertTrackerMap]: mapping of alert id to index in the new config // [newAnomalyTrackers]: contains the list of sp to the AnomalyTrackers created. -bool updateAlerts(const StatsdConfig& config, const int64_t currentTimeNs, - const std::unordered_map<int64_t, int>& metricProducerMap, - const std::set<int64_t>& replacedMetrics, - const std::unordered_map<int64_t, int>& oldAlertTrackerMap, - const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - std::vector<sp<MetricProducer>>& allMetricProducers, - std::unordered_map<int64_t, int>& newAlertTrackerMap, - std::vector<sp<AnomalyTracker>>& newAnomalyTrackers); +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> updateAlerts( + const StatsdConfig& config, const int64_t currentTimeNs, + const std::unordered_map<int64_t, int>& metricProducerMap, + const std::set<int64_t>& replacedMetrics, + const std::unordered_map<int64_t, int>& oldAlertTrackerMap, + const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers, + const sp<AlarmMonitor>& anomalyAlarmMonitor, + std::vector<sp<MetricProducer>>& allMetricProducers, + std::unordered_map<int64_t, int>& newAlertTrackerMap, + std::vector<sp<AnomalyTracker>>& newAnomalyTrackers); // Updates the existing MetricsManager from a new StatsdConfig. // Parameters are the members of MetricsManager. See MetricsManager for declaration. -bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, - const sp<StatsPullerManager>& pullerManager, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, - const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const std::vector<sp<ConditionTracker>>& oldConditionTrackers, - const std::unordered_map<int64_t, int>& oldConditionTrackerMap, - const std::vector<sp<MetricProducer>>& oldMetricProducers, - const std::unordered_map<int64_t, int>& oldMetricProducerMap, - const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers, - const std::unordered_map<int64_t, int>& oldAlertTrackerMap, - const std::map<int64_t, uint64_t>& oldStateProtoHashes, - std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap, - std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, - std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - std::vector<sp<ConditionTracker>>& newConditionTrackers, - std::unordered_map<int64_t, int>& newConditionTrackerMap, - std::vector<sp<MetricProducer>>& newMetricProducers, - std::unordered_map<int64_t, int>& newMetricProducerMap, - std::vector<sp<AnomalyTracker>>& newAlertTrackers, - std::unordered_map<int64_t, int>& newAlertTrackerMap, - std::vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& trackerToConditionMap, - std::unordered_map<int, std::vector<int>>& activationTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationTrackerToMetricMap, - std::vector<int>& metricsWithActivation, - std::map<int64_t, uint64_t>& newStateProtoHashes, - std::set<int64_t>& noReportMetricIds); +optional<InvalidConfigReason> updateStatsdConfig( + const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, + const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor, + const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, + const int64_t currentTimeNs, + const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const std::vector<sp<ConditionTracker>>& oldConditionTrackers, + const std::unordered_map<int64_t, int>& oldConditionTrackerMap, + const std::vector<sp<MetricProducer>>& oldMetricProducers, + const std::unordered_map<int64_t, int>& oldMetricProducerMap, + const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers, + const std::unordered_map<int64_t, int>& oldAlertTrackerMap, + const std::map<int64_t, uint64_t>& oldStateProtoHashes, + std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap, + std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, + std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + std::vector<sp<ConditionTracker>>& newConditionTrackers, + std::unordered_map<int64_t, int>& newConditionTrackerMap, + std::vector<sp<MetricProducer>>& newMetricProducers, + std::unordered_map<int64_t, int>& newMetricProducerMap, + std::vector<sp<AnomalyTracker>>& newAlertTrackers, + std::unordered_map<int64_t, int>& newAlertTrackerMap, + std::vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::unordered_map<int, std::vector<int>>& trackerToConditionMap, + std::unordered_map<int, std::vector<int>>& activationTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationTrackerToMetricMap, + std::vector<int>& metricsWithActivation, std::map<int64_t, uint64_t>& newStateProtoHashes, + std::set<int64_t>& noReportMetricIds); } // namespace statsd } // namespace os diff --git a/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp index 915c4aef..c44c8186 100644 --- a/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp +++ b/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp @@ -65,32 +65,42 @@ bool hasLeafNode(const FieldMatcher& matcher) { } // namespace -sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index, - const sp<UidMap>& uidMap) { +sp<AtomMatchingTracker> createAtomMatchingTracker( + const AtomMatcher& logMatcher, const int index, const sp<UidMap>& uidMap, + optional<InvalidConfigReason>& invalidConfigReason) { string serializedMatcher; if (!logMatcher.SerializeToString(&serializedMatcher)) { ALOGE("Unable to serialize matcher %lld", (long long)logMatcher.id()); + invalidConfigReason = createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_SERIALIZATION_FAILED, logMatcher.id()); return nullptr; } uint64_t protoHash = Hash64(serializedMatcher); switch (logMatcher.contents_case()) { - case AtomMatcher::ContentsCase::kSimpleAtomMatcher: - return new SimpleAtomMatchingTracker(logMatcher.id(), index, protoHash, - logMatcher.simple_atom_matcher(), uidMap); + case AtomMatcher::ContentsCase::kSimpleAtomMatcher: { + sp<AtomMatchingTracker> simpleAtomMatcher = new SimpleAtomMatchingTracker( + logMatcher.id(), index, protoHash, logMatcher.simple_atom_matcher(), uidMap); + return simpleAtomMatcher; + } case AtomMatcher::ContentsCase::kCombination: return new CombinationAtomMatchingTracker(logMatcher.id(), index, protoHash); default: ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id()); + invalidConfigReason = createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_MALFORMED_CONTENTS_CASE, logMatcher.id()); return nullptr; } } sp<ConditionTracker> createConditionTracker( const ConfigKey& key, const Predicate& predicate, const int index, - const unordered_map<int64_t, int>& atomMatchingTrackerMap) { + const unordered_map<int64_t, int>& atomMatchingTrackerMap, + optional<InvalidConfigReason>& invalidConfigReason) { string serializedPredicate; if (!predicate.SerializeToString(&serializedPredicate)) { ALOGE("Unable to serialize predicate %lld", (long long)predicate.id()); + invalidConfigReason = createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_SERIALIZATION_FAILED, predicate.id()); return nullptr; } uint64_t protoHash = Hash64(serializedPredicate); @@ -104,17 +114,19 @@ sp<ConditionTracker> createConditionTracker( } default: ALOGE("Predicate \"%lld\" malformed", (long long)predicate.id()); + invalidConfigReason = createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_MALFORMED_CONTENTS_CASE, predicate.id()); return nullptr; } } -bool getMetricProtoHash(const StatsdConfig& config, const MessageLite& metric, const int64_t id, - const unordered_map<int64_t, int>& metricToActivationMap, - uint64_t& metricHash) { +optional<InvalidConfigReason> getMetricProtoHash( + const StatsdConfig& config, const MessageLite& metric, const int64_t id, + const unordered_map<int64_t, int>& metricToActivationMap, uint64_t& metricHash) { string serializedMetric; if (!metric.SerializeToString(&serializedMetric)) { ALOGE("Unable to serialize metric %lld", (long long)id); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_SERIALIZATION_FAILED, id); } metricHash = Hash64(serializedMetric); @@ -125,38 +137,41 @@ bool getMetricProtoHash(const StatsdConfig& config, const MessageLite& metric, c const MetricActivation& activation = config.metric_activation(metricActivationIt->second); if (!activation.SerializeToString(&serializedActivation)) { ALOGE("Unable to serialize metric activation for metric %lld", (long long)id); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_ACTIVATION_SERIALIZATION_FAILED, + id); } metricHash = Hash64(to_string(metricHash).append(to_string(Hash64(serializedActivation)))); } - return true; + return nullopt; } -bool handleMetricWithAtomMatchingTrackers( - const int64_t matcherId, const int metricIndex, const bool enforceOneAtom, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, +optional<InvalidConfigReason> handleMetricWithAtomMatchingTrackers( + const int64_t matcherId, const int64_t metricId, const int metricIndex, + const bool enforceOneAtom, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const unordered_map<int64_t, int>& atomMatchingTrackerMap, unordered_map<int, vector<int>>& trackerToMetricMap, int& logTrackerIndex) { auto logTrackerIt = atomMatchingTrackerMap.find(matcherId); if (logTrackerIt == atomMatchingTrackerMap.end()) { ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)matcherId); - return false; + return createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_METRIC_MATCHER_NOT_FOUND, + metricId, matcherId); } if (enforceOneAtom && allAtomMatchingTrackers[logTrackerIt->second]->getAtomIds().size() > 1) { ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, " "the \"what\" can only be about one atom type. trigger_event matchers can also only " "be about one atom type.", (long long)matcherId); - return false; + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_METRIC_MATCHER_MORE_THAN_ONE_ATOM, metricId, matcherId); } logTrackerIndex = logTrackerIt->second; auto& metric_list = trackerToMetricMap[logTrackerIndex]; metric_list.push_back(metricIndex); - return true; + return nullopt; } -bool handleMetricWithConditions( - const int64_t condition, const int metricIndex, +optional<InvalidConfigReason> handleMetricWithConditions( + const int64_t condition, const int64_t metricId, const int metricIndex, const unordered_map<int64_t, int>& conditionTrackerMap, const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>& links, @@ -165,14 +180,16 @@ bool handleMetricWithConditions( auto condition_it = conditionTrackerMap.find(condition); if (condition_it == conditionTrackerMap.end()) { ALOGW("cannot find Predicate \"%lld\" in the config", (long long)condition); - return false; + return createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_METRIC_CONDITION_NOT_FOUND, metricId, condition); } - for (const auto& link : links) { auto it = conditionTrackerMap.find(link.condition()); if (it == conditionTrackerMap.end()) { ALOGW("cannot find Predicate \"%lld\" in the config", (long long)link.condition()); - return false; + return createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_METRIC_CONDITION_LINK_NOT_FOUND, metricId, + link.condition()); } } conditionIndex = condition_it->second; @@ -180,7 +197,7 @@ bool handleMetricWithConditions( // will create new vector if not exist before. auto& metricList = conditionToMetricMap[condition_it->second]; metricList.push_back(metricIndex); - return true; + return nullopt; } // Initializes state data structures for a metric. @@ -195,8 +212,9 @@ bool handleMetricWithConditions( // [stateGroupMap]: this map should contain the mapping from states ids and state // values to state group ids for all states that this metric // is interested in -bool handleMetricWithStates( - const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds, +optional<InvalidConfigReason> handleMetricWithStates( + const StatsdConfig& config, const int64_t metricId, + const ::google::protobuf::RepeatedField<int64_t>& stateIds, const unordered_map<int64_t, int>& stateAtomIdMap, const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, vector<int>& slicedStateAtoms, @@ -205,7 +223,8 @@ bool handleMetricWithStates( auto it = stateAtomIdMap.find(stateId); if (it == stateAtomIdMap.end()) { ALOGW("cannot find State %" PRId64 " in the config", stateId); - return false; + return createInvalidConfigReasonWithState(INVALID_CONFIG_REASON_METRIC_STATE_NOT_FOUND, + metricId, stateId); } int atomId = it->second; slicedStateAtoms.push_back(atomId); @@ -215,22 +234,64 @@ bool handleMetricWithStates( stateGroupMap[atomId] = stateIt->second; } } - return true; + return nullopt; } -bool handleMetricWithStateLink(const FieldMatcher& stateMatcher, - const vector<Matcher>& dimensionsInWhat) { +optional<InvalidConfigReason> handleMetricWithStateLink(const int64_t metricId, + const FieldMatcher& stateMatcher, + const vector<Matcher>& dimensionsInWhat) { vector<Matcher> stateMatchers; translateFieldMatcher(stateMatcher, &stateMatchers); + if (!subsetDimensions(stateMatchers, dimensionsInWhat)) { + return InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_STATELINKS_NOT_SUBSET_DIM_IN_WHAT, + metricId); + } + return nullopt; +} - return subsetDimensions(stateMatchers, dimensionsInWhat); +optional<InvalidConfigReason> handleMetricWithSampling( + const int64_t metricId, const DimensionalSamplingInfo& dimSamplingInfo, + const vector<Matcher>& dimensionsInWhat, SamplingInfo& samplingInfo) { + if (!dimSamplingInfo.has_sampled_what_field()) { + ALOGE("metric DimensionalSamplingInfo missing sampledWhatField"); + return InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_DIMENSIONAL_SAMPLING_INFO_MISSING_SAMPLED_FIELD, + metricId); + } + + if (dimSamplingInfo.shard_count() <= 1) { + ALOGE("metric shardCount must be > 1"); + return InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_DIMENSIONAL_SAMPLING_INFO_INCORRECT_SHARD_COUNT, + metricId); + } + samplingInfo.shardCount = dimSamplingInfo.shard_count(); + + if (HasPositionALL(dimSamplingInfo.sampled_what_field()) || + HasPositionANY(dimSamplingInfo.sampled_what_field())) { + ALOGE("metric has repeated field with position ALL or ANY as the sampled dimension"); + return InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_SAMPLED_FIELD_INCORRECT_SIZE, + metricId); + } + + translateFieldMatcher(dimSamplingInfo.sampled_what_field(), &samplingInfo.sampledWhatFields); + if (samplingInfo.sampledWhatFields.size() != 1) { + ALOGE("metric has incorrect number of sampled dimension fields"); + return InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_SAMPLED_FIELD_INCORRECT_SIZE, + metricId); + } + if (!subsetDimensions(samplingInfo.sampledWhatFields, dimensionsInWhat)) { + return InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_SAMPLED_FIELDS_NOT_SUBSET_DIM_IN_WHAT, metricId); + } + return nullopt; } // Validates a metricActivation and populates state. // EventActivationMap and EventDeactivationMap are supplied to a MetricProducer // to provide the producer with state about its activators and deactivators. // Returns false if there are errors. -bool handleMetricActivation( +optional<InvalidConfigReason> handleMetricActivation( const StatsdConfig& config, const int64_t metricId, const int metricIndex, const unordered_map<int64_t, int>& metricToActivationMap, const unordered_map<int64_t, int>& atomMatchingTrackerMap, @@ -242,7 +303,7 @@ bool handleMetricActivation( // Check if metric has an associated activation auto itr = metricToActivationMap.find(metricId); if (itr == metricToActivationMap.end()) { - return true; + return nullopt; } int activationIndex = itr->second; @@ -254,7 +315,9 @@ bool handleMetricActivation( auto itr = atomMatchingTrackerMap.find(activation.atom_matcher_id()); if (itr == atomMatchingTrackerMap.end()) { ALOGE("Atom matcher not found for event activation."); - return false; + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_METRIC_ACTIVATION_MATCHER_NOT_FOUND, metricId, + activation.atom_matcher_id()); } ActivationType activationType = (activation.has_activation_type()) @@ -271,7 +334,9 @@ bool handleMetricActivation( itr = atomMatchingTrackerMap.find(activation.deactivation_atom_matcher_id()); if (itr == atomMatchingTrackerMap.end()) { ALOGE("Atom matcher not found for event deactivation."); - return false; + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_METRIC_DEACTIVATION_MATCHER_NOT_FOUND, metricId, + activation.deactivation_atom_matcher_id()); } int deactivationAtomMatcherIndex = itr->second; deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex); @@ -280,13 +345,13 @@ bool handleMetricActivation( } metricsWithActivation.push_back(metricIndex); - return true; + return nullopt; } // Validates a metricActivation and populates state. // Fills the new event activation/deactivation maps, preserving the existing activations // Returns false if there are errors. -bool handleMetricActivationOnConfigUpdate( +optional<InvalidConfigReason> handleMetricActivationOnConfigUpdate( const StatsdConfig& config, const int64_t metricId, const int metricIndex, const unordered_map<int64_t, int>& metricToActivationMap, const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, @@ -300,7 +365,7 @@ bool handleMetricActivationOnConfigUpdate( // Check if metric has an associated activation. const auto& itr = metricToActivationMap.find(metricId); if (itr == metricToActivationMap.end()) { - return true; + return nullopt; } int activationIndex = itr->second; @@ -312,7 +377,9 @@ bool handleMetricActivationOnConfigUpdate( const auto& newActivationIt = newAtomMatchingTrackerMap.find(activationMatcherId); if (newActivationIt == newAtomMatchingTrackerMap.end()) { ALOGE("Atom matcher not found in new config for event activation."); - return false; + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_METRIC_ACTIVATION_MATCHER_NOT_FOUND_NEW, metricId, + activationMatcherId); } int newActivationMatcherIndex = newActivationIt->second; @@ -320,13 +387,17 @@ bool handleMetricActivationOnConfigUpdate( const auto& oldActivationIt = oldAtomMatchingTrackerMap.find(activationMatcherId); if (oldActivationIt == oldAtomMatchingTrackerMap.end()) { ALOGE("Atom matcher not found in existing config for event activation."); - return false; + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_METRIC_ACTIVATION_MATCHER_NOT_FOUND_EXISTING, metricId, + activationMatcherId); } int oldActivationMatcherIndex = oldActivationIt->second; const auto& oldEventActivationIt = oldEventActivationMap.find(oldActivationMatcherIndex); if (oldEventActivationIt == oldEventActivationMap.end()) { ALOGE("Could not find existing event activation to update"); - return false; + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_METRIC_ACTIVATION_NOT_FOUND_EXISTING, metricId, + activationMatcherId); } newEventActivationMap.emplace(newActivationMatcherIndex, oldEventActivationIt->second); activationAtomTrackerToMetricMap[newActivationMatcherIndex].push_back(metricIndex); @@ -337,7 +408,9 @@ bool handleMetricActivationOnConfigUpdate( const auto& newDeactivationIt = newAtomMatchingTrackerMap.find(deactivationMatcherId); if (newDeactivationIt == newAtomMatchingTrackerMap.end()) { ALOGE("Deactivation atom matcher not found in new config for event activation."); - return false; + return createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_METRIC_DEACTIVATION_MATCHER_NOT_FOUND_NEW, metricId, + deactivationMatcherId); } int newDeactivationMatcherIndex = newDeactivationIt->second; newEventDeactivationMap[newDeactivationMatcherIndex].push_back( @@ -347,7 +420,7 @@ bool handleMetricActivationOnConfigUpdate( } metricsWithActivation.push_back(metricIndex); - return true; + return nullopt; } optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata( @@ -365,29 +438,34 @@ optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata( unordered_map<int, vector<int>>& conditionToMetricMap, unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { + vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason) { if (!metric.has_id() || !metric.has_what()) { ALOGE("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id()); + invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_MISSING_ID_OR_WHAT, metric.id()); return nullopt; } int trackerIndex; - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, trackerIndex)) { + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + metric.what(), metric.id(), metricIndex, metric.has_dimensions_in_what(), + allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, trackerIndex); + if (invalidConfigReason.has_value()) { return nullopt; } int conditionIndex = -1; if (metric.has_condition()) { - if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap)) { + invalidConfigReason = handleMetricWithConditions( + metric.condition(), metric.id(), metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, conditionIndex, conditionToMetricMap); + if (invalidConfigReason.has_value()) { return nullopt; } } else { if (metric.links_size() > 0) { ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_CONDITIONLINK_NO_CONDITION, metric.id()); return nullopt; } } @@ -395,28 +473,47 @@ optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata( std::vector<int> slicedStateAtoms; unordered_map<int, unordered_map<int, int64_t>> stateGroupMap; if (metric.slice_by_state_size() > 0) { - if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, - allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { + invalidConfigReason = + handleMetricWithStates(config, metric.id(), metric.slice_by_state(), stateAtomIdMap, + allStateGroupMaps, slicedStateAtoms, stateGroupMap); + if (invalidConfigReason.has_value()) { return nullopt; } } else { if (metric.state_link_size() > 0) { ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state"); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_STATELINK_NO_STATE, metric.id()); + return nullopt; + } + } + + // Check that all metric state links are a subset of dimensions_in_what fields. + std::vector<Matcher> dimensionsInWhat; + translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); + for (const auto& stateLink : metric.state_link()) { + invalidConfigReason = handleMetricWithStateLink(metric.id(), stateLink.fields_in_what(), + dimensionsInWhat); + if (invalidConfigReason.has_value()) { + ALOGW("CountMetric's MetricStateLinks must be a subset of dimensions in what"); return nullopt; } } unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, - atomMatchingTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, - eventActivationMap, eventDeactivationMap)) { + invalidConfigReason = handleMetricActivation( + config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap); + if (invalidConfigReason.has_value()) { return nullopt; } uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { + invalidConfigReason = + getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash); + if (invalidConfigReason.has_value()) { return nullopt; } @@ -424,12 +521,27 @@ optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata( (metric.threshold().value_comparison_case() == UploadThreshold::kLtFloat || metric.threshold().value_comparison_case() == UploadThreshold::kGtFloat)) { ALOGW("Count metric incorrect upload threshold type or no type used"); + invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_BAD_THRESHOLD, metric.id()); return nullopt; } - return {new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, + sp<MetricProducer> metricProducer = + new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, metricHash, timeBaseNs, currentTimeNs, eventActivationMap, - eventDeactivationMap, slicedStateAtoms, stateGroupMap)}; + eventDeactivationMap, slicedStateAtoms, stateGroupMap); + + SamplingInfo samplingInfo; + if (metric.has_dimensional_sampling_info()) { + invalidConfigReason = handleMetricWithSampling( + metric.id(), metric.dimensional_sampling_info(), dimensionsInWhat, samplingInfo); + if (invalidConfigReason.has_value()) { + return nullopt; + } + metricProducer->setSamplingInfo(samplingInfo); + } + + return metricProducer; } optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( @@ -447,15 +559,19 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( unordered_map<int, vector<int>>& conditionToMetricMap, unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { + vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason) { if (!metric.has_id() || !metric.has_what()) { ALOGE("cannot find metric id or \"what\" in DurationMetric \"%lld\"", (long long)metric.id()); + invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_MISSING_ID_OR_WHAT, metric.id()); return nullopt; } const auto& what_it = conditionTrackerMap.find(metric.what()); if (what_it == conditionTrackerMap.end()) { ALOGE("DurationMetric's \"what\" is not present in the condition trackers"); + invalidConfigReason = createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_DURATION_METRIC_WHAT_NOT_FOUND, metric.id(), metric.what()); return nullopt; } @@ -463,6 +579,8 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( const Predicate& durationWhat = config.predicate(whatIndex); if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) { ALOGE("DurationMetric's \"what\" must be a simple condition"); + invalidConfigReason = createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_DURATION_METRIC_WHAT_NOT_SIMPLE, metric.id(), metric.what()); return nullopt; } @@ -470,40 +588,52 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( bool nesting = simplePredicate.count_nesting(); int startIndex = -1, stopIndex = -1, stopAllIndex = -1; - if (!simplePredicate.has_start() || - !handleMetricWithAtomMatchingTrackers( - simplePredicate.start(), metricIndex, metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, startIndex)) { + if (!simplePredicate.has_start()) { ALOGE("Duration metrics must specify a valid start event matcher"); + invalidConfigReason = createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_DURATION_METRIC_MISSING_START, metric.id(), metric.what()); return nullopt; } - - if (simplePredicate.has_stop() && - !handleMetricWithAtomMatchingTrackers( - simplePredicate.stop(), metricIndex, metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, stopIndex)) { + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + simplePredicate.start(), metric.id(), metricIndex, metric.has_dimensions_in_what(), + allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, startIndex); + if (invalidConfigReason.has_value()) { return nullopt; } - if (simplePredicate.has_stop_all() && - !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, stopAllIndex)) { - return nullopt; + if (simplePredicate.has_stop()) { + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + simplePredicate.stop(), metric.id(), metricIndex, metric.has_dimensions_in_what(), + allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, stopIndex); + if (invalidConfigReason.has_value()) { + return nullopt; + } + } + + if (simplePredicate.has_stop_all()) { + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + simplePredicate.stop_all(), metric.id(), metricIndex, + metric.has_dimensions_in_what(), allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, stopAllIndex); + if (invalidConfigReason.has_value()) { + return nullopt; + } } FieldMatcher internalDimensions = simplePredicate.dimensions(); int conditionIndex = -1; if (metric.has_condition()) { - if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap)) { + invalidConfigReason = handleMetricWithConditions( + metric.condition(), metric.id(), metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, conditionIndex, conditionToMetricMap); + if (invalidConfigReason.has_value()) { return nullopt; } } else if (metric.links_size() > 0) { ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_CONDITIONLINK_NO_CONDITION, metric.id()); return nullopt; } @@ -512,14 +642,21 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( if (metric.slice_by_state_size() > 0) { if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) { ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state"); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_DURATION_METRIC_MAX_SPARSE_HAS_SLICE_BY_STATE, + metric.id()); return nullopt; } - if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, - allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { + invalidConfigReason = + handleMetricWithStates(config, metric.id(), metric.slice_by_state(), stateAtomIdMap, + allStateGroupMaps, slicedStateAtoms, stateGroupMap); + if (invalidConfigReason.has_value()) { return nullopt; } } else if (metric.state_link_size() > 0) { ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state"); + invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_STATELINK_NO_STATE, metric.id()); return nullopt; } @@ -527,7 +664,9 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( std::vector<Matcher> dimensionsInWhat; translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); for (const auto& stateLink : metric.state_link()) { - if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) { + invalidConfigReason = handleMetricWithStateLink(metric.id(), stateLink.fields_in_what(), + dimensionsInWhat); + if (invalidConfigReason.has_value()) { ALOGW("DurationMetric's MetricStateLinks must be a subset of dimensions in what"); return nullopt; } @@ -535,15 +674,18 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, - atomMatchingTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, - eventActivationMap, eventDeactivationMap)) { + invalidConfigReason = handleMetricActivation( + config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap); + if (invalidConfigReason.has_value()) { return nullopt; } uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { + invalidConfigReason = + getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash); + if (invalidConfigReason.has_value()) { return nullopt; } @@ -556,20 +698,35 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( break; default: ALOGE("Duration metric incorrect upload threshold type or no type used"); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_BAD_THRESHOLD, metric.id()); return nullopt; - break; } } - sp<MetricProducer> producer = new DurationMetricProducer( + sp<MetricProducer> metricProducer = new DurationMetricProducer( key, metric, conditionIndex, initialConditionCache, whatIndex, startIndex, stopIndex, stopAllIndex, nesting, wizard, metricHash, internalDimensions, timeBaseNs, currentTimeNs, eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap); - if (!producer->isValid()) { + if (!metricProducer->isValid()) { + // TODO: Remove once invalidConfigReason is added to the DurationMetricProducer constructor + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_DURATION_METRIC_PRODUCER_INVALID, metric.id()); return nullopt; } - return {producer}; + + SamplingInfo samplingInfo; + if (metric.has_dimensional_sampling_info()) { + invalidConfigReason = handleMetricWithSampling( + metric.id(), metric.dimensional_sampling_info(), dimensionsInWhat, samplingInfo); + if (invalidConfigReason.has_value()) { + return nullopt; + } + metricProducer->setSamplingInfo(samplingInfo); + } + + return metricProducer; } optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata( @@ -585,42 +742,50 @@ optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata( unordered_map<int, vector<int>>& conditionToMetricMap, unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { + vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason) { if (!metric.has_id() || !metric.has_what()) { ALOGE("cannot find the metric name or what in config"); + invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_MISSING_ID_OR_WHAT, metric.id()); return nullopt; } int trackerIndex; - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false, - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, trackerIndex)) { + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + metric.what(), metric.id(), metricIndex, false, allAtomMatchingTrackers, + atomMatchingTrackerMap, trackerToMetricMap, trackerIndex); + if (invalidConfigReason.has_value()) { return nullopt; } int conditionIndex = -1; if (metric.has_condition()) { - if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap)) { + invalidConfigReason = handleMetricWithConditions( + metric.condition(), metric.id(), metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, conditionIndex, conditionToMetricMap); + if (invalidConfigReason.has_value()) { return nullopt; } } else { if (metric.links_size() > 0) { ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_CONDITIONLINK_NO_CONDITION, metric.id()); return nullopt; } } unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - bool success = handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, - atomMatchingTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, - eventActivationMap, eventDeactivationMap); - if (!success) return nullptr; + invalidConfigReason = handleMetricActivation( + config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap); + if (invalidConfigReason.has_value()) return nullptr; uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { + invalidConfigReason = + getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash); + if (invalidConfigReason.has_value()) { return nullopt; } @@ -646,32 +811,41 @@ optional<sp<MetricProducer>> createNumericValueMetricProducerAndUpdateMetadata( unordered_map<int, vector<int>>& conditionToMetricMap, unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { + vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason) { if (!metric.has_id() || !metric.has_what()) { ALOGE("cannot find metric id or \"what\" in ValueMetric \"%lld\"", (long long)metric.id()); + invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_MISSING_ID_OR_WHAT, metric.id()); return nullopt; } if (!metric.has_value_field()) { ALOGE("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id()); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_VALUE_METRIC_MISSING_VALUE_FIELD, metric.id()); return nullopt; } if (HasPositionALL(metric.value_field())) { ALOGE("value field with position ALL is not supported. ValueMetric \"%lld\"", (long long)metric.id()); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_VALUE_METRIC_VALUE_FIELD_HAS_POSITION_ALL, metric.id()); return nullopt; } std::vector<Matcher> fieldMatchers; translateFieldMatcher(metric.value_field(), &fieldMatchers); if (fieldMatchers.size() < 1) { ALOGE("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id()); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_VALUE_METRIC_HAS_INCORRECT_VALUE_FIELD, metric.id()); return nullopt; } int trackerIndex; - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, - /*enforceOneAtom=*/true, allAtomMatchingTrackers, - atomMatchingTrackerMap, trackerToMetricMap, - trackerIndex)) { + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + metric.what(), metric.id(), metricIndex, + /*enforceOneAtom=*/true, allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, trackerIndex); + if (invalidConfigReason.has_value()) { return nullopt; } @@ -681,25 +855,32 @@ optional<sp<MetricProducer>> createNumericValueMetricProducerAndUpdateMetadata( int conditionIndex = -1; if (metric.has_condition()) { - if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap)) { + invalidConfigReason = handleMetricWithConditions( + metric.condition(), metric.id(), metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, conditionIndex, conditionToMetricMap); + if (invalidConfigReason.has_value()) { return nullopt; } } else if (metric.links_size() > 0) { ALOGE("metrics has a MetricConditionLink but doesn't have a condition"); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_CONDITIONLINK_NO_CONDITION, metric.id()); return nullopt; } std::vector<int> slicedStateAtoms; unordered_map<int, unordered_map<int, int64_t>> stateGroupMap; if (metric.slice_by_state_size() > 0) { - if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, - allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { + invalidConfigReason = + handleMetricWithStates(config, metric.id(), metric.slice_by_state(), stateAtomIdMap, + allStateGroupMaps, slicedStateAtoms, stateGroupMap); + if (invalidConfigReason.has_value()) { return nullopt; } } else if (metric.state_link_size() > 0) { ALOGE("ValueMetric has a MetricStateLink but doesn't have a sliced state"); + invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_STATELINK_NO_STATE, metric.id()); return nullopt; } @@ -707,7 +888,9 @@ optional<sp<MetricProducer>> createNumericValueMetricProducerAndUpdateMetadata( std::vector<Matcher> dimensionsInWhat; translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); for (const auto& stateLink : metric.state_link()) { - if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) { + invalidConfigReason = handleMetricWithStateLink(metric.id(), stateLink.fields_in_what(), + dimensionsInWhat); + if (invalidConfigReason.has_value()) { ALOGW("ValueMetric's MetricStateLinks must be a subset of the dimensions in what"); return nullopt; } @@ -715,15 +898,18 @@ optional<sp<MetricProducer>> createNumericValueMetricProducerAndUpdateMetadata( unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, - atomMatchingTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, - eventActivationMap, eventDeactivationMap)) { + invalidConfigReason = handleMetricActivation( + config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap); + if (invalidConfigReason.has_value()) { return nullopt; } uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { + invalidConfigReason = + getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash); + if (invalidConfigReason.has_value()) { return nullopt; } @@ -744,7 +930,7 @@ optional<sp<MetricProducer>> createNumericValueMetricProducerAndUpdateMetadata( ? optional<int64_t>(metric.condition_correction_threshold_nanos()) : nullopt; - return new NumericValueMetricProducer( + sp<MetricProducer> metricProducer = new NumericValueMetricProducer( key, metric, metricHash, {pullTagId, pullerManager}, {timeBaseNs, currentTimeNs, bucketSizeNs, metric.min_bucket_size_nanos(), conditionCorrectionThresholdNs, getAppUpgradeBucketSplit(metric)}, @@ -753,6 +939,18 @@ optional<sp<MetricProducer>> createNumericValueMetricProducerAndUpdateMetadata( {conditionIndex, metric.links(), initialConditionCache, wizard}, {metric.state_link(), slicedStateAtoms, stateGroupMap}, {eventActivationMap, eventDeactivationMap}, {dimensionSoftLimit, dimensionHardLimit}); + + SamplingInfo samplingInfo; + if (metric.has_dimensional_sampling_info()) { + invalidConfigReason = handleMetricWithSampling( + metric.id(), metric.dimensional_sampling_info(), dimensionsInWhat, samplingInfo); + if (invalidConfigReason.has_value()) { + return nullopt; + } + metricProducer->setSamplingInfo(samplingInfo); + } + + return metricProducer; } optional<sp<MetricProducer>> createKllMetricProducerAndUpdateMetadata( @@ -772,56 +970,72 @@ optional<sp<MetricProducer>> createKllMetricProducerAndUpdateMetadata( unordered_map<int, vector<int>>& conditionToMetricMap, unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { + vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason) { if (!metric.has_id() || !metric.has_what()) { ALOGE("cannot find metric id or \"what\" in KllMetric \"%lld\"", (long long)metric.id()); + invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_MISSING_ID_OR_WHAT, metric.id()); return nullopt; } if (!metric.has_kll_field()) { ALOGE("cannot find \"kll_field\" in KllMetric \"%lld\"", (long long)metric.id()); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_KLL_METRIC_MISSING_KLL_FIELD, metric.id()); return nullopt; } if (HasPositionALL(metric.kll_field())) { ALOGE("kll field with position ALL is not supported. KllMetric \"%lld\"", (long long)metric.id()); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_KLL_METRIC_KLL_FIELD_HAS_POSITION_ALL, metric.id()); return nullopt; } std::vector<Matcher> fieldMatchers; translateFieldMatcher(metric.kll_field(), &fieldMatchers); if (fieldMatchers.empty()) { ALOGE("incorrect \"kll_field\" in KllMetric \"%lld\"", (long long)metric.id()); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_KLL_METRIC_HAS_INCORRECT_KLL_FIELD, metric.id()); return nullopt; } int trackerIndex; - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, - /*enforceOneAtom=*/true, allAtomMatchingTrackers, - atomMatchingTrackerMap, trackerToMetricMap, - trackerIndex)) { + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + metric.what(), metric.id(), metricIndex, + /*enforceOneAtom=*/true, allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, trackerIndex); + if (invalidConfigReason.has_value()) { return nullopt; } int conditionIndex = -1; if (metric.has_condition()) { - if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap)) { + invalidConfigReason = handleMetricWithConditions( + metric.condition(), metric.id(), metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, conditionIndex, conditionToMetricMap); + if (invalidConfigReason.has_value()) { return nullopt; } } else if (metric.links_size() > 0) { ALOGE("metrics has a MetricConditionLink but doesn't have a condition"); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_CONDITIONLINK_NO_CONDITION, metric.id()); return nullopt; } std::vector<int> slicedStateAtoms; unordered_map<int, unordered_map<int, int64_t>> stateGroupMap; if (metric.slice_by_state_size() > 0) { - if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, - allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { + invalidConfigReason = + handleMetricWithStates(config, metric.id(), metric.slice_by_state(), stateAtomIdMap, + allStateGroupMaps, slicedStateAtoms, stateGroupMap); + if (invalidConfigReason.has_value()) { return nullopt; } } else if (metric.state_link_size() > 0) { ALOGE("KllMetric has a MetricStateLink but doesn't have a sliced state"); + invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_STATELINK_NO_STATE, metric.id()); return nullopt; } @@ -829,7 +1043,9 @@ optional<sp<MetricProducer>> createKllMetricProducerAndUpdateMetadata( std::vector<Matcher> dimensionsInWhat; translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); for (const auto& stateLink : metric.state_link()) { - if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) { + invalidConfigReason = handleMetricWithStateLink(metric.id(), stateLink.fields_in_what(), + dimensionsInWhat); + if (invalidConfigReason.has_value()) { ALOGW("KllMetric's MetricStateLinks must be a subset of the dimensions in what"); return nullopt; } @@ -837,15 +1053,18 @@ optional<sp<MetricProducer>> createKllMetricProducerAndUpdateMetadata( unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, - atomMatchingTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, - eventActivationMap, eventDeactivationMap)) { + invalidConfigReason = handleMetricActivation( + config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap); + if (invalidConfigReason.has_value()) { return nullopt; } uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { + invalidConfigReason = + getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash); + if (invalidConfigReason.has_value()) { return nullopt; } @@ -862,7 +1081,7 @@ optional<sp<MetricProducer>> createKllMetricProducerAndUpdateMetadata( const auto [dimensionSoftLimit, dimensionHardLimit] = StatsdStats::getAtomDimensionKeySizeLimits(atomTagId); - return new KllMetricProducer( + sp<MetricProducer> metricProducer = new KllMetricProducer( key, metric, metricHash, {/*pullTagId=*/-1, pullerManager}, {timeBaseNs, currentTimeNs, bucketSizeNs, metric.min_bucket_size_nanos(), /*conditionCorrectionThresholdNs=*/nullopt, getAppUpgradeBucketSplit(metric)}, @@ -871,6 +1090,18 @@ optional<sp<MetricProducer>> createKllMetricProducerAndUpdateMetadata( {conditionIndex, metric.links(), initialConditionCache, wizard}, {metric.state_link(), slicedStateAtoms, stateGroupMap}, {eventActivationMap, eventDeactivationMap}, {dimensionSoftLimit, dimensionHardLimit}); + + SamplingInfo samplingInfo; + if (metric.has_dimensional_sampling_info()) { + invalidConfigReason = handleMetricWithSampling( + metric.id(), metric.dimensional_sampling_info(), dimensionsInWhat, samplingInfo); + if (invalidConfigReason.has_value()) { + return nullopt; + } + metricProducer->setSamplingInfo(samplingInfo); + } + + return metricProducer; } optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata( @@ -888,9 +1119,11 @@ optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata( unordered_map<int, vector<int>>& conditionToMetricMap, unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { + vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason) { if (!metric.has_id() || !metric.has_what()) { ALOGE("cannot find metric id or \"what\" in GaugeMetric \"%lld\"", (long long)metric.id()); + invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_MISSING_ID_OR_WHAT, metric.id()); return nullopt; } @@ -898,28 +1131,28 @@ optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata( (metric.gauge_fields_filter().include_all() == false)) && !hasLeafNode(metric.gauge_fields_filter().fields())) { ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id()); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_GAUGE_METRIC_INCORRECT_FIELD_FILTER, metric.id()); return nullopt; } if ((metric.gauge_fields_filter().has_include_all() && metric.gauge_fields_filter().include_all() == true) && hasLeafNode(metric.gauge_fields_filter().fields())) { ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id()); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_GAUGE_METRIC_INCORRECT_FIELD_FILTER, metric.id()); return nullopt; } int trackerIndex; - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, trackerIndex)) { + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + metric.what(), metric.id(), metricIndex, true, allAtomMatchingTrackers, + atomMatchingTrackerMap, trackerToMetricMap, trackerIndex); + if (invalidConfigReason.has_value()) { return nullopt; } sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex); - // For GaugeMetric atom, it should be simple matcher with one tagId. - if (atomMatcher->getAtomIds().size() != 1) { - return nullopt; - } int atomTagId = *(atomMatcher->getAtomIds().begin()); int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1; @@ -928,17 +1161,22 @@ optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata( if (metric.has_trigger_event()) { if (pullTagId == -1) { ALOGW("Pull atom not specified for trigger"); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_GAUGE_METRIC_TRIGGER_NO_PULL_ATOM, metric.id()); return nullopt; } // trigger_event should be used with FIRST_N_SAMPLES if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) { ALOGW("Gauge Metric with trigger event must have sampling type FIRST_N_SAMPLES"); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_GAUGE_METRIC_TRIGGER_NO_FIRST_N_SAMPLES, metric.id()); return nullopt; } - if (!handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex, - /*enforceOneAtom=*/true, allAtomMatchingTrackers, - atomMatchingTrackerMap, trackerToMetricMap, - triggerTrackerIndex)) { + invalidConfigReason = handleMetricWithAtomMatchingTrackers( + metric.trigger_event(), metric.id(), metricIndex, + /*enforceOneAtom=*/true, allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, triggerTrackerIndex); + if (invalidConfigReason.has_value()) { return nullopt; } sp<AtomMatchingTracker> triggerAtomMatcher = @@ -949,65 +1187,94 @@ optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata( if (!metric.has_trigger_event() && pullTagId != -1 && metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) { ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger"); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_GAUGE_METRIC_FIRST_N_SAMPLES_WITH_WRONG_EVENT, metric.id()); return nullopt; } int conditionIndex = -1; if (metric.has_condition()) { - if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap)) { + invalidConfigReason = handleMetricWithConditions( + metric.condition(), metric.id(), metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, conditionIndex, conditionToMetricMap); + if (invalidConfigReason.has_value()) { return nullopt; } } else { if (metric.links_size() > 0) { ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); + invalidConfigReason = InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_CONDITIONLINK_NO_CONDITION, metric.id()); return nullopt; } } unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, - atomMatchingTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, - eventActivationMap, eventDeactivationMap)) { + invalidConfigReason = handleMetricActivation( + config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap); + if (invalidConfigReason.has_value()) { return nullopt; } uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { + invalidConfigReason = + getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash); + if (invalidConfigReason.has_value()) { return nullopt; } const auto [dimensionSoftLimit, dimensionHardLimit] = StatsdStats::getAtomDimensionKeySizeLimits(pullTagId); - return {new GaugeMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, - metricHash, trackerIndex, matcherWizard, pullTagId, - triggerAtomId, atomTagId, timeBaseNs, currentTimeNs, - pullerManager, eventActivationMap, eventDeactivationMap, - dimensionSoftLimit, dimensionHardLimit)}; + sp<MetricProducer> metricProducer = new GaugeMetricProducer( + key, metric, conditionIndex, initialConditionCache, wizard, metricHash, trackerIndex, + matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseNs, currentTimeNs, + pullerManager, eventActivationMap, eventDeactivationMap, dimensionSoftLimit, + dimensionHardLimit); + + SamplingInfo samplingInfo; + std::vector<Matcher> dimensionsInWhat; + translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); + if (metric.has_dimensional_sampling_info()) { + invalidConfigReason = handleMetricWithSampling( + metric.id(), metric.dimensional_sampling_info(), dimensionsInWhat, samplingInfo); + if (invalidConfigReason.has_value()) { + return nullopt; + } + metricProducer->setSamplingInfo(samplingInfo); + } + + return metricProducer; } optional<sp<AnomalyTracker>> createAnomalyTracker( const Alert& alert, const sp<AlarmMonitor>& anomalyAlarmMonitor, const UpdateStatus& updateStatus, const int64_t currentTimeNs, const unordered_map<int64_t, int>& metricProducerMap, - vector<sp<MetricProducer>>& allMetricProducers) { + vector<sp<MetricProducer>>& allMetricProducers, + optional<InvalidConfigReason>& invalidConfigReason) { const auto& itr = metricProducerMap.find(alert.metric_id()); if (itr == metricProducerMap.end()) { ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(), (long long)alert.metric_id()); + invalidConfigReason = createInvalidConfigReasonWithAlert( + INVALID_CONFIG_REASON_ALERT_METRIC_NOT_FOUND, alert.metric_id(), alert.id()); return nullopt; } if (!alert.has_trigger_if_sum_gt()) { ALOGW("invalid alert: missing threshold"); + invalidConfigReason = createInvalidConfigReasonWithAlert( + INVALID_CONFIG_REASON_ALERT_THRESHOLD_MISSING, alert.id()); return nullopt; } if (alert.trigger_if_sum_gt() < 0 || alert.num_buckets() <= 0) { ALOGW("invalid alert: threshold=%f num_buckets= %d", alert.trigger_if_sum_gt(), alert.num_buckets()); + invalidConfigReason = createInvalidConfigReasonWithAlert( + INVALID_CONFIG_REASON_ALERT_INVALID_TRIGGER_OR_NUM_BUCKETS, alert.id()); return nullopt; } const int metricIndex = itr->second; @@ -1016,30 +1283,36 @@ optional<sp<AnomalyTracker>> createAnomalyTracker( metric->addAnomalyTracker(alert, anomalyAlarmMonitor, updateStatus, currentTimeNs); if (anomalyTracker == nullptr) { // The ALOGW for this invalid alert was already displayed in addAnomalyTracker(). + invalidConfigReason = createInvalidConfigReasonWithAlert( + INVALID_CONFIG_REASON_ALERT_CANNOT_ADD_ANOMALY, alert.metric_id(), alert.id()); return nullopt; } return {anomalyTracker}; } -bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - unordered_map<int64_t, int>& atomMatchingTrackerMap, - vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - unordered_map<int, vector<int>>& allTagIdsToMatchersMap) { +optional<InvalidConfigReason> initAtomMatchingTrackers( + const StatsdConfig& config, const sp<UidMap>& uidMap, + unordered_map<int64_t, int>& atomMatchingTrackerMap, + vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + unordered_map<int, vector<int>>& allTagIdsToMatchersMap) { vector<AtomMatcher> matcherConfigs; const int atomMatcherCount = config.atom_matcher_size(); matcherConfigs.reserve(atomMatcherCount); allAtomMatchingTrackers.reserve(atomMatcherCount); + optional<InvalidConfigReason> invalidConfigReason; for (int i = 0; i < atomMatcherCount; i++) { const AtomMatcher& logMatcher = config.atom_matcher(i); - sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(logMatcher, i, uidMap); + sp<AtomMatchingTracker> tracker = + createAtomMatchingTracker(logMatcher, i, uidMap, invalidConfigReason); if (tracker == nullptr) { - return false; + return invalidConfigReason; } allAtomMatchingTrackers.push_back(tracker); if (atomMatchingTrackerMap.find(logMatcher.id()) != atomMatchingTrackerMap.end()) { ALOGE("Duplicate AtomMatcher found!"); - return false; + return createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_MATCHER_DUPLICATE, + logMatcher.id()); } atomMatchingTrackerMap[logMatcher.id()] = i; matcherConfigs.push_back(logMatcher); @@ -1048,9 +1321,10 @@ bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidM vector<bool> stackTracker2(allAtomMatchingTrackers.size(), false); for (size_t matcherIndex = 0; matcherIndex < allAtomMatchingTrackers.size(); matcherIndex++) { auto& matcher = allAtomMatchingTrackers[matcherIndex]; - if (!matcher->init(matcherConfigs, allAtomMatchingTrackers, atomMatchingTrackerMap, - stackTracker2)) { - return false; + invalidConfigReason = matcher->init(matcherConfigs, allAtomMatchingTrackers, + atomMatchingTrackerMap, stackTracker2); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only. @@ -1071,32 +1345,35 @@ bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidM } } - return true; + return nullopt; } -bool initConditions(const ConfigKey& key, const StatsdConfig& config, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - unordered_map<int64_t, int>& conditionTrackerMap, - vector<sp<ConditionTracker>>& allConditionTrackers, - unordered_map<int, std::vector<int>>& trackerToConditionMap, - vector<ConditionState>& initialConditionCache) { +optional<InvalidConfigReason> initConditions( + const ConfigKey& key, const StatsdConfig& config, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, + unordered_map<int64_t, int>& conditionTrackerMap, + vector<sp<ConditionTracker>>& allConditionTrackers, + unordered_map<int, std::vector<int>>& trackerToConditionMap, + vector<ConditionState>& initialConditionCache) { vector<Predicate> conditionConfigs; const int conditionTrackerCount = config.predicate_size(); conditionConfigs.reserve(conditionTrackerCount); allConditionTrackers.reserve(conditionTrackerCount); initialConditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated); + optional<InvalidConfigReason> invalidConfigReason; for (int i = 0; i < conditionTrackerCount; i++) { const Predicate& condition = config.predicate(i); - sp<ConditionTracker> tracker = - createConditionTracker(key, condition, i, atomMatchingTrackerMap); + sp<ConditionTracker> tracker = createConditionTracker( + key, condition, i, atomMatchingTrackerMap, invalidConfigReason); if (tracker == nullptr) { - return false; + return invalidConfigReason; } allConditionTrackers.push_back(tracker); if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) { ALOGE("Duplicate Predicate found!"); - return false; + return createInvalidConfigReasonWithPredicate(INVALID_CONFIG_REASON_CONDITION_DUPLICATE, + condition.id()); } conditionTrackerMap[condition.id()] = i; conditionConfigs.push_back(condition); @@ -1105,21 +1382,24 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, vector<bool> stackTracker(allConditionTrackers.size(), false); for (size_t i = 0; i < allConditionTrackers.size(); i++) { auto& conditionTracker = allConditionTrackers[i]; - if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap, - stackTracker, initialConditionCache)) { - return false; + invalidConfigReason = + conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap, + stackTracker, initialConditionCache); + if (invalidConfigReason.has_value()) { + return invalidConfigReason; } for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) { auto& conditionList = trackerToConditionMap[trackerIndex]; conditionList.push_back(i); } } - return true; + return nullopt; } -bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap, - unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - map<int64_t, uint64_t>& stateProtoHashes) { +optional<InvalidConfigReason> initStates( + const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap, + unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, + map<int64_t, uint64_t>& stateProtoHashes) { for (int i = 0; i < config.state_size(); i++) { const State& state = config.state(i); const int64_t stateId = state.id(); @@ -1128,7 +1408,8 @@ bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAt string serializedState; if (!state.SerializeToString(&serializedState)) { ALOGE("Unable to serialize state %lld", (long long)stateId); - return false; + return createInvalidConfigReasonWithState( + INVALID_CONFIG_REASON_STATE_SERIALIZATION_FAILED, state.id(), state.atom_id()); } stateProtoHashes[stateId] = Hash64(serializedState); @@ -1140,31 +1421,33 @@ bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAt } } - return true; + return nullopt; } -bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs, - const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, - const unordered_map<int64_t, int>& conditionTrackerMap, - const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - const unordered_map<int64_t, int>& stateAtomIdMap, - const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - vector<sp<ConditionTracker>>& allConditionTrackers, - const vector<ConditionState>& initialConditionCache, - vector<sp<MetricProducer>>& allMetricProducers, - unordered_map<int, vector<int>>& conditionToMetricMap, - unordered_map<int, vector<int>>& trackerToMetricMap, - unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds, - unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { +optional<InvalidConfigReason> initMetrics( + const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs, + const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, + const unordered_map<int64_t, int>& conditionTrackerMap, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& stateAtomIdMap, + const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, + vector<sp<ConditionTracker>>& allConditionTrackers, + const vector<ConditionState>& initialConditionCache, + vector<sp<MetricProducer>>& allMetricProducers, + unordered_map<int, vector<int>>& conditionToMetricMap, + unordered_map<int, vector<int>>& trackerToMetricMap, unordered_map<int64_t, int>& metricMap, + std::set<int64_t>& noReportMetricIds, + unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation) { sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers); sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers); const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() + config.event_metric_size() + config.gauge_metric_size() + config.value_metric_size() + config.kll_metric_size(); allMetricProducers.reserve(allMetricsCount); + optional<InvalidConfigReason> invalidConfigReason; // Construct map from metric id to metric activation index. The map will be used to determine // the metric activation corresponding to a metric. @@ -1174,7 +1457,8 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t int64_t metricId = metricActivation.metric_id(); if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) { ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_HAS_MULTIPLE_ACTIVATIONS, + metricId); } metricToActivationMap.insert({metricId, i}); } @@ -1191,9 +1475,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation); + metricsWithActivation, invalidConfigReason); if (!producer) { - return false; + return invalidConfigReason; } allMetricProducers.push_back(producer.value()); } @@ -1210,9 +1494,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation); + metricsWithActivation, invalidConfigReason); if (!producer) { - return false; + return invalidConfigReason; } allMetricProducers.push_back(producer.value()); } @@ -1227,9 +1511,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation); + deactivationAtomTrackerToMetricMap, metricsWithActivation, invalidConfigReason); if (!producer) { - return false; + return invalidConfigReason; } allMetricProducers.push_back(producer.value()); } @@ -1245,9 +1529,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t conditionTrackerMap, initialConditionCache, wizard, matcherWizard, stateAtomIdMap, allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation); + metricsWithActivation, invalidConfigReason); if (!producer) { - return false; + return invalidConfigReason; } allMetricProducers.push_back(producer.value()); } @@ -1263,9 +1547,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t conditionTrackerMap, initialConditionCache, wizard, matcherWizard, stateAtomIdMap, allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation); + metricsWithActivation, invalidConfigReason); if (!producer) { - return false; + return invalidConfigReason; } allMetricProducers.push_back(producer.value()); } @@ -1281,9 +1565,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t conditionTrackerMap, initialConditionCache, wizard, matcherWizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation); + metricsWithActivation, invalidConfigReason); if (!producer) { - return false; + return invalidConfigReason; } allMetricProducers.push_back(producer.value()); } @@ -1291,7 +1575,8 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t const auto no_report_metric = config.no_report_metric(i); if (metricMap.find(no_report_metric) == metricMap.end()) { ALOGW("no_report_metric %" PRId64 " not exist", no_report_metric); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_NO_REPORT_METRIC_NOT_FOUND, + no_report_metric); } noReportMetricIds.insert(no_report_metric); } @@ -1306,40 +1591,41 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t if (whitelistedAtomIds.find(atomId) == whitelistedAtomIds.end()) { StateManager::getInstance().registerListener(atomId, it); } else { - return false; + return InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_SLICED_STATE_ATOM_ALLOWED_FROM_ANY_UID, + it->getMetricId()); } } } - return true; + return nullopt; } -bool initAlerts(const StatsdConfig& config, const int64_t currentTimeNs, - const unordered_map<int64_t, int>& metricProducerMap, - unordered_map<int64_t, int>& alertTrackerMap, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - vector<sp<MetricProducer>>& allMetricProducers, - vector<sp<AnomalyTracker>>& allAnomalyTrackers) { +optional<InvalidConfigReason> initAlerts(const StatsdConfig& config, const int64_t currentTimeNs, + const unordered_map<int64_t, int>& metricProducerMap, + unordered_map<int64_t, int>& alertTrackerMap, + const sp<AlarmMonitor>& anomalyAlarmMonitor, + vector<sp<MetricProducer>>& allMetricProducers, + vector<sp<AnomalyTracker>>& allAnomalyTrackers) { + optional<InvalidConfigReason> invalidConfigReason; for (int i = 0; i < config.alert_size(); i++) { const Alert& alert = config.alert(i); alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size())); - optional<sp<AnomalyTracker>> anomalyTracker = - createAnomalyTracker(alert, anomalyAlarmMonitor, UpdateStatus::UPDATE_NEW, - currentTimeNs, metricProducerMap, allMetricProducers); + optional<sp<AnomalyTracker>> anomalyTracker = createAnomalyTracker( + alert, anomalyAlarmMonitor, UpdateStatus::UPDATE_NEW, currentTimeNs, + metricProducerMap, allMetricProducers, invalidConfigReason); if (!anomalyTracker) { - return false; + return invalidConfigReason; } allAnomalyTrackers.push_back(anomalyTracker.value()); } - if (!initSubscribersForSubscriptionType(config, Subscription::ALERT, alertTrackerMap, - allAnomalyTrackers)) { - return false; - } - return true; + return initSubscribersForSubscriptionType(config, Subscription::ALERT, alertTrackerMap, + allAnomalyTrackers); } -bool initAlarms(const StatsdConfig& config, const ConfigKey& key, - const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, vector<sp<AlarmTracker>>& allAlarmTrackers) { +optional<InvalidConfigReason> initAlarms(const StatsdConfig& config, const ConfigKey& key, + const sp<AlarmMonitor>& periodicAlarmMonitor, + const int64_t timeBaseNs, const int64_t currentTimeNs, + vector<sp<AlarmTracker>>& allAlarmTrackers) { unordered_map<int64_t, int> alarmTrackerMap; int64_t startMillis = timeBaseNs / 1000 / 1000; int64_t currentTimeMillis = currentTimeNs / 1000 / 1000; @@ -1347,45 +1633,43 @@ bool initAlarms(const StatsdConfig& config, const ConfigKey& key, const Alarm& alarm = config.alarm(i); if (alarm.offset_millis() <= 0) { ALOGW("Alarm offset_millis should be larger than 0."); - return false; + return createInvalidConfigReasonWithAlarm( + INVALID_CONFIG_REASON_ALARM_OFFSET_LESS_THAN_OR_EQUAL_ZERO, alarm.id()); } if (alarm.period_millis() <= 0) { ALOGW("Alarm period_millis should be larger than 0."); - return false; + return createInvalidConfigReasonWithAlarm( + INVALID_CONFIG_REASON_ALARM_PERIOD_LESS_THAN_OR_EQUAL_ZERO, alarm.id()); } alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size())); allAlarmTrackers.push_back( new AlarmTracker(startMillis, currentTimeMillis, alarm, key, periodicAlarmMonitor)); } - if (!initSubscribersForSubscriptionType(config, Subscription::ALARM, alarmTrackerMap, - allAlarmTrackers)) { - return false; - } - return true; + return initSubscribersForSubscriptionType(config, Subscription::ALARM, alarmTrackerMap, + allAlarmTrackers); } -bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, - const sp<StatsPullerManager>& pullerManager, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, - std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap, - vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - unordered_map<int64_t, int>& atomMatchingTrackerMap, - vector<sp<ConditionTracker>>& allConditionTrackers, - unordered_map<int64_t, int>& conditionTrackerMap, - vector<sp<MetricProducer>>& allMetricProducers, - unordered_map<int64_t, int>& metricProducerMap, - vector<sp<AnomalyTracker>>& allAnomalyTrackers, - vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers, - unordered_map<int, std::vector<int>>& conditionToMetricMap, - unordered_map<int, std::vector<int>>& trackerToMetricMap, - unordered_map<int, std::vector<int>>& trackerToConditionMap, - unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - unordered_map<int64_t, int>& alertTrackerMap, - vector<int>& metricsWithActivation, map<int64_t, uint64_t>& stateProtoHashes, - set<int64_t>& noReportMetricIds) { +optional<InvalidConfigReason> initStatsdConfig( + const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, + const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor, + const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, + const int64_t currentTimeNs, + std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap, + vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + unordered_map<int64_t, int>& atomMatchingTrackerMap, + vector<sp<ConditionTracker>>& allConditionTrackers, + unordered_map<int64_t, int>& conditionTrackerMap, + vector<sp<MetricProducer>>& allMetricProducers, + unordered_map<int64_t, int>& metricProducerMap, + vector<sp<AnomalyTracker>>& allAnomalyTrackers, + vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers, + unordered_map<int, std::vector<int>>& conditionToMetricMap, + unordered_map<int, std::vector<int>>& trackerToMetricMap, + unordered_map<int, std::vector<int>>& trackerToConditionMap, + unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + unordered_map<int64_t, int>& alertTrackerMap, vector<int>& metricsWithActivation, + map<int64_t, uint64_t>& stateProtoHashes, set<int64_t>& noReportMetricIds) { vector<ConditionState> initialConditionCache; unordered_map<int64_t, int> stateAtomIdMap; unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; @@ -1393,47 +1677,59 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp if (config.package_certificate_hash_size_bytes() > UINT8_MAX) { ALOGE("Invalid value for package_certificate_hash_size_bytes: %d", config.package_certificate_hash_size_bytes()); - return false; + return InvalidConfigReason(INVALID_CONFIG_REASON_PACKAGE_CERT_HASH_SIZE_TOO_LARGE); } - if (!initAtomMatchingTrackers(config, uidMap, atomMatchingTrackerMap, allAtomMatchingTrackers, - allTagIdsToMatchersMap)) { + optional<InvalidConfigReason> invalidConfigReason = + initAtomMatchingTrackers(config, uidMap, atomMatchingTrackerMap, + allAtomMatchingTrackers, allTagIdsToMatchersMap); + if (invalidConfigReason.has_value()) { ALOGE("initAtomMatchingTrackers failed"); - return false; + return invalidConfigReason; } VLOG("initAtomMatchingTrackers succeed..."); - if (!initConditions(key, config, atomMatchingTrackerMap, conditionTrackerMap, - allConditionTrackers, trackerToConditionMap, initialConditionCache)) { + invalidConfigReason = + initConditions(key, config, atomMatchingTrackerMap, conditionTrackerMap, + allConditionTrackers, trackerToConditionMap, initialConditionCache); + if (invalidConfigReason.has_value()) { ALOGE("initConditionTrackers failed"); - return false; + return invalidConfigReason; } - if (!initStates(config, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)) { + invalidConfigReason = initStates(config, stateAtomIdMap, allStateGroupMaps, stateProtoHashes); + if (invalidConfigReason.has_value()) { ALOGE("initStates failed"); - return false; + return invalidConfigReason; } - if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, atomMatchingTrackerMap, - conditionTrackerMap, allAtomMatchingTrackers, stateAtomIdMap, - allStateGroupMaps, allConditionTrackers, initialConditionCache, - allMetricProducers, conditionToMetricMap, trackerToMetricMap, - metricProducerMap, noReportMetricIds, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { + + invalidConfigReason = initMetrics( + key, config, timeBaseNs, currentTimeNs, pullerManager, atomMatchingTrackerMap, + conditionTrackerMap, allAtomMatchingTrackers, stateAtomIdMap, allStateGroupMaps, + allConditionTrackers, initialConditionCache, allMetricProducers, conditionToMetricMap, + trackerToMetricMap, metricProducerMap, noReportMetricIds, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation); + if (invalidConfigReason.has_value()) { ALOGE("initMetricProducers failed"); - return false; + return invalidConfigReason; } - if (!initAlerts(config, currentTimeNs, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor, - allMetricProducers, allAnomalyTrackers)) { + + invalidConfigReason = initAlerts(config, currentTimeNs, metricProducerMap, alertTrackerMap, + anomalyAlarmMonitor, allMetricProducers, allAnomalyTrackers); + if (invalidConfigReason.has_value()) { ALOGE("initAlerts failed"); - return false; + return invalidConfigReason; } - if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs, - allPeriodicAlarmTrackers)) { + + invalidConfigReason = initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs, + allPeriodicAlarmTrackers); + if (invalidConfigReason.has_value()) { ALOGE("initAlarms failed"); - return false; + return invalidConfigReason; } - return true; + return nullopt; } } // namespace statsd diff --git a/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/statsd/src/metrics/parsing_utils/metrics_manager_util.h index 17e4b66b..c99b886b 100644 --- a/statsd/src/metrics/parsing_utils/metrics_manager_util.h +++ b/statsd/src/metrics/parsing_utils/metrics_manager_util.h @@ -37,41 +37,45 @@ namespace statsd { // input: // [logMatcher]: the input AtomMatcher from the StatsdConfig // [index]: the index of the matcher +// [invalidConfigReason]: logging ids if config is invalid // output: // new AtomMatchingTracker, or null if the tracker is unable to be created -sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index, - const sp<UidMap>& uidMap); +sp<AtomMatchingTracker> createAtomMatchingTracker( + const AtomMatcher& logMatcher, const int index, const sp<UidMap>& uidMap, + optional<InvalidConfigReason>& invalidConfigReason); // Create a ConditionTracker. // input: // [predicate]: the input Predicate from the StatsdConfig // [index]: the index of the condition tracker // [atomMatchingTrackerMap]: map of atom matcher id to its index in allAtomMatchingTrackers +// [invalidConfigReason]: logging ids if config is invalid // output: // new ConditionTracker, or null if the tracker is unable to be created sp<ConditionTracker> createConditionTracker( const ConfigKey& key, const Predicate& predicate, const int index, - const unordered_map<int64_t, int>& atomMatchingTrackerMap); + const unordered_map<int64_t, int>& atomMatchingTrackerMap, + optional<InvalidConfigReason>& invalidConfigReason); // Get the hash of a metric, combining the activation if the metric has one. -bool getMetricProtoHash(const StatsdConfig& config, const google::protobuf::MessageLite& metric, - const int64_t id, - const std::unordered_map<int64_t, int>& metricToActivationMap, - uint64_t& metricHash); +optional<InvalidConfigReason> getMetricProtoHash( + const StatsdConfig& config, const google::protobuf::MessageLite& metric, const int64_t id, + const std::unordered_map<int64_t, int>& metricToActivationMap, uint64_t& metricHash); // 1. Validates matcher existence // 2. Enforces matchers with dimensions and those used for trigger_event are about one atom // 3. Gets matcher index and updates tracker to metric map -bool handleMetricWithAtomMatchingTrackers( - const int64_t matcherId, const int metricIndex, const bool enforceOneAtom, +optional<InvalidConfigReason> handleMetricWithAtomMatchingTrackers( + const int64_t matcherId, const int64_t metricId, const int metricIndex, + const bool enforceOneAtom, const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, std::unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex); // 1. Validates condition existence, including those in links // 2. Gets condition index and updates condition to metric map -bool handleMetricWithConditions( - const int64_t condition, const int metricIndex, +optional<InvalidConfigReason> handleMetricWithConditions( + const int64_t condition, const int64_t metricId, const int metricIndex, const std::unordered_map<int64_t, int>& conditionTrackerMap, const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>& links, @@ -80,8 +84,8 @@ bool handleMetricWithConditions( // Validates a metricActivation and populates state. // Fills the new event activation/deactivation maps, preserving the existing activations. -// Returns false if there are errors. -bool handleMetricActivationOnConfigUpdate( +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> handleMetricActivationOnConfigUpdate( const StatsdConfig& config, const int64_t metricId, const int metricIndex, const std::unordered_map<int64_t, int>& metricToActivationMap, const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, @@ -110,7 +114,8 @@ optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata( std::unordered_map<int, std::vector<int>>& conditionToMetricMap, std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation); + std::vector<int>& metricsWithActivation, + optional<InvalidConfigReason>& invalidConfigReason); // Creates a DurationMetricProducer and updates the vectors/maps used by MetricsManager with // the appropriate indices. Returns an sp to the producer, or nullopt if there was an error. @@ -129,7 +134,8 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( std::unordered_map<int, std::vector<int>>& conditionToMetricMap, std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation); + std::vector<int>& metricsWithActivation, + optional<InvalidConfigReason>& invalidConfigReason); // Creates an EventMetricProducer and updates the vectors/maps used by MetricsManager with // the appropriate indices. Returns an sp to the producer, or nullopt if there was an error. @@ -146,7 +152,8 @@ optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata( std::unordered_map<int, std::vector<int>>& conditionToMetricMap, std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation); + std::vector<int>& metricsWithActivation, + optional<InvalidConfigReason>& invalidConfigReason); // Creates a NumericValueMetricProducer and updates the vectors/maps used by MetricsManager with // the appropriate indices. Returns an sp to the producer, or nullopt if there was an error. @@ -167,7 +174,8 @@ optional<sp<MetricProducer>> createNumericValueMetricProducerAndUpdateMetadata( std::unordered_map<int, std::vector<int>>& conditionToMetricMap, std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation); + std::vector<int>& metricsWithActivation, + optional<InvalidConfigReason>& invalidConfigReason); // Creates a GaugeMetricProducer and updates the vectors/maps used by MetricsManager with // the appropriate indices. Returns an sp to the producer, or nullopt if there was an error. @@ -186,7 +194,8 @@ optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata( std::unordered_map<int, std::vector<int>>& conditionToMetricMap, std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::vector<int>& metricsWithActivation); + std::vector<int>& metricsWithActivation, + optional<InvalidConfigReason>& invalidConfigReason); // Creates a KllMetricProducer and updates the vectors/maps used by MetricsManager with // the appropriate indices. Returns an sp to the producer, or nullopt if there was an error. @@ -207,7 +216,7 @@ optional<sp<MetricProducer>> createKllMetricProducerAndUpdateMetadata( unordered_map<int, vector<int>>& conditionToMetricMap, unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation); + vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason); // Creates an AnomalyTracker and adds it to the appropriate metric. // Returns an sp to the AnomalyTracker, or nullopt if there was an error. @@ -215,14 +224,15 @@ optional<sp<AnomalyTracker>> createAnomalyTracker( const Alert& alert, const sp<AlarmMonitor>& anomalyAlarmMonitor, const UpdateStatus& updateStatus, const int64_t currentTimeNs, const std::unordered_map<int64_t, int>& metricProducerMap, - std::vector<sp<MetricProducer>>& allMetricProducers); + std::vector<sp<MetricProducer>>& allMetricProducers, + optional<InvalidConfigReason>& invalidConfigReason); -// Templated function for adding subscriptions to alarms or alerts. Returns true if successful. +// Templated function for adding subscriptions to alarms or alerts. Returns nullopt if successful +// and InvalidConfigReason if not. template <typename T> -bool initSubscribersForSubscriptionType(const StatsdConfig& config, - const Subscription_RuleType ruleType, - const std::unordered_map<int64_t, int>& ruleMap, - std::vector<T>& allRules) { +optional<InvalidConfigReason> initSubscribersForSubscriptionType( + const StatsdConfig& config, const Subscription_RuleType ruleType, + const std::unordered_map<int64_t, int>& ruleMap, std::vector<T>& allRules) { for (int i = 0; i < config.subscription_size(); ++i) { const Subscription& subscription = config.subscription(i); if (subscription.rule_type() != ruleType) { @@ -231,18 +241,31 @@ bool initSubscribersForSubscriptionType(const StatsdConfig& config, if (subscription.subscriber_information_case() == Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) { ALOGW("subscription \"%lld\" has no subscriber info.\"", (long long)subscription.id()); - return false; + return createInvalidConfigReasonWithSubscription( + INVALID_CONFIG_REASON_SUBSCRIPTION_SUBSCRIBER_INFO_MISSING, subscription.id()); } const auto& itr = ruleMap.find(subscription.rule_id()); if (itr == ruleMap.end()) { ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"", (long long)subscription.id(), (long long)subscription.rule_id()); - return false; + switch (subscription.rule_type()) { + case Subscription::ALARM: + return createInvalidConfigReasonWithSubscriptionAndAlarm( + INVALID_CONFIG_REASON_SUBSCRIPTION_RULE_NOT_FOUND, subscription.id(), + subscription.rule_id()); + case Subscription::ALERT: + return createInvalidConfigReasonWithSubscriptionAndAlert( + INVALID_CONFIG_REASON_SUBSCRIPTION_RULE_NOT_FOUND, subscription.id(), + subscription.rule_id()); + case Subscription::RULE_TYPE_UNSPECIFIED: + return createInvalidConfigReasonWithSubscription( + INVALID_CONFIG_REASON_SUBSCRIPTION_RULE_NOT_FOUND, subscription.id()); + } } const int ruleIndex = itr->second; allRules[ruleIndex]->addSubscription(subscription); } - return true; + return nullopt; } // Helper functions for MetricsManager to initialize from StatsdConfig. @@ -259,10 +282,12 @@ bool initSubscribersForSubscriptionType(const StatsdConfig& config, // [atomMatchingTrackerMap]: this map should contain matcher name to index mapping // [allAtomMatchingTrackers]: should store the sp to all the AtomMatchingTracker // [allTagIdsToMatchersMap]: maps of tag ids to atom matchers -bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap); +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> initAtomMatchingTrackers( + const StatsdConfig& config, const sp<UidMap>& uidMap, + std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap); // Initialize ConditionTrackers // input: @@ -275,12 +300,14 @@ bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidM // [trackerToConditionMap]: contain the mapping from index of // log tracker to condition trackers that use the log tracker // [initialConditionCache]: stores the initial conditions for each ConditionTracker -bool initConditions(const ConfigKey& key, const StatsdConfig& config, - const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - std::unordered_map<int64_t, int>& conditionTrackerMap, - std::vector<sp<ConditionTracker>>& allConditionTrackers, - std::unordered_map<int, std::vector<int>>& trackerToConditionMap, - std::vector<ConditionState>& initialConditionCache); +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> initConditions( + const ConfigKey& key, const StatsdConfig& config, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + std::unordered_map<int64_t, int>& conditionTrackerMap, + std::vector<sp<ConditionTracker>>& allConditionTrackers, + std::unordered_map<int, std::vector<int>>& trackerToConditionMap, + std::vector<ConditionState>& initialConditionCache); // Initialize State maps using State protos in the config. These maps will // eventually be passed to MetricProducers to initialize their state info. @@ -291,9 +318,11 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, // [allStateGroupMaps]: this map should contain the mapping from states ids and state // values to state group ids for all states // [stateProtoHashes]: contains a map of state id to the hash of the State proto from the config -bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap, - unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, - std::map<int64_t, uint64_t>& stateProtoHashes); +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> initStates( + const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap, + unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, + std::map<int64_t, uint64_t>& stateProtoHashes); // Initialize MetricProducers. // input: @@ -310,7 +339,8 @@ bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAt // [conditionToMetricMap]: contains the mapping from condition tracker index to // the list of MetricProducer index // [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index. -bool initMetrics( +// Returns nullopt if successful and InvalidConfigReason if not. +optional<InvalidConfigReason> initMetrics( const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs, const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, @@ -330,35 +360,34 @@ bool initMetrics( // Initialize alarms // Is called both on initialize new configs and config updates since alarms do not have any state. -bool initAlarms(const StatsdConfig& config, const ConfigKey& key, - const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, std::vector<sp<AlarmTracker>>& allAlarmTrackers); +optional<InvalidConfigReason> initAlarms(const StatsdConfig& config, const ConfigKey& key, + const sp<AlarmMonitor>& periodicAlarmMonitor, + const int64_t timeBaseNs, const int64_t currentTimeNs, + std::vector<sp<AlarmTracker>>& allAlarmTrackers); // Initialize MetricsManager from StatsdConfig. // Parameters are the members of MetricsManager. See MetricsManager for declaration. -bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, - const sp<StatsPullerManager>& pullerManager, - const sp<AlarmMonitor>& anomalyAlarmMonitor, - const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, - std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap, - std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, - std::unordered_map<int64_t, int>& atomMatchingTrackerMap, - std::vector<sp<ConditionTracker>>& allConditionTrackers, - std::unordered_map<int64_t, int>& conditionTrackerMap, - std::vector<sp<MetricProducer>>& allMetricProducers, - std::unordered_map<int64_t, int>& metricProducerMap, - vector<sp<AnomalyTracker>>& allAnomalyTrackers, - vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers, - std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& trackerToConditionMap, - std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - std::unordered_map<int64_t, int>& alertTrackerMap, - std::vector<int>& metricsWithActivation, - std::map<int64_t, uint64_t>& stateProtoHashes, - std::set<int64_t>& noReportMetricIds); +optional<InvalidConfigReason> initStatsdConfig( + const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, + const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor, + const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, + const int64_t currentTimeNs, + std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap, + std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + std::vector<sp<ConditionTracker>>& allConditionTrackers, + std::unordered_map<int64_t, int>& conditionTrackerMap, + std::vector<sp<MetricProducer>>& allMetricProducers, + std::unordered_map<int64_t, int>& metricProducerMap, + vector<sp<AnomalyTracker>>& allAnomalyTrackers, + vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::unordered_map<int, std::vector<int>>& trackerToConditionMap, + std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::unordered_map<int64_t, int>& alertTrackerMap, std::vector<int>& metricsWithActivation, + std::map<int64_t, uint64_t>& stateProtoHashes, std::set<int64_t>& noReportMetricIds); } // namespace statsd } // namespace os diff --git a/statsd/src/stats_log.proto b/statsd/src/stats_log.proto index 89fdb9c7..ef3a60dc 100644 --- a/statsd/src/stats_log.proto +++ b/statsd/src/stats_log.proto @@ -22,6 +22,7 @@ option java_package = "com.android.os"; option java_outer_classname = "StatsLog"; import "frameworks/proto_logging/stats/atoms.proto"; +import "packages/modules/StatsD/statsd/src/guardrail/invalid_config_reason_enum.proto"; message DimensionsValue { optional int32 field = 1; @@ -141,6 +142,7 @@ message ValueBucketInfo { int64 value_long = 2; double value_double = 3; } + optional int32 sample_size = 4; } repeated Value values = 9; @@ -438,6 +440,17 @@ message StatsdStatsReport { optional int32 stats_end_time_sec = 2; + message InvalidConfigReason { + optional InvalidConfigReasonEnum reason = 1; + optional int64 metric_id = 2; + optional int64 state_id = 3; + optional int64 alert_id = 4; + optional int64 alarm_id = 5; + optional int64 subscription_id = 6; + repeated int64 matcher_id = 7; + repeated int64 condition_id = 8; + } + message MatcherStats { optional int64 id = 1; optional int32 matched_times = 2; @@ -469,6 +482,7 @@ message StatsdStatsReport { optional int32 matcher_count = 7; optional int32 alert_count = 8; optional bool is_valid = 9; + optional InvalidConfigReason invalid_config_reason = 24; repeated int32 broadcast_sent_time_sec = 10; repeated int32 data_drop_time_sec = 11; repeated int64 data_drop_bytes = 21; diff --git a/statsd/src/statsd_config.proto b/statsd/src/statsd_config.proto index b75b6702..758a8d11 100644 --- a/statsd/src/statsd_config.proto +++ b/statsd/src/statsd_config.proto @@ -214,6 +214,12 @@ message UploadThreshold { } } +message DimensionalSamplingInfo { + optional FieldMatcher sampled_what_field = 1; + + optional int32 shard_count = 2; +} + message EventMetric { optional int64 id = 1; @@ -250,6 +256,8 @@ message CountMetric { optional FieldMatcher dimensions_in_condition = 7 [deprecated = true]; + optional DimensionalSamplingInfo dimensional_sampling_info = 12; + reserved 100; reserved 101; } @@ -284,6 +292,8 @@ message DurationMetric { optional FieldMatcher dimensions_in_condition = 8 [deprecated = true]; + optional DimensionalSamplingInfo dimensional_sampling_info = 13; + reserved 100; reserved 101; } @@ -323,6 +333,8 @@ message GaugeMetric { optional bool split_bucket_for_app_upgrade = 14; + optional DimensionalSamplingInfo dimensional_sampling_info = 15; + reserved 100; reserved 101; } @@ -358,6 +370,8 @@ message ValueMetric { } optional AggregationType aggregation_type = 8 [default = SUM]; + optional bool include_sample_size = 22; + optional int64 min_bucket_size_nanos = 10; optional bool use_absolute_value_on_reset = 11 [default = false]; @@ -382,6 +396,8 @@ message ValueMetric { optional FieldMatcher dimensions_in_condition = 9 [deprecated = true]; + optional DimensionalSamplingInfo dimensional_sampling_info = 23; + reserved 100; reserved 101; } @@ -409,6 +425,8 @@ message KllMetric { repeated MetricStateLink state_link = 11; + optional DimensionalSamplingInfo dimensional_sampling_info = 12; + reserved 100; reserved 101; } diff --git a/statsd/src/utils/ShardOffsetProvider.cpp b/statsd/src/utils/ShardOffsetProvider.cpp new file mode 100644 index 00000000..6d847dff --- /dev/null +++ b/statsd/src/utils/ShardOffsetProvider.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ShardOffsetProvider.h" + +namespace android { +namespace os { +namespace statsd { + +ShardOffsetProvider::ShardOffsetProvider(const int shardOffset) : mShardOffset(shardOffset) { +} + +ShardOffsetProvider& ShardOffsetProvider::getInstance() { + static ShardOffsetProvider sShardOffsetProvider(rand()); + return sShardOffsetProvider; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/statsd/src/utils/ShardOffsetProvider.h b/statsd/src/utils/ShardOffsetProvider.h new file mode 100644 index 00000000..0623e0f2 --- /dev/null +++ b/statsd/src/utils/ShardOffsetProvider.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SHARD_OFFSET_PROVIDER_H +#define SHARD_OFFSET_PROVIDER_H + +#include <gtest/gtest_prod.h> +#include <stdlib.h> + +namespace android { +namespace os { +namespace statsd { + +/* + * Class is not guarded by any mutex. It is currently thread safe. + * Thread safety needs to be considered on all future changes to this class. + */ +class ShardOffsetProvider final { +public: + ~ShardOffsetProvider(){}; + + int getShardOffset() const { + return mShardOffset; + } + + static ShardOffsetProvider& getInstance(); + +private: + ShardOffsetProvider(const int shardOffset); + + // Only used for testing. + void setShardOffset(const int shardOffset) { + mShardOffset = shardOffset; + } + + int mShardOffset; + + FRIEND_TEST(CountMetricE2eTest, TestDimensionalSampling); + FRIEND_TEST(DurationMetricE2eTest, TestDimensionalSampling); + FRIEND_TEST(GaugeMetricE2ePushedTest, TestDimensionalSampling); + FRIEND_TEST(GaugeMetricProducerTest, TestPullDimensionalSampling); + FRIEND_TEST(KllMetricE2eTest, TestDimensionalSampling); + FRIEND_TEST(NumericValueMetricProducerTest, TestDimensionalSampling); +}; + +} // namespace statsd +} // namespace os +} // namespace android +#endif // METRIC_PRODUCER_H
\ No newline at end of file diff --git a/statsd/tests/ConfigManager_test.cpp b/statsd/tests/ConfigManager_test.cpp index 7b8f74d0..bfc89685 100644 --- a/statsd/tests/ConfigManager_test.cpp +++ b/statsd/tests/ConfigManager_test.cpp @@ -147,6 +147,8 @@ TEST(ConfigManagerTest, TestRemoveUid) { EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("xxx")))); EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("yyy")))); EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("zzz")))); + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, StringToId("aaa")))); + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(3, StringToId("bbb")))); manager->StartupForTest(); manager->UpdateConfig(ConfigKey(1, StringToId("aaa")), config); @@ -156,4 +158,6 @@ TEST(ConfigManagerTest, TestRemoveUid) { manager->UpdateConfig(ConfigKey(3, StringToId("bbb")), config); manager->RemoveConfigs(2); + manager->RemoveConfigs(1); + manager->RemoveConfigs(3); } diff --git a/statsd/tests/FieldValue_test.cpp b/statsd/tests/FieldValue_test.cpp index ff3e54af..64be5593 100644 --- a/statsd/tests/FieldValue_test.cpp +++ b/statsd/tests/FieldValue_test.cpp @@ -132,7 +132,8 @@ TEST(AtomMatcherTest, TestFilter_ALL) { std::vector<string> attributionTags = {"location1", "location2", "location3"}; LogEvent event(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event, 10 /*atomId*/, 1012345, attributionUids, attributionTags, "some value"); + makeLogEvent(&event, 10 /*atomId*/, /*timestamp=*/1012345, attributionUids, attributionTags, + "some value"); HashableDimensionKey output; filterValues(matchers, event.getValues(), &output); @@ -227,6 +228,142 @@ TEST(AtomMatcherTest, TestFilterRepeated_ALL) { EXPECT_EQ((int32_t)13, output.getValues()[2].mValue.int_value); } +TEST(AtomMatcherTest, TestFilterWithOneMatcher) { + FieldMatcher matcher; + matcher.set_field(10); + FieldMatcher* child = matcher.add_child(); + child->set_field(2); + + vector<Matcher> matchers; + translateFieldMatcher(matcher, &matchers); + + std::vector<int> attributionUids = {1111, 2222, 3333}; + std::vector<string> attributionTags = {"location1", "location2", "location3"}; + + LogEvent event(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event, 10 /*atomId*/, /*timestamp=*/1012345, attributionUids, attributionTags, + "some value"); + FieldValue value; + + EXPECT_TRUE(filterValues(matchers[0], event.getValues(), &value)); + EXPECT_EQ((int32_t)0x20000, value.mField.getField()); + EXPECT_EQ("some value", value.mValue.str_value); +} + +TEST(AtomMatcherTest, TestFilterWithOneMatcher_PositionFIRST) { + FieldMatcher matcher; + matcher.set_field(10); + FieldMatcher* child = matcher.add_child(); + child->set_field(1); + child->set_position(Position::FIRST); + child->add_child()->set_field(1); + + vector<Matcher> matchers; + translateFieldMatcher(matcher, &matchers); + + std::vector<int> attributionUids = {1111, 2222, 3333}; + std::vector<string> attributionTags = {"location1", "location2", "location3"}; + + LogEvent event(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event, 10 /*atomId*/, /*timestamp=*/1012345, attributionUids, attributionTags, + "some value"); + FieldValue value; + + // Should only match the first field. + EXPECT_TRUE(filterValues(matchers[0], event.getValues(), &value)); + EXPECT_EQ((int32_t)0x02010101, value.mField.getField()); + EXPECT_EQ((int32_t)1111, value.mValue.int_value); +} + +TEST(AtomMatcherTest, TestFilterWithOneMatcher_PositionLAST) { + FieldMatcher matcher; + matcher.set_field(10); + FieldMatcher* child = matcher.add_child(); + child->set_field(1); + child->set_position(Position::LAST); + child->add_child()->set_field(1); + + vector<Matcher> matchers; + translateFieldMatcher(matcher, &matchers); + + std::vector<int> attributionUids = {1111, 2222, 3333}; + std::vector<string> attributionTags = {"location1", "location2", "location3"}; + + LogEvent event(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event, 10 /*atomId*/, /*timestamp=*/1012345, attributionUids, attributionTags, + "some value"); + FieldValue value; + + // Should only match the last field. + EXPECT_TRUE(filterValues(matchers[0], event.getValues(), &value)); + EXPECT_EQ((int32_t)0x02018301, value.mField.getField()); + EXPECT_EQ((int32_t)3333, value.mValue.int_value); +} + +TEST(AtomMatcherTest, TestFilterWithOneMatcher_PositionALL) { + FieldMatcher matcher; + matcher.set_field(10); + FieldMatcher* child = matcher.add_child(); + child->set_field(1); + child->set_position(Position::ALL); + child->add_child()->set_field(1); + + vector<Matcher> matchers; + translateFieldMatcher(matcher, &matchers); + + std::vector<int> attributionUids = {1111, 2222, 3333}; + std::vector<string> attributionTags = {"location1", "location2", "location3"}; + + LogEvent event(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event, 10 /*atomId*/, 1012345, attributionUids, attributionTags, "some value"); + FieldValue value; + + // Can't filter with position ALL matcher. + EXPECT_FALSE(filterValues(matchers[0], event.getValues(), &value)); +} + +TEST(AtomMatcherTest, TestFilterWithOneMatcher_DifferentField) { + FieldMatcher matcher; + matcher.set_field(10); + FieldMatcher* child = matcher.add_child(); + child->set_field(3); + + vector<Matcher> matchers; + translateFieldMatcher(matcher, &matchers); + + std::vector<int> attributionUids = {1111, 2222, 3333}; + std::vector<string> attributionTags = {"location1", "location2", "location3"}; + + LogEvent event(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event, 10 /*atomId*/, /*timestamp=*/1012345, attributionUids, attributionTags, + "some value"); + FieldValue value; + + // Shouldn't match any fields because matcher is looking for field 3. + EXPECT_FALSE(filterValues(matchers[0], event.getValues(), &value)); +} + +TEST(AtomMatcherTest, TestFilterWithOneMatcher_EmptyAttributionUids) { + FieldMatcher matcher; + matcher.set_field(10); + FieldMatcher* child = matcher.add_child(); + child->set_field(1); + child->set_position(Position::ALL); + child->add_child()->set_field(1); + + vector<Matcher> matchers; + translateFieldMatcher(matcher, &matchers); + + std::vector<string> attributionTags = {"location1", "location2", "location3"}; + + LogEvent event(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event, 10 /*atomId*/, /*timestamp=*/1012345, {}, attributionTags, "some value"); + FieldValue value; + + // Shouldn't match any fields because field 1 is empty. + EXPECT_FALSE(filterValues(matchers[0], event.getValues(), &value)); +} + TEST(AtomMatcherTest, TestSubDimension) { HashableDimensionKey dim; @@ -792,6 +929,111 @@ TEST(AtomMatcherTest, TestIsPrimitiveRepeatedField) { EXPECT_FALSE(isPrimitiveRepeatedField(field7)); } +TEST(FieldValueTest, TestShouldKeepSampleInt) { + int shardOffset = 5; + int shardCount = 2; + int pos1[] = {1, 1, 1}; + + Field field(1, pos1, 2); + + Value value1((int32_t)1001); + Value value2((int32_t)1002); + + FieldValue fieldValue1(field, value1); + FieldValue fieldValue2(field, value2); + + EXPECT_TRUE(shouldKeepSample(fieldValue1, shardOffset, shardCount)); + EXPECT_FALSE(shouldKeepSample(fieldValue2, shardOffset, shardCount)); +} + +TEST(FieldValueTest, TestShouldKeepSampleLong) { + int shardOffset = 5; + int shardCount = 2; + int pos1[] = {1, 1, 1}; + + Field field(1, pos1, 2); + + Value value1((int64_t)1001L); + Value value2((int64_t)1005L); + + FieldValue fieldValue1(field, value1); + FieldValue fieldValue2(field, value2); + + EXPECT_FALSE(shouldKeepSample(fieldValue1, shardOffset, shardCount)); + EXPECT_TRUE(shouldKeepSample(fieldValue2, shardOffset, shardCount)); +} + +TEST(FieldValueTest, TestShouldKeepSampleFloat) { + int shardOffset = 5; + int shardCount = 2; + int pos1[] = {1, 1, 1}; + + Field field(1, pos1, 2); + + Value value1((float)10.5); + Value value2((float)3.9); + + FieldValue fieldValue1(field, value1); + FieldValue fieldValue2(field, value2); + + EXPECT_TRUE(shouldKeepSample(fieldValue1, shardOffset, shardCount)); + EXPECT_FALSE(shouldKeepSample(fieldValue2, shardOffset, shardCount)); +} + +TEST(FieldValueTest, TestShouldKeepSampleDouble) { + int shardOffset = 5; + int shardCount = 2; + int pos1[] = {1, 1, 1}; + + Field field(1, pos1, 2); + + Value value1((double)1.5); + Value value2((double)3.9); + + FieldValue fieldValue1(field, value1); + FieldValue fieldValue2(field, value2); + + EXPECT_TRUE(shouldKeepSample(fieldValue1, shardOffset, shardCount)); + EXPECT_FALSE(shouldKeepSample(fieldValue2, shardOffset, shardCount)); +} + +TEST(FieldValueTest, TestShouldKeepSampleString) { + int shardOffset = 5; + int shardCount = 2; + int pos1[] = {1, 1, 1}; + + Field field(1, pos1, 2); + + Value value1("str1"); + Value value2("str2"); + + FieldValue fieldValue1(field, value1); + FieldValue fieldValue2(field, value2); + + EXPECT_FALSE(shouldKeepSample(fieldValue1, shardOffset, shardCount)); + EXPECT_TRUE(shouldKeepSample(fieldValue2, shardOffset, shardCount)); +} + +TEST(FieldValueTest, TestShouldKeepSampleByteArray) { + int shardOffset = 5; + int shardCount = 2; + int pos1[] = {1, 1, 1}; + + Field field(1, pos1, 2); + + vector<uint8_t> message1 = {'\t', 'e', '\0', 's', 't'}; + vector<uint8_t> message2 = {'\t', 'e', '\0', 's', 't', 't'}; + + Value value1(message1); + Value value2(message2); + + FieldValue fieldValue1(field, value1); + FieldValue fieldValue2(field, value2); + + EXPECT_FALSE(shouldKeepSample(fieldValue1, shardOffset, shardCount)); + EXPECT_TRUE(shouldKeepSample(fieldValue2, shardOffset, shardCount)); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/statsd/tests/HashableDimensionKey_test.cpp b/statsd/tests/HashableDimensionKey_test.cpp index 8fe555e7..b6ddf409 100644 --- a/statsd/tests/HashableDimensionKey_test.cpp +++ b/statsd/tests/HashableDimensionKey_test.cpp @@ -129,6 +129,41 @@ TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_AllConditionsMet) { EXPECT_TRUE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId)); } +/** + * Test that FieldValues with STORAGE values are hashed differently. + */ +TEST(HashableDimensionKeyTest, TestHashDimensionStorage) { + int pos[] = {1, 1, 1}; + Field field(1, pos, 1); + vector<uint8_t> bytesField1{10, 20, 30}; + vector<uint8_t> bytesField2{10, 20, 30, 40}; + FieldValue fieldValue1(field, Value(bytesField1)); + FieldValue fieldValue2(field, Value(bytesField2)); + HashableDimensionKey dimKey1; + dimKey1.addValue(fieldValue1); + HashableDimensionKey dimKey2; + dimKey2.addValue(fieldValue2); + + EXPECT_NE(std::hash<HashableDimensionKey>{}(dimKey1), + std::hash<HashableDimensionKey>{}(dimKey2)); +} +/** + * Test that FieldValues with DOUBLE values are hashed differently. + */ +TEST(HashableDimensionKeyTest, TestHashDimensionDouble) { + int pos[] = {1, 1, 1}; + Field field(1, pos, 1); + FieldValue fieldValue1(field, Value((double)100.00)); + FieldValue fieldValue2(field, Value((double)200.00)); + HashableDimensionKey dimKey1; + dimKey1.addValue(fieldValue1); + HashableDimensionKey dimKey2; + dimKey2.addValue(fieldValue2); + + EXPECT_NE(std::hash<HashableDimensionKey>{}(dimKey1), + std::hash<HashableDimensionKey>{}(dimKey2)); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/statsd/tests/StatsLogProcessor_test.cpp b/statsd/tests/StatsLogProcessor_test.cpp index 4bca4528..32f091f7 100644 --- a/statsd/tests/StatsLogProcessor_test.cpp +++ b/statsd/tests/StatsLogProcessor_test.cpp @@ -1748,6 +1748,11 @@ TEST_P(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { vector<uint8_t> buffer; processor->onDumpReport(cfgKey1, configAddedTimeNs + NS_PER_SEC, false, true, ADB_DUMP, FAST, &buffer); + + service->removeConfiguration(configId, uid); + service->mProcessor->onDumpReport(cfgKey1, getElapsedRealtimeNs(), + false /* include_current_bucket*/, true /* erase_data */, + ADB_DUMP, NO_TIME_CONSTRAINTS, nullptr); } TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogHostUid) { diff --git a/statsd/tests/StatsService_test.cpp b/statsd/tests/StatsService_test.cpp index 24cc2f26..bd760814 100644 --- a/statsd/tests/StatsService_test.cpp +++ b/statsd/tests/StatsService_test.cpp @@ -22,6 +22,7 @@ #include "config/ConfigKey.h" #include "packages/UidMap.h" #include "src/statsd_config.pb.h" +#include "tests/statsd_test_util.h" using namespace android; using namespace testing; @@ -39,12 +40,19 @@ TEST(StatsServiceTest, TestAddConfig_simple) { const sp<UidMap> uidMap = new UidMap(); shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(uidMap, /* queue */ nullptr); + const int kConfigKey = 12345; + const int kCallingUid = 123; StatsdConfig config; - config.set_id(12345); + config.set_id(kConfigKey); string serialized = config.SerializeAsString(); - EXPECT_TRUE( - service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()})); + EXPECT_TRUE(service->addConfigurationChecked(kCallingUid, kConfigKey, + {serialized.begin(), serialized.end()})); + service->removeConfiguration(kConfigKey, kCallingUid); + ConfigKey configKey(kCallingUid, kConfigKey); + service->mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), + false /* include_current_bucket*/, true /* erase_data */, + ADB_DUMP, NO_TIME_CONSTRAINTS, nullptr); } TEST(StatsServiceTest, TestAddConfig_empty) { @@ -52,9 +60,15 @@ TEST(StatsServiceTest, TestAddConfig_empty) { shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(uidMap, /* queue */ nullptr); string serialized = ""; - - EXPECT_TRUE( - service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()})); + const int kConfigKey = 12345; + const int kCallingUid = 123; + EXPECT_TRUE(service->addConfigurationChecked(kCallingUid, kConfigKey, + {serialized.begin(), serialized.end()})); + service->removeConfiguration(kConfigKey, kCallingUid); + ConfigKey configKey(kCallingUid, kConfigKey); + service->mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), + false /* include_current_bucket*/, true /* erase_data */, + ADB_DUMP, NO_TIME_CONSTRAINTS, nullptr); } TEST(StatsServiceTest, TestAddConfig_invalid) { diff --git a/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/statsd/tests/e2e/Anomaly_count_e2e_test.cpp index ec74df63..eccaafea 100644 --- a/statsd/tests/e2e/Anomaly_count_e2e_test.cpp +++ b/statsd/tests/e2e/Anomaly_count_e2e_test.cpp @@ -54,7 +54,7 @@ StatsdConfig CreateStatsdConfig(int num_buckets, int threshold, int refractory_p } // namespace -TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) { +TEST(AnomalyCountDetectionE2eTest, TestSlicedCountMetric_single_bucket) { const int num_buckets = 1; const int threshold = 3; const int refractory_period_sec = 10; @@ -171,7 +171,7 @@ TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) { anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); } -TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) { +TEST(AnomalyCountDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) { const int num_buckets = 3; const int threshold = 3; const int refractory_period_sec = 10; @@ -241,7 +241,7 @@ TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) { anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); } -TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written) { +TEST(AnomalyCountDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written) { const int num_buckets = 1; const int threshold = 0; const int refractory_period_sec = 86400 * 365; // 1 year @@ -266,7 +266,7 @@ TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_wr ASSERT_EQ(result.stats_metadata_size(), 0); } -TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk) { +TEST(AnomalyCountDetectionE2eTest, TestCountMetric_save_refractory_to_disk) { const int num_buckets = 1; const int threshold = 0; const int refractory_period_sec = 86400 * 365; // 1 year @@ -329,7 +329,7 @@ TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk) { EXPECT_EQ(dimKeyInWhat.value_int(), fieldValue1.mValue.int_value); } -TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk) { +TEST(AnomalyCountDetectionE2eTest, TestCountMetric_load_refractory_from_disk) { const int num_buckets = 1; const int threshold = 0; const int refractory_period_sec = 86400 * 365; // 1 year diff --git a/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp index 30e8fd75..04b473cc 100644 --- a/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp +++ b/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp @@ -35,9 +35,6 @@ namespace statsd { namespace { -const int kConfigKey = 789130124; -const int kCallingUid = 0; - StatsdConfig CreateStatsdConfig(int num_buckets, uint64_t threshold_ns, DurationMetric::AggregationType aggregationType, @@ -98,25 +95,19 @@ MetricDimensionKey dimensionKey2( (int32_t)0x02010101), Value((int32_t)222))}), DEFAULT_DIMENSION_KEY); -void sendConfig(shared_ptr<StatsService>& service, const StatsdConfig& config) { - string str; - config.SerializeToString(&str); - std::vector<uint8_t> configAsVec(str.begin(), str.end()); - service->addConfiguration(kConfigKey, configAsVec, kCallingUid); -} - } // namespace -TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) { +// Setup for test fixture. +class AnomalyDurationDetectionE2eTest : public StatsServiceConfigTest {}; + +TEST_F(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_single_bucket) { const int num_buckets = 1; const uint64_t threshold_ns = NS_PER_SEC; auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); const uint64_t alert_id = config.alert(0).id(); const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); - sendConfig(service, config); + sendConfig(config); auto processor = service->mProcessor; ASSERT_EQ(processor->mMetricsManagers.size(), 1u); @@ -291,16 +282,14 @@ TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) { EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); } -TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) { +TEST_F(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) { const int num_buckets = 3; const uint64_t threshold_ns = NS_PER_SEC; auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); const uint64_t alert_id = config.alert(0).id(); const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); - sendConfig(service, config); + sendConfig(config); auto processor = service->mProcessor; ASSERT_EQ(processor->mMetricsManagers.size(), 1u); @@ -415,16 +404,14 @@ TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) { anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); } -TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_partial_bucket) { +TEST_F(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_partial_bucket) { const int num_buckets = 1; const uint64_t threshold_ns = NS_PER_SEC; auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); const uint64_t alert_id = config.alert(0).id(); const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); - sendConfig(service, config); + sendConfig(config); auto processor = service->mProcessor; ASSERT_EQ(processor->mMetricsManagers.size(), 1u); @@ -505,7 +492,7 @@ TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_partial_bucket) { anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); } -TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) { +TEST_F(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) { const int num_buckets = 2; const uint64_t threshold_ns = 3 * NS_PER_SEC; auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false); @@ -514,9 +501,7 @@ TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) { const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC; config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec); - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); - sendConfig(service, config); + sendConfig(config); auto processor = service->mProcessor; ASSERT_EQ(processor->mMetricsManagers.size(), 1u); diff --git a/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp b/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp index 004ddeef..ad706632 100644 --- a/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp +++ b/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp @@ -1455,6 +1455,7 @@ TEST_F(ConfigUpdateE2eTest, TestValueMetric) { EXPECT_FALSE(data.has_dimensions_in_what()); EXPECT_EQ(data.slice_by_state_size(), 0); ASSERT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(3, data.bucket_info(0).values(0).sample_size()); ValidateValueBucket(data.bucket_info(0), roundedBucketStartNs, roundedUpdateTimeNs, {20}, 0, 0); // Min screen brightness while screen on. Bucket skipped due to condition unknown. diff --git a/statsd/tests/e2e/CountMetric_e2e_test.cpp b/statsd/tests/e2e/CountMetric_e2e_test.cpp index ef183b7a..6212c43d 100644 --- a/statsd/tests/e2e/CountMetric_e2e_test.cpp +++ b/statsd/tests/e2e/CountMetric_e2e_test.cpp @@ -469,6 +469,8 @@ TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) { countMetric->set_what(appCrashMatcher.id()); countMetric->set_bucket(TimeUnit::FIVE_MINUTES); countMetric->add_slice_by_state(state.id()); + *countMetric->mutable_dimensions_in_what() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); MetricStateLink* stateLink = countMetric->add_state_link(); stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); auto fieldsInWhat = stateLink->mutable_fields_in_what(); @@ -592,7 +594,7 @@ TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) { EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); StatsLogReport::CountMetricDataWrapper countMetrics; sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(5, countMetrics.data_size()); + ASSERT_EQ(6, countMetrics.data_size()); // For each CountMetricData, check StateValue info is correct and buckets // have correct counts. @@ -601,6 +603,7 @@ TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) { EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_value()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1); ASSERT_EQ(1, data.bucket_info_size()); EXPECT_EQ(1, data.bucket_info(0).count()); @@ -609,14 +612,16 @@ TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) { EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_value()); EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(0).value()); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1); ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, data.bucket_info(0).count()); + EXPECT_EQ(1, data.bucket_info(0).count()); data = countMetrics.data(2); ASSERT_EQ(1, data.slice_by_state_size()); EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_value()); EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(0).value()); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1); ASSERT_EQ(2, data.bucket_info_size()); EXPECT_EQ(1, data.bucket_info(0).count()); EXPECT_EQ(2, data.bucket_info(1).count()); @@ -626,6 +631,7 @@ TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) { EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_value()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(0).value()); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1); ASSERT_EQ(1, data.bucket_info_size()); EXPECT_EQ(2, data.bucket_info(0).count()); @@ -633,7 +639,17 @@ TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) { ASSERT_EQ(1, data.slice_by_state_size()); EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(0).value()); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 2); + ASSERT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = countMetrics.data(5); + ASSERT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(0).value()); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 2); ASSERT_EQ(1, data.bucket_info_size()); EXPECT_EQ(1, data.bucket_info(0).count()); } @@ -663,6 +679,8 @@ TEST(CountMetricE2eTest, TestMultipleSlicedStates) { countMetric->set_bucket(TimeUnit::FIVE_MINUTES); countMetric->add_slice_by_state(state1.id()); countMetric->add_slice_by_state(state2.id()); + *countMetric->mutable_dimensions_in_what() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); MetricStateLink* stateLink = countMetric->add_state_link(); stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); auto fieldsInWhat = stateLink->mutable_fields_in_what(); @@ -821,7 +839,7 @@ TEST(CountMetricE2eTest, TestMultipleSlicedStates) { EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); StatsLogReport::CountMetricDataWrapper countMetrics; sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(6, countMetrics.data_size()); + ASSERT_EQ(7, countMetrics.data_size()); // For each CountMetricData, check StateValue info is correct and buckets // have correct counts. @@ -833,6 +851,7 @@ TEST(CountMetricE2eTest, TestMultipleSlicedStates) { EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); EXPECT_TRUE(data.slice_by_state(1).has_value()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1); ASSERT_EQ(1, data.bucket_info_size()); EXPECT_EQ(1, data.bucket_info(0).count()); @@ -844,6 +863,7 @@ TEST(CountMetricE2eTest, TestMultipleSlicedStates) { EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); EXPECT_TRUE(data.slice_by_state(1).has_value()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1); ASSERT_EQ(1, data.bucket_info_size()); EXPECT_EQ(1, data.bucket_info(0).count()); @@ -851,10 +871,11 @@ TEST(CountMetricE2eTest, TestMultipleSlicedStates) { ASSERT_EQ(2, data.slice_by_state_size()); EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_group_id()); - EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id()); + EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id()); EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); EXPECT_TRUE(data.slice_by_state(1).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(1).value()); + EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(1).value()); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1); ASSERT_EQ(1, data.bucket_info_size()); EXPECT_EQ(1, data.bucket_info(0).count()); @@ -865,9 +886,10 @@ TEST(CountMetricE2eTest, TestMultipleSlicedStates) { EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id()); EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); EXPECT_TRUE(data.slice_by_state(1).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(1).value()); + EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(1).value()); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1); ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, data.bucket_info(0).count()); + EXPECT_EQ(1, data.bucket_info(0).count()); data = countMetrics.data(4); ASSERT_EQ(2, data.slice_by_state_size()); @@ -876,21 +898,35 @@ TEST(CountMetricE2eTest, TestMultipleSlicedStates) { EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id()); EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); EXPECT_TRUE(data.slice_by_state(1).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(1).value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1); + ASSERT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).count()); + EXPECT_EQ(1, data.bucket_info(1).count()); + + data = countMetrics.data(5); + ASSERT_EQ(2, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); + EXPECT_TRUE(data.slice_by_state(1).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(1).value()); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 2); ASSERT_EQ(1, data.bucket_info_size()); EXPECT_EQ(1, data.bucket_info(0).count()); - data = countMetrics.data(5); + data = countMetrics.data(6); ASSERT_EQ(2, data.slice_by_state_size()); EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_group_id()); EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id()); EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); EXPECT_TRUE(data.slice_by_state(1).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(2, data.bucket_info(0).count()); - EXPECT_EQ(1, data.bucket_info(1).count()); + EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(1).value()); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 2); + ASSERT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); } TEST(CountMetricE2eTest, TestUploadThreshold) { @@ -1747,9 +1783,92 @@ TEST(CountMetricE2eTest, TestConditionSlicedByRepeatedUidWithUidDimension) { 1); } +TEST(CountMetricE2eTest, TestDimensionalSampling) { + ShardOffsetProvider::getInstance().setShardOffset(5); + + // Initialize config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + AtomMatcher appCrashMatcher = + CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED); + *config.add_atom_matcher() = appCrashMatcher; + + CountMetric sampledCountMetric = + createCountMetric("CountSampledAppCrashesPerUid", appCrashMatcher.id(), nullopt, {}); + *sampledCountMetric.mutable_dimensions_in_what() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); + *sampledCountMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); + sampledCountMetric.mutable_dimensional_sampling_info()->set_shard_count(2); + *config.add_count_metric() = sampledCountMetric; + + // Initialize StatsLogProcessor. + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + const uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<StatsLogProcessor> processor = CreateStatsLogProcessor( + bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap()); + + int appUid1 = 1001; // odd hash value + int appUid2 = 1002; // even hash value + int appUid3 = 1003; // odd hash value + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, appUid1)); // 0:30 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 40 * NS_PER_SEC, appUid2)); // 0:50 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, appUid3)); // 1:10 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 80 * NS_PER_SEC, appUid1)); // 1:20 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 90 * NS_PER_SEC, appUid2)); // 1:30 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 100 * NS_PER_SEC, appUid3)); // 1:40 + + // Send log events to StatsLogProcessor. + for (auto& event : events) { + processor->OnLogEvent(event.get()); + } + + // Check dump report. + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + ASSERT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + ASSERT_EQ(1, reports.reports_size()); + ASSERT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + ASSERT_EQ(2, countMetrics.data_size()); + + // Only Uid 1 and 3 are logged. (odd hash value) + (offset of 5) % (shard count of 2) = 0 + CountMetricData data = countMetrics.data(0); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, appUid1); + ValidateCountBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + bucketSizeNs, + 2); + + data = countMetrics.data(1); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, appUid3); + ValidateCountBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + bucketSizeNs, + 2); +} + } // namespace statsd } // namespace os } // namespace android #else GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif +#endif
\ No newline at end of file diff --git a/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/statsd/tests/e2e/DurationMetric_e2e_test.cpp index 32023688..4a3ab11d 100644 --- a/statsd/tests/e2e/DurationMetric_e2e_test.cpp +++ b/statsd/tests/e2e/DurationMetric_e2e_test.cpp @@ -1649,6 +1649,106 @@ TEST(DurationMetricE2eTest, TestConditionOnRepeatedEnumField) { EXPECT_EQ(baseTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); } +TEST(DurationMetricE2eTest, TestDimensionalSampling) { + ShardOffsetProvider::getInstance().setShardOffset(5); + + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher(); + AtomMatcher stopAllMatcher = CreateScheduleScheduledJobAtomMatcher(); + *config.add_atom_matcher() = stopAllMatcher; + + Predicate scheduledJobPredicate = CreateScheduledJobPredicate(); + *scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateAttributionUidDimensions(util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + SimplePredicate* simplePredicate = scheduledJobPredicate.mutable_simple_predicate(); + simplePredicate->set_stop_all(stopAllMatcher.id()); + *config.add_predicate() = scheduledJobPredicate; + + DurationMetric sampledDurationMetric = createDurationMetric( + "DurationSampledScheduledJobPerUid", scheduledJobPredicate.id(), nullopt, {}); + sampledDurationMetric.set_aggregation_type(DurationMetric::SUM); + *sampledDurationMetric.mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + *sampledDurationMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateAttributionUidDimensions(util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + sampledDurationMetric.mutable_dimensional_sampling_info()->set_shard_count(2); + *config.add_duration_metric() = sampledDurationMetric; + + const int64_t configAddedTimeNs = 1 * NS_PER_SEC; // 0:01 + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<StatsLogProcessor> processor = CreateStatsLogProcessor( + configAddedTimeNs, configAddedTimeNs, config, cfgKey, nullptr, 0, new UidMap()); + + int uid1 = 1001; // odd hash value + int uid2 = 1002; // even hash value + int uid3 = 1003; // odd hash value + + const int64_t durationStartNs1 = 20 * NS_PER_SEC; + const int64_t durationStartNs2 = 40 * NS_PER_SEC; + const int64_t durationStartNs3 = 60 * NS_PER_SEC; + const int64_t durationStartNs4 = 80 * NS_PER_SEC; + const int64_t durationEndNs1 = 100 * NS_PER_SEC; + const int64_t durationEndNs2 = 110 * NS_PER_SEC; + const int64_t stopAllNs = 130 * NS_PER_SEC; + const int64_t durationEndNs3 = 150 * NS_PER_SEC; + const int64_t durationEndNs4 = 200 * NS_PER_SEC; + + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(CreateStartScheduledJobEvent(durationStartNs1, {uid1}, {"App1"}, "job1")); + events.push_back(CreateStartScheduledJobEvent(durationStartNs2, {uid2}, {"App2"}, "job2")); + events.push_back(CreateStartScheduledJobEvent(durationStartNs3, {uid3}, {"App3"}, "job3")); + events.push_back(CreateFinishScheduledJobEvent(durationEndNs1, {uid1}, {"App1"}, "job1")); + events.push_back(CreateFinishScheduledJobEvent(durationEndNs2, {uid2}, {"App2"}, "job2")); + // This event should pass the sample check regardless of the uid. + events.push_back(CreateScheduleScheduledJobEvent(stopAllNs, {uid2}, {"App2"}, "job2")); + // These events shouldn't do anything since all jobs were stopped with the cancel event. + events.push_back(CreateFinishScheduledJobEvent(durationEndNs3, {uid3}, {"App3"}, "job3")); + + // Send log events to StatsLogProcessor. + for (auto& event : events) { + processor->OnLogEvent(event.get()); + } + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + + ASSERT_EQ(1, reports.reports_size()); + ASSERT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + StatsLogReport::DurationMetricDataWrapper durationMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), + &durationMetrics); + ASSERT_EQ(2, durationMetrics.data_size()); + + // Only Uid 1 and 3 are logged. (odd hash value) + (offset of 5) % (shard count of 2) = 0 + DurationMetricData data = durationMetrics.data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::SCHEDULED_JOB_STATE_CHANGED, + uid1); + ValidateDurationBucket(data.bucket_info(0), configAddedTimeNs, configAddedTimeNs + bucketSizeNs, + durationEndNs1 - durationStartNs1); + + data = durationMetrics.data(1); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::SCHEDULED_JOB_STATE_CHANGED, + uid3); + ValidateDurationBucket(data.bucket_info(0), configAddedTimeNs, configAddedTimeNs + bucketSizeNs, + stopAllNs - durationStartNs3); +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp index 984b72e6..fc1db28e 100644 --- a/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp +++ b/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp @@ -69,9 +69,11 @@ StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type, } // namespaces // Setup for test fixture. -class GaugeMetricE2ePulledTest : public ::testing::Test { +class GaugeMetricE2ePulledTest : public ::testing::TestWithParam<string> { void SetUp() override { FlagProvider::getInstance().overrideFuncs(&isAtLeastSFuncTrue); + FlagProvider::getInstance().overrideFlag(LIMIT_PULL_FLAG, GetParam(), + /*isBootFlag=*/true); } void TearDown() override { @@ -79,7 +81,10 @@ class GaugeMetricE2ePulledTest : public ::testing::Test { } }; -TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEvents) { +INSTANTIATE_TEST_SUITE_P(LimitPull, GaugeMetricE2ePulledTest, + testing::Values(FLAG_FALSE, FLAG_TRUE)); + +TEST_P(GaugeMetricE2ePulledTest, TestRandomSamplePulledEvents) { auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE); int64_t baseTimeNs = getElapsedRealtimeNs(); int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; @@ -219,7 +224,7 @@ TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEvents) { EXPECT_GT(data.bucket_info(5).atom(0).subsystem_sleep_state().time_millis(), 0); } -TEST_F(GaugeMetricE2ePulledTest, TestConditionChangeToTrueSamplePulledEvents) { +TEST_P(GaugeMetricE2ePulledTest, TestConditionChangeToTrueSamplePulledEvents) { auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE); int64_t baseTimeNs = getElapsedRealtimeNs(); int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; @@ -317,7 +322,7 @@ TEST_F(GaugeMetricE2ePulledTest, TestConditionChangeToTrueSamplePulledEvents) { EXPECT_GT(data.bucket_info(2).atom(1).subsystem_sleep_state().time_millis(), 0); } -TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEvent_LateAlarm) { +TEST_P(GaugeMetricE2ePulledTest, TestRandomSamplePulledEvent_LateAlarm) { auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE); int64_t baseTimeNs = getElapsedRealtimeNs(); int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; @@ -415,7 +420,7 @@ TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEvent_LateAlarm) { EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); } -TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsWithActivation) { +TEST_P(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsWithActivation) { auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false); int64_t baseTimeNs = getElapsedRealtimeNs(); @@ -460,14 +465,15 @@ TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsWithActivation) { // Check no pull occurred on metric initialization when it's not active. const int64_t metricInitTimeNs = configAddedTimeNs + 1; // 10 mins + 1 ns. processor->onStatsdInitCompleted(metricInitTimeNs); - StatsdStatsReport_PulledAtomStats pulledAtomStats = getPulledAtomStats(); + StatsdStatsReport_PulledAtomStats pulledAtomStats = + getPulledAtomStats(util::SUBSYSTEM_SLEEP_STATE); EXPECT_EQ(pulledAtomStats.atom_id(), ATOM_TAG); EXPECT_EQ(pulledAtomStats.total_pull(), 0); // Check no pull occurred on app upgrade when metric is not active. const int64_t appUpgradeTimeNs = metricInitTimeNs + 1; // 10 mins + 2 ns. processor->notifyAppUpgrade(appUpgradeTimeNs, "appName", 1000 /* uid */, 2 /* version */); - pulledAtomStats = getPulledAtomStats(); + pulledAtomStats = getPulledAtomStats(util::SUBSYSTEM_SLEEP_STATE); EXPECT_EQ(pulledAtomStats.atom_id(), ATOM_TAG); EXPECT_EQ(pulledAtomStats.total_pull(), 0); @@ -593,7 +599,7 @@ TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsWithActivation) { EXPECT_EQ(gaugeMetrics.skipped_size(), 0); } -TEST_F(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsNoCondition) { +TEST_P(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsNoCondition) { auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false); int64_t baseTimeNs = getElapsedRealtimeNs(); diff --git a/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp index f5a01582..81192b2f 100644 --- a/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp +++ b/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp @@ -414,6 +414,93 @@ TEST_F(GaugeMetricE2ePushedTest, TestRepeatedFieldsForPushedEvent) { } } +TEST_F(GaugeMetricE2ePushedTest, TestDimensionalSampling) { + ShardOffsetProvider::getInstance().setShardOffset(5); + + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + AtomMatcher appCrashMatcher = + CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED); + *config.add_atom_matcher() = appCrashMatcher; + + GaugeMetric sampledGaugeMetric = + createGaugeMetric("GaugeSampledAppCrashesPerUid", appCrashMatcher.id(), + GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt); + *sampledGaugeMetric.mutable_dimensions_in_what() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /* uid */}); + *sampledGaugeMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); + sampledGaugeMetric.mutable_dimensional_sampling_info()->set_shard_count(2); + *config.add_gauge_metric() = sampledGaugeMetric; + + const int64_t configAddedTimeNs = 10 * NS_PER_SEC; // 0:10 + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<StatsLogProcessor> processor = CreateStatsLogProcessor( + configAddedTimeNs, configAddedTimeNs, config, cfgKey, nullptr, 0, new UidMap()); + + int appUid1 = 1001; // odd hash value + int appUid2 = 1002; // even hash value + int appUid3 = 1003; // odd hash value + + const int64_t gaugeEventTimeNs1 = configAddedTimeNs + 20 * NS_PER_SEC; + const int64_t gaugeEventTimeNs2 = configAddedTimeNs + 40 * NS_PER_SEC; + const int64_t gaugeEventTimeNs3 = configAddedTimeNs + 60 * NS_PER_SEC; + const int64_t gaugeEventTimeNs4 = configAddedTimeNs + 100 * NS_PER_SEC; + const int64_t gaugeEventTimeNs5 = configAddedTimeNs + 110 * NS_PER_SEC; + const int64_t gaugeEventTimeNs6 = configAddedTimeNs + 150 * NS_PER_SEC; + + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(CreateAppCrashOccurredEvent(gaugeEventTimeNs1, appUid1)); // 0:30 + events.push_back(CreateAppCrashOccurredEvent(gaugeEventTimeNs2, appUid2)); // 0:50 + events.push_back(CreateAppCrashOccurredEvent(gaugeEventTimeNs3, appUid3)); // 1:10 + events.push_back(CreateAppCrashOccurredEvent(gaugeEventTimeNs4, appUid1)); // 1:50 + events.push_back(CreateAppCrashOccurredEvent(gaugeEventTimeNs5, appUid2)); // 2:00 + events.push_back(CreateAppCrashOccurredEvent(gaugeEventTimeNs6, appUid3)); // 2:40 + + // Send log events to StatsLogProcessor. + for (auto& event : events) { + processor->OnLogEvent(event.get()); + } + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + backfillAggregatedAtoms(&reports); + + ASSERT_EQ(1, reports.reports_size()); + ASSERT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_gauge_metrics()); + StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); + ASSERT_EQ(2, gaugeMetrics.data_size()); + + // Only Uid 1 and 3 are logged. (odd hash value) + (offset of 5) % (shard count of 2) = 0 + GaugeMetricData data = gaugeMetrics.data(0); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, appUid1); + ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs, + configAddedTimeNs + bucketSizeNs, + {gaugeEventTimeNs1, gaugeEventTimeNs4}); + + data = gaugeMetrics.data(1); + ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, appUid3); + ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs, + configAddedTimeNs + bucketSizeNs, + {gaugeEventTimeNs3, gaugeEventTimeNs6}); +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/statsd/tests/e2e/KllMetric_e2e_test.cpp b/statsd/tests/e2e/KllMetric_e2e_test.cpp index 6f2530f4..a6100c29 100644 --- a/statsd/tests/e2e/KllMetric_e2e_test.cpp +++ b/statsd/tests/e2e/KllMetric_e2e_test.cpp @@ -25,6 +25,21 @@ namespace statsd { using namespace std; +namespace { + +unique_ptr<LogEvent> CreateTestAtomReportedEvent(const uint64_t timestampNs, const long longField, + const string& stringField) { + return CreateTestAtomReportedEvent( + timestampNs, /* attributionUids */ {1001}, + /* attributionTags */ {"app1"}, /* intField */ 0, longField, /* floatField */ 0.0f, + stringField, /* boolField */ false, TestAtomReported::OFF, /* bytesField */ {}, + /* repeatedIntField */ {}, /* repeatedLongField */ {}, /* repeatedFloatField */ {}, + /* repeatedStringField */ {}, /* repeatedBoolField */ {}, + /* repeatedBoolFieldLength */ 0, /* repeatedEnumField */ {}); +} + +} // anonymous namespace. + class KllMetricE2eTest : public ::testing::Test { protected: void SetUp() override { @@ -90,6 +105,75 @@ TEST_F(KllMetricE2eTest, TestSimpleMetric) { EXPECT_EQ(metricReport.kll_metrics().skipped_size(), 0); } +TEST_F(KllMetricE2eTest, TestMetricWithDimensions) { + whatMatcher = CreateSimpleAtomMatcher("TestAtomReported", util::TEST_ATOM_REPORTED); + metric = createKllMetric("TestAtomMetric", whatMatcher, /* kllField */ 3, + /* condition */ nullopt); + + *metric.mutable_dimensions_in_what() = + CreateDimensions(util::TEST_ATOM_REPORTED, {5 /* string_field */}); + + config.clear_atom_matcher(); + *config.add_atom_matcher() = whatMatcher; + + config.clear_kll_metric(); + *config.add_kll_metric() = metric; + + events.clear(); + events.push_back(CreateTestAtomReportedEvent(bucketStartTimeNs + 5 * NS_PER_SEC, 5l, "dim_1")); + events.push_back(CreateTestAtomReportedEvent(bucketStartTimeNs + 15 * NS_PER_SEC, 6l, "dim_2")); + events.push_back(CreateTestAtomReportedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 7l, "dim_1")); + + const sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); + + for (auto& event : events) { + processor->OnLogEvent(event.get()); + } + + uint64_t dumpTimeNs = bucketStartTimeNs + bucketSizeNs; + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(key, dumpTimeNs, /*include_current_bucket*/ false, true, ADB_DUMP, FAST, + &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + ASSERT_EQ(reports.reports_size(), 1); + + ConfigMetricsReport report = reports.reports(0); + ASSERT_EQ(report.metrics_size(), 1); + StatsLogReport metricReport = report.metrics(0); + EXPECT_EQ(metricReport.metric_id(), metric.id()); + EXPECT_TRUE(metricReport.has_kll_metrics()); + ASSERT_EQ(metricReport.kll_metrics().data_size(), 2); + + KllMetricData data = metricReport.kll_metrics().data(0); + ASSERT_EQ(data.bucket_info_size(), 1); + KllBucketInfo bucket = data.bucket_info(0); + EXPECT_EQ(bucket.start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(bucket.end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(bucket.sketches_size(), 1); + EXPECT_EQ(metricReport.kll_metrics().skipped_size(), 0); + EXPECT_EQ(data.dimensions_in_what().field(), util::TEST_ATOM_REPORTED); + ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 5); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), "dim_1"); + + data = metricReport.kll_metrics().data(1); + ASSERT_EQ(data.bucket_info_size(), 1); + bucket = data.bucket_info(0); + EXPECT_EQ(bucket.start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(bucket.end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(bucket.sketches_size(), 1); + EXPECT_EQ(metricReport.kll_metrics().skipped_size(), 0); + EXPECT_EQ(data.dimensions_in_what().field(), util::TEST_ATOM_REPORTED); + ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 5); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), "dim_2"); +} + TEST_F(KllMetricE2eTest, TestInitWithKllFieldPositionALL) { // Create config. StatsdConfig config; @@ -120,6 +204,97 @@ TEST_F(KllMetricE2eTest, TestInitWithKllFieldPositionALL) { ASSERT_EQ(0, processor->mMetricsManagers.size()); } +TEST_F(KllMetricE2eTest, TestDimensionalSampling) { + ShardOffsetProvider::getInstance().setShardOffset(5); + + // Create config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + AtomMatcher bleScanResultReceivedMatcher = CreateSimpleAtomMatcher( + "BleScanResultReceivedAtomMatcher", util::BLE_SCAN_RESULT_RECEIVED); + *config.add_atom_matcher() = bleScanResultReceivedMatcher; + + // Create kll metric. + KllMetric sampledKllMetric = + createKllMetric("KllSampledBleScanResultsPerUid", bleScanResultReceivedMatcher, + /*num_results=*/2, nullopt); + *sampledKllMetric.mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::BLE_SCAN_RESULT_RECEIVED, {Position::FIRST}); + *sampledKllMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateAttributionUidDimensions(util::BLE_SCAN_RESULT_RECEIVED, {Position::FIRST}); + sampledKllMetric.mutable_dimensional_sampling_info()->set_shard_count(2); + *config.add_kll_metric() = sampledKllMetric; + + // Initialize StatsLogProcessor. + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<StatsLogProcessor> processor = CreateStatsLogProcessor( + bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap()); + + int appUid1 = 1001; // odd hash value + int appUid2 = 1002; // even hash value + int appUid3 = 1003; // odd hash value + std::vector<std::unique_ptr<LogEvent>> events; + + events.push_back(CreateBleScanResultReceivedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, + {appUid1}, {"tag1"}, 10)); + events.push_back(CreateBleScanResultReceivedEvent(bucketStartTimeNs + 40 * NS_PER_SEC, + {appUid2}, {"tag2"}, 10)); + events.push_back(CreateBleScanResultReceivedEvent(bucketStartTimeNs + 60 * NS_PER_SEC, + {appUid3}, {"tag3"}, 10)); + + events.push_back(CreateBleScanResultReceivedEvent(bucketStartTimeNs + 120 * NS_PER_SEC, + {appUid1}, {"tag1"}, 11)); + events.push_back(CreateBleScanResultReceivedEvent(bucketStartTimeNs + 140 * NS_PER_SEC, + {appUid2}, {"tag2"}, 12)); + events.push_back(CreateBleScanResultReceivedEvent(bucketStartTimeNs + 160 * NS_PER_SEC, + {appUid3}, {"tag3"}, 13)); + + // Send log events to StatsLogProcessor. + for (auto& event : events) { + processor->OnLogEvent(event.get()); + } + + // Check dump report. + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + ASSERT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + backfillAggregatedAtoms(&reports); + + ConfigMetricsReport report = reports.reports(0); + ASSERT_EQ(report.metrics_size(), 1); + StatsLogReport metricReport = report.metrics(0); + EXPECT_EQ(metricReport.metric_id(), sampledKllMetric.id()); + EXPECT_TRUE(metricReport.has_kll_metrics()); + StatsLogReport::KllMetricDataWrapper kllMetrics; + sortMetricDataByDimensionsValue(metricReport.kll_metrics(), &kllMetrics); + ASSERT_EQ(kllMetrics.data_size(), 2); + EXPECT_EQ(kllMetrics.skipped_size(), 0); + + // Only Uid 1 and 3 are logged. (odd hash value) + (offset of 5) % (shard count of 2) = 0 + KllMetricData data = kllMetrics.data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::BLE_SCAN_RESULT_RECEIVED, + appUid1); + ValidateKllBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + bucketSizeNs, {2}, + 0); + + data = kllMetrics.data(1); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::BLE_SCAN_RESULT_RECEIVED, + appUid3); + ValidateKllBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + bucketSizeNs, {2}, + 0); +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/statsd/tests/e2e/PartialBucket_e2e_test.cpp index 176fd9ca..46d0d5d4 100644 --- a/statsd/tests/e2e/PartialBucket_e2e_test.cpp +++ b/statsd/tests/e2e/PartialBucket_e2e_test.cpp @@ -24,7 +24,7 @@ #include "src/stats_log_util.h" #include "tests/statsd_test_util.h" -using::ndk::SharedRefBase; +using ::ndk::SharedRefBase; using std::shared_ptr; namespace android { @@ -34,27 +34,6 @@ namespace statsd { #ifdef __ANDROID__ namespace { const string kApp1 = "app1.sharing.1"; -const int kConfigKey = 789130123; // Randomly chosen to avoid collisions with existing configs. -const int kCallingUid = 0; // Randomly chosen - -void SendConfig(shared_ptr<StatsService>& service, const StatsdConfig& config) { - string str; - config.SerializeToString(&str); - std::vector<uint8_t> configAsVec(str.begin(), str.end()); - service->addConfiguration(kConfigKey, configAsVec, kCallingUid); -} - -ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp, - bool include_current = false) { - vector<uint8_t> output; - ConfigKey configKey(AIBinder_getCallingUid(), kConfigKey); - processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/, - true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &output); - ConfigMetricsReportList reports; - reports.ParseFromArray(output.data(), output.size()); - EXPECT_EQ(1, reports.reports_size()); - return reports.reports(kCallingUid); -} StatsdConfig MakeCountMetricConfig(const std::optional<bool> splitBucket) { StatsdConfig config; @@ -122,26 +101,25 @@ StatsdConfig MakeGaugeMetricConfig(int64_t minTime) { } } // anonymous namespace -TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) { - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); - SendConfig(service, MakeCountMetricConfig({true})); +// Setup for test fixture. +class PartialBucketE2eTest : public StatsServiceConfigTest {}; + +TEST_F(PartialBucketE2eTest, TestCountMetricWithoutSplit) { + sendConfig(MakeCountMetricConfig({true})); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get()); service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 2, 100).get()); - ConfigMetricsReport report = GetReports(service->mProcessor, start + 3); + ConfigMetricsReport report = getReports(service->mProcessor, start + 3); // Expect no metrics since the bucket has not finished yet. ASSERT_EQ(1, report.metrics_size()); ASSERT_EQ(0, report.metrics(0).count_metrics().data_size()); } -TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) { - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); - SendConfig(service, MakeCountMetricConfig({true})); +TEST_F(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) { + sendConfig(MakeCountMetricConfig({true})); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. @@ -154,15 +132,13 @@ TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) { // Goes into the second bucket. service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); - ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); + ConfigMetricsReport report = getReports(service->mProcessor, start + 4); ASSERT_EQ(1, report.metrics_size()); ASSERT_EQ(0, report.metrics(0).count_metrics().data_size()); } -TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); - SendConfig(service, MakeCountMetricConfig({true})); +TEST_F(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { + sendConfig(MakeCountMetricConfig({true})); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, @@ -175,7 +151,7 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { // Goes into the second bucket. service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); - ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); + ConfigMetricsReport report = getReports(service->mProcessor, start + 4); backfillStartEndTimestamp(&report); ASSERT_EQ(1, report.metrics_size()); @@ -194,10 +170,8 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); } -TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); - SendConfig(service, MakeCountMetricConfig({true})); +TEST_F(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { + sendConfig(MakeCountMetricConfig({true})); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, @@ -209,7 +183,7 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { // Goes into the second bucket. service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); - ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); + ConfigMetricsReport report = getReports(service->mProcessor, start + 4); backfillStartEndTimestamp(&report); ASSERT_EQ(1, report.metrics_size()); @@ -228,10 +202,8 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); } -TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot) { - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); - SendConfig(service, MakeCountMetricConfig(std::nullopt)); +TEST_F(PartialBucketE2eTest, TestCountMetricSplitOnBoot) { + sendConfig(MakeCountMetricConfig(std::nullopt)); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. @@ -242,7 +214,7 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot) { // Goes into the second bucket. service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3 * NS_PER_SEC, 100).get()); - ConfigMetricsReport report = GetReports(service->mProcessor, start + 4 * NS_PER_SEC); + ConfigMetricsReport report = getReports(service->mProcessor, start + 4 * NS_PER_SEC); backfillStartEndTimestamp(&report); ASSERT_EQ(1, report.metrics_size()); @@ -258,11 +230,9 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot) { EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); } -TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnUpgradeWhenDisabled) { - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); +TEST_F(PartialBucketE2eTest, TestCountMetricNoSplitOnUpgradeWhenDisabled) { StatsdConfig config = MakeCountMetricConfig({false}); - SendConfig(service, config); + sendConfig(config); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, @@ -276,7 +246,7 @@ TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnUpgradeWhenDisabled) { service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); ConfigMetricsReport report = - GetReports(service->mProcessor, start + 4, /*include_current=*/true); + getReports(service->mProcessor, start + 4, /*include_current=*/true); backfillStartEndTimestamp(&report); ASSERT_EQ(1, report.metrics_size()); @@ -287,16 +257,14 @@ TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnUpgradeWhenDisabled) { EXPECT_EQ(bucketInfo.count(), 2); } -TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); +TEST_F(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { service->mPullerManager->RegisterPullAtomCallback( /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, SharedRefBase::make<FakeSubsystemSleepCallback>()); // Partial buckets don't occur when app is first installed. service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""), /* certificateHash */ {}); - SendConfig(service, MakeValueMetricConfig(0)); + sendConfig(MakeValueMetricConfig(0)); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. @@ -306,7 +274,7 @@ TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { String16(""), /* certificateHash */ {}); ConfigMetricsReport report = - GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); + getReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); backfillStartEndTimestamp(&report); ASSERT_EQ(1, report.metrics_size()); @@ -319,16 +287,14 @@ TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { report.metrics(0).value_metrics().data(0).bucket_info(1).end_bucket_elapsed_nanos()); } -TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); +TEST_F(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { service->mPullerManager->RegisterPullAtomCallback( /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, SharedRefBase::make<FakeSubsystemSleepCallback>()); // Partial buckets don't occur when app is first installed. service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""), /* certificateHash */ {}); - SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */)); + sendConfig(MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */)); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. @@ -338,7 +304,7 @@ TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { String16(""), /* certificateHash */ {}); ConfigMetricsReport report = - GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); + getReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); backfillStartEndTimestamp(&report); ASSERT_EQ(1, report.metrics_size()); @@ -352,11 +318,9 @@ TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { ASSERT_EQ(1, report.metrics(0).value_metrics().data(0).bucket_info_size()); } -TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket) { - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); +TEST_F(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket) { // Initial pull will fail since puller is not registered. - SendConfig(service, MakeValueMetricConfig(0)); + sendConfig(MakeValueMetricConfig(0)); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. @@ -369,7 +333,7 @@ TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket) { service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); + ConfigMetricsReport report = getReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); backfillStartEndTimestamp(&report); // First bucket is dropped due to the initial pull failing @@ -386,16 +350,14 @@ TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket) { report.metrics(0).value_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos()); } -TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) { - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); +TEST_F(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) { service->mPullerManager->RegisterPullAtomCallback( /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, SharedRefBase::make<FakeSubsystemSleepCallback>()); // Partial buckets don't occur when app is first installed. service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""), /* certificateHash */ {}); - SendConfig(service, MakeGaugeMetricConfig(0)); + sendConfig(MakeGaugeMetricConfig(0)); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. @@ -403,7 +365,7 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) { service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"), String16(""), /* certificateHash */ {}); - ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); + ConfigMetricsReport report = getReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); backfillStartEndTimestamp(&report); ASSERT_EQ(1, report.metrics_size()); ASSERT_EQ(0, report.metrics(0).gauge_metrics().skipped_size()); @@ -412,16 +374,14 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) { ASSERT_EQ(2, report.metrics(0).gauge_metrics().data(0).bucket_info_size()); } -TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); +TEST_F(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { // Partial buckets don't occur when app is first installed. service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""), /* certificateHash */ {}); service->mPullerManager->RegisterPullAtomCallback( /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, SharedRefBase::make<FakeSubsystemSleepCallback>()); - SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */)); + sendConfig(MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */)); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. @@ -431,7 +391,7 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { String16(""), /* certificateHash */ {}); ConfigMetricsReport report = - GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); + getReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); backfillStartEndTimestamp(&report); ASSERT_EQ(1, report.metrics_size()); ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size()); @@ -443,11 +403,9 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { ASSERT_EQ(1, report.metrics(0).gauge_metrics().data(0).bucket_info_size()); } -TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket) { - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); +TEST_F(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket) { // Initial pull will fail since puller hasn't been registered. - SendConfig(service, MakeGaugeMetricConfig(0)); + sendConfig(MakeGaugeMetricConfig(0)); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. @@ -460,7 +418,7 @@ TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket) { service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); + ConfigMetricsReport report = getReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); backfillStartEndTimestamp(&report); ASSERT_EQ(1, report.metrics_size()); @@ -474,11 +432,9 @@ TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket) { report.metrics(0).gauge_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos()); } -TEST(PartialBucketE2eTest, TestCountMetricNoSplitByDefault) { - shared_ptr<StatsService> service = - SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); +TEST_F(PartialBucketE2eTest, TestCountMetricNoSplitByDefault) { StatsdConfig config = MakeCountMetricConfig({nullopt}); // Do not set the value in the metric. - SendConfig(service, config); + sendConfig(config); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, @@ -492,7 +448,7 @@ TEST(PartialBucketE2eTest, TestCountMetricNoSplitByDefault) { service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); ConfigMetricsReport report = - GetReports(service->mProcessor, start + 4, /*include_current=*/true); + getReports(service->mProcessor, start + 4, /*include_current=*/true); backfillStartEndTimestamp(&report); ASSERT_EQ(1, report.metrics_size()); diff --git a/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp index 385a4730..810b550d 100644 --- a/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp +++ b/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp @@ -15,12 +15,13 @@ #include <android/binder_interface_utils.h> #include <gtest/gtest.h> +#include <vector> + +#include "flags/FlagProvider.h" #include "src/StatsLogProcessor.h" #include "src/stats_log_util.h" #include "tests/statsd_test_util.h" -#include <vector> - using ::ndk::SharedRefBase; namespace android { @@ -143,6 +144,20 @@ StatsdConfig CreateStatsdConfigWithStates() { } // namespace +// Setup for test fixture. +class ValueMetricE2eTest : public testing::TestWithParam<string> { + void SetUp() override { + FlagProvider::getInstance().overrideFlag(LIMIT_PULL_FLAG, GetParam(), + /*isBootFlag=*/true); + } + + void TearDown() override { + FlagProvider::getInstance().resetOverrides(); + } +}; + +INSTANTIATE_TEST_SUITE_P(LimitPull, ValueMetricE2eTest, testing::Values(FLAG_FALSE, FLAG_TRUE)); + /** * Tests the initial condition and condition after the first log events for * value metrics with either a combination condition or simple condition. @@ -202,7 +217,7 @@ TEST(ValueMetricE2eTest, TestInitialConditionChanges) { EXPECT_EQ(ConditionState::kTrue, metricProducer2->mCondition); } -TEST(ValueMetricE2eTest, TestPulledEvents) { +TEST_P(ValueMetricE2eTest, TestPulledEvents) { auto config = CreateStatsdConfig(); int64_t baseTimeNs = getElapsedRealtimeNs(); int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; @@ -303,9 +318,26 @@ TEST(ValueMetricE2eTest, TestPulledEvents) { EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos()); EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos()); ASSERT_EQ(1, data.bucket_info(3).values_size()); + + valueMetrics = reports.reports(0).metrics(0).value_metrics(); + ASSERT_EQ(2, valueMetrics.skipped_size()); + + StatsLogReport::SkippedBuckets skipped = valueMetrics.skipped(0); + EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, skipped.drop_event(0).drop_reason()); + EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 2 * bucketSizeNs)), + skipped.start_bucket_elapsed_nanos()); + EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 3 * bucketSizeNs)), + skipped.end_bucket_elapsed_nanos()); + + skipped = valueMetrics.skipped(1); + EXPECT_EQ(BucketDropReason::NO_DATA, skipped.drop_event(0).drop_reason()); + EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)), + skipped.start_bucket_elapsed_nanos()); + EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 6 * bucketSizeNs)), + skipped.end_bucket_elapsed_nanos()); } -TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) { +TEST_P(ValueMetricE2eTest, TestPulledEvents_LateAlarm) { auto config = CreateStatsdConfig(); int64_t baseTimeNs = getElapsedRealtimeNs(); // 10 mins == 2 bucket durations. @@ -406,9 +438,33 @@ TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) { EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); ASSERT_EQ(1, data.bucket_info(2).values_size()); + + valueMetrics = reports.reports(0).metrics(0).value_metrics(); + ASSERT_EQ(3, valueMetrics.skipped_size()); + + StatsLogReport::SkippedBuckets skipped = valueMetrics.skipped(0); + EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, skipped.drop_event(0).drop_reason()); + EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 2 * bucketSizeNs)), + skipped.start_bucket_elapsed_nanos()); + EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)), + skipped.end_bucket_elapsed_nanos()); + + skipped = valueMetrics.skipped(1); + EXPECT_EQ(BucketDropReason::NO_DATA, skipped.drop_event(0).drop_reason()); + EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 6 * bucketSizeNs)), + skipped.start_bucket_elapsed_nanos()); + EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 7 * bucketSizeNs)), + skipped.end_bucket_elapsed_nanos()); + + skipped = valueMetrics.skipped(2); + EXPECT_EQ(BucketDropReason::NO_DATA, skipped.drop_event(0).drop_reason()); + EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 7 * bucketSizeNs)), + skipped.start_bucket_elapsed_nanos()); + EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 8 * bucketSizeNs)), + skipped.end_bucket_elapsed_nanos()); } -TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) { +TEST_P(ValueMetricE2eTest, TestPulledEvents_WithActivation) { auto config = CreateStatsdConfig(false); int64_t baseTimeNs = getElapsedRealtimeNs(); int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; @@ -454,7 +510,8 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) { processor->onStatsdInitCompleted(metricInitTimeNs); // Check no pull occurred since metric not active. - StatsdStatsReport_PulledAtomStats pulledAtomStats = getPulledAtomStats(); + StatsdStatsReport_PulledAtomStats pulledAtomStats = + getPulledAtomStats(util::SUBSYSTEM_SLEEP_STATE); EXPECT_EQ(pulledAtomStats.atom_id(), util::SUBSYSTEM_SLEEP_STATE); EXPECT_EQ(pulledAtomStats.total_pull(), 0); @@ -477,7 +534,7 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) { processor->notifyAppUpgrade(appUpgradeTimeNs, "appName", 1000 /* uid */, 2 /* version */); // Check no pull occurred since metric not active. - pulledAtomStats = getPulledAtomStats(); + pulledAtomStats = getPulledAtomStats(util::SUBSYSTEM_SLEEP_STATE); EXPECT_EQ(pulledAtomStats.atom_id(), util::SUBSYSTEM_SLEEP_STATE); EXPECT_EQ(pulledAtomStats.total_pull(), 0); @@ -498,7 +555,7 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) { buffer.clear(); processor->onDumpReport(cfgKey, dumpReportTimeNs, true /* include_current_partial_bucket */, true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &buffer); - pulledAtomStats = getPulledAtomStats(); + pulledAtomStats = getPulledAtomStats(util::SUBSYSTEM_SLEEP_STATE); EXPECT_EQ(pulledAtomStats.atom_id(), util::SUBSYSTEM_SLEEP_STATE); EXPECT_EQ(pulledAtomStats.total_pull(), 0); diff --git a/statsd/tests/guardrail/StatsdStats_test.cpp b/statsd/tests/guardrail/StatsdStats_test.cpp index 81fcbe95..cc4d7c4d 100644 --- a/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/statsd/tests/guardrail/StatsdStats_test.cpp @@ -13,12 +13,15 @@ // limitations under the License. #include "src/guardrail/StatsdStats.h" -#include "statslog_statsdtest.h" -#include "tests/statsd_test_util.h" #include <gtest/gtest.h> + #include <vector> +#include "src/metrics/parsing_utils/metrics_manager_util.h" +#include "statslog_statsdtest.h" +#include "tests/statsd_test_util.h" + #ifdef __ANDROID__ namespace android { @@ -35,7 +38,7 @@ TEST(StatsdStatsTest, TestValidConfigAdd) { const int matchersCount = 30; const int alertsCount = 10; stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {}, - true /*valid config*/); + nullopt /*valid config*/); vector<uint8_t> output; stats.dumpStats(&output, false /*reset stats*/); @@ -51,6 +54,7 @@ TEST(StatsdStatsTest, TestValidConfigAdd) { EXPECT_EQ(matchersCount, configReport.matcher_count()); EXPECT_EQ(alertsCount, configReport.alert_count()); EXPECT_EQ(true, configReport.is_valid()); + EXPECT_FALSE(configReport.has_invalid_config_reason()); EXPECT_FALSE(configReport.has_deletion_time_sec()); } @@ -61,8 +65,99 @@ TEST(StatsdStatsTest, TestInvalidConfigAdd) { const int conditionsCount = 20; const int matchersCount = 30; const int alertsCount = 10; + optional<InvalidConfigReason> invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_UNKNOWN, 1); + invalidConfigReason->stateId = 2; + invalidConfigReason->alertId = 3; + invalidConfigReason->alarmId = 4; + invalidConfigReason->subscriptionId = 5; + invalidConfigReason->matcherIds.push_back(6); + invalidConfigReason->matcherIds.push_back(7); + invalidConfigReason->conditionIds.push_back(8); + invalidConfigReason->conditionIds.push_back(9); + invalidConfigReason->conditionIds.push_back(10); + stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {}, + invalidConfigReason /*bad config*/); + vector<uint8_t> output; + stats.dumpStats(&output, false); + + StatsdStatsReport report; + bool good = report.ParseFromArray(&output[0], output.size()); + EXPECT_TRUE(good); + ASSERT_EQ(1, report.config_stats_size()); + const auto& configReport = report.config_stats(0); + // The invalid config should be put into icebox with a deletion time. + EXPECT_TRUE(configReport.has_deletion_time_sec()); + EXPECT_TRUE(configReport.has_invalid_config_reason()); + EXPECT_EQ(configReport.invalid_config_reason().reason(), INVALID_CONFIG_REASON_UNKNOWN); + EXPECT_EQ(configReport.invalid_config_reason().metric_id(), 1); + EXPECT_EQ(configReport.invalid_config_reason().state_id(), 2); + EXPECT_EQ(configReport.invalid_config_reason().alert_id(), 3); + EXPECT_EQ(configReport.invalid_config_reason().alarm_id(), 4); + EXPECT_EQ(configReport.invalid_config_reason().subscription_id(), 5); + EXPECT_EQ(configReport.invalid_config_reason().matcher_id_size(), 2); + EXPECT_EQ(configReport.invalid_config_reason().matcher_id(0), 6); + EXPECT_EQ(configReport.invalid_config_reason().matcher_id(1), 7); + EXPECT_EQ(configReport.invalid_config_reason().condition_id_size(), 3); + EXPECT_EQ(configReport.invalid_config_reason().condition_id(0), 8); + EXPECT_EQ(configReport.invalid_config_reason().condition_id(1), 9); + EXPECT_EQ(configReport.invalid_config_reason().condition_id(2), 10); +} + +TEST(StatsdStatsTest, TestInvalidConfigMissingMetricId) { + StatsdStats stats; + ConfigKey key(0, 12345); + const int metricsCount = 10; + const int conditionsCount = 20; + const int matchersCount = 30; + const int alertsCount = 10; + optional<InvalidConfigReason> invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_SUBSCRIPTION_SUBSCRIBER_INFO_MISSING); + invalidConfigReason->stateId = 1; + invalidConfigReason->alertId = 2; + invalidConfigReason->alarmId = 3; + invalidConfigReason->subscriptionId = 4; + invalidConfigReason->matcherIds.push_back(5); + invalidConfigReason->conditionIds.push_back(6); + invalidConfigReason->conditionIds.push_back(7); + stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {}, + invalidConfigReason /*bad config*/); + vector<uint8_t> output; + stats.dumpStats(&output, false); + + StatsdStatsReport report; + bool good = report.ParseFromArray(&output[0], output.size()); + EXPECT_TRUE(good); + ASSERT_EQ(1, report.config_stats_size()); + const auto& configReport = report.config_stats(0); + // The invalid config should be put into icebox with a deletion time. + EXPECT_TRUE(configReport.has_deletion_time_sec()); + EXPECT_TRUE(configReport.has_invalid_config_reason()); + EXPECT_EQ(configReport.invalid_config_reason().reason(), + INVALID_CONFIG_REASON_SUBSCRIPTION_SUBSCRIBER_INFO_MISSING); + EXPECT_FALSE(configReport.invalid_config_reason().has_metric_id()); + EXPECT_EQ(configReport.invalid_config_reason().state_id(), 1); + EXPECT_EQ(configReport.invalid_config_reason().alert_id(), 2); + EXPECT_EQ(configReport.invalid_config_reason().alarm_id(), 3); + EXPECT_EQ(configReport.invalid_config_reason().subscription_id(), 4); + EXPECT_EQ(configReport.invalid_config_reason().matcher_id_size(), 1); + EXPECT_EQ(configReport.invalid_config_reason().matcher_id(0), 5); + EXPECT_EQ(configReport.invalid_config_reason().condition_id_size(), 2); + EXPECT_EQ(configReport.invalid_config_reason().condition_id(0), 6); + EXPECT_EQ(configReport.invalid_config_reason().condition_id(1), 7); +} + +TEST(StatsdStatsTest, TestInvalidConfigOnlyMetricId) { + StatsdStats stats; + ConfigKey key(0, 12345); + const int metricsCount = 10; + const int conditionsCount = 20; + const int matchersCount = 30; + const int alertsCount = 10; + optional<InvalidConfigReason> invalidConfigReason = + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_NOT_IN_PREV_CONFIG, 1); stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {}, - false /*bad config*/); + invalidConfigReason /*bad config*/); vector<uint8_t> output; stats.dumpStats(&output, false); @@ -73,6 +168,16 @@ TEST(StatsdStatsTest, TestInvalidConfigAdd) { const auto& configReport = report.config_stats(0); // The invalid config should be put into icebox with a deletion time. EXPECT_TRUE(configReport.has_deletion_time_sec()); + EXPECT_TRUE(configReport.has_invalid_config_reason()); + EXPECT_EQ(configReport.invalid_config_reason().reason(), + INVALID_CONFIG_REASON_METRIC_NOT_IN_PREV_CONFIG); + EXPECT_EQ(configReport.invalid_config_reason().metric_id(), 1); + EXPECT_FALSE(configReport.invalid_config_reason().has_state_id()); + EXPECT_FALSE(configReport.invalid_config_reason().has_alert_id()); + EXPECT_FALSE(configReport.invalid_config_reason().has_alarm_id()); + EXPECT_FALSE(configReport.invalid_config_reason().has_subscription_id()); + EXPECT_EQ(configReport.invalid_config_reason().matcher_id_size(), 0); + EXPECT_EQ(configReport.invalid_config_reason().condition_id_size(), 0); } TEST(StatsdStatsTest, TestConfigRemove) { @@ -83,7 +188,7 @@ TEST(StatsdStatsTest, TestConfigRemove) { const int matchersCount = 30; const int alertsCount = 10; stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {}, - true); + nullopt); vector<uint8_t> output; stats.dumpStats(&output, false); StatsdStatsReport report; @@ -105,7 +210,7 @@ TEST(StatsdStatsTest, TestConfigRemove) { TEST(StatsdStatsTest, TestSubStats) { StatsdStats stats; ConfigKey key(0, 12345); - stats.noteConfigReceived(key, 2, 3, 4, 5, {std::make_pair(123, 456)}, true); + stats.noteConfigReceived(key, 2, 3, 4, 5, {std::make_pair(123, 456)}, nullopt); stats.noteMatcherMatched(key, StringToId("matcher1")); stats.noteMatcherMatched(key, StringToId("matcher1")); @@ -393,7 +498,7 @@ TEST(StatsdStatsTest, TestTimestampThreshold) { timestamps.push_back(i); } ConfigKey key(0, 12345); - stats.noteConfigReceived(key, 2, 3, 4, 5, {}, true); + stats.noteConfigReceived(key, 2, 3, 4, 5, {}, nullopt); for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) { stats.noteDataDropped(key, timestamps[i]); diff --git a/statsd/tests/metrics/EventMetricProducer_test.cpp b/statsd/tests/metrics/EventMetricProducer_test.cpp index 192848ec..d5fe9327 100644 --- a/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -41,11 +41,15 @@ namespace { const ConfigKey kConfigKey(0, 12345); const uint64_t protoHash = 0x1234567890; -void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str) { +void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str, + vector<uint8_t>* bytesField = nullptr) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, atomId); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); AStatsEvent_writeString(statsEvent, str.c_str()); + if (bytesField != nullptr) { + AStatsEvent_writeByteArray(statsEvent, bytesField->data(), bytesField->size()); + } parseStatsEventToLogEvent(statsEvent, logEvent); } @@ -241,6 +245,58 @@ TEST_F(EventMetricProducerTest, TestOneAtomTagAggregatedEvents) { } } +TEST_F(EventMetricProducerTest, TestBytesFieldAggregatedEvents) { + int64_t bucketStartTimeNs = 10000000000; + int tagId = 1; + + EventMetric metric; + metric.set_id(1); + + vector<uint8_t> bytesField1{10, 20, 30}; + vector<uint8_t> bytesField2{10, 20, 30, 40}; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, tagId, bucketStartTimeNs + 10, "111", &bytesField1); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, tagId, bucketStartTimeNs + 20, "111", &bytesField1); + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event3, tagId, bucketStartTimeNs + 30, "111", &bytesField1); + + LogEvent event4(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event4, tagId, bucketStartTimeNs + 40, "111", &bytesField2); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, protoHash, bucketStartTimeNs); + + eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); + eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); + eventProducer.onMatchedLogEvent(1 /*matcher index*/, event3); + eventProducer.onMatchedLogEvent(1 /*matcher index*/, event4); + + // Check dump report content. + ProtoOutputStream output; + std::set<string> strSet; + eventProducer.onDumpReport(bucketStartTimeNs + 50, true /*include current partial bucket*/, + true /*erase data*/, FAST, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_event_metrics()); + ASSERT_EQ(2, report.event_metrics().data_size()); + + for (EventMetricData metricData : report.event_metrics().data()) { + AggregatedAtomInfo atomInfo = metricData.aggregated_atom_info(); + if (atomInfo.elapsed_timestamp_nanos_size() == 1) { + EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(0), bucketStartTimeNs + 40); + } else if (atomInfo.elapsed_timestamp_nanos_size() == 3) { + EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(0), bucketStartTimeNs + 10); + EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(1), bucketStartTimeNs + 20); + EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(2), bucketStartTimeNs + 30); + } else { + FAIL(); + } + } +} + TEST_F(EventMetricProducerTest, TestTwoAtomTagAggregatedEvents) { int64_t bucketStartTimeNs = 10000000000; int tagId = 1; diff --git a/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/statsd/tests/metrics/GaugeMetricProducer_test.cpp index c64fb7b7..a378393c 100644 --- a/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -150,7 +150,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { allData.clear(); allData.push_back(makeLogEvent(tagId, bucket2StartTimeNs + 1, 10, "some value", 11)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); EXPECT_EQ(INT, it->mValue.getType()); @@ -168,7 +168,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { allData.clear(); allData.push_back(makeLogEvent(tagId, bucket3StartTimeNs + 10, 24, "some value", 25)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); EXPECT_EQ(INT, it->mValue.getType()); @@ -331,7 +331,7 @@ TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) { vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucketStartTimeNs); ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() @@ -361,7 +361,8 @@ TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 1, 3)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); + gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, + bucketStartTimeNs + bucketSizeNs); ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin() @@ -399,7 +400,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucketStartTimeNs); ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() @@ -462,7 +463,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin() @@ -551,7 +552,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1000, 110)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); @@ -598,7 +599,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 13)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucketStartTimeNs); ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() @@ -611,7 +612,8 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { allData.clear(); allData.push_back(event2); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); + gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, + bucketStartTimeNs + bucketSizeNs); ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() @@ -623,7 +625,8 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { allData.clear(); allData.push_back( CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10, 26)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs); + gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, + bucket2StartTimeNs + 2 * bucketSizeNs); ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() @@ -635,7 +638,8 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { // This event does not have the gauge field. Thus the current bucket value is 0. allData.clear(); allData.push_back(CreateNoValuesLogEvent(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs); + gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, + bucketStartTimeNs + 3 * bucketSizeNs); ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty()); } @@ -848,6 +852,96 @@ TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) { EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis()); } +TEST(GaugeMetricProducerTest, TestPullDimensionalSampling) { + ShardOffsetProvider::getInstance().setShardOffset(5); + + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + int triggerId = 5; + int shardCount = 2; + GaugeMetric sampledGaugeMetric = createGaugeMetric( + "GaugePullSampled", metricId, GaugeMetric::FIRST_N_SAMPLES, nullopt, triggerId); + sampledGaugeMetric.set_max_pull_delay_sec(INT_MAX); + *sampledGaugeMetric.mutable_dimensions_in_what() = CreateDimensions(tagId, {1}); + *sampledGaugeMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateDimensions(tagId, {1}); + sampledGaugeMetric.mutable_dimensional_sampling_info()->set_shard_count(shardCount); + *config.add_gauge_metric() = sampledGaugeMetric; + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + sp<EventMatcherWizard> eventMatcherWizard = + createEventMatcherWizard(tagId, logEventMatcherIndex); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + + EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 10, 1001, 5, 10)); + data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 10, 1002, 10, 10)); + data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 10, 1003, 15, 10)); + return true; + })) + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 20, 1001, 6, 10)); + data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 20, 1002, 12, 10)); + data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 20, 1003, 18, 10)); + return true; + })); + + GaugeMetricProducer gaugeProducer(kConfigKey, sampledGaugeMetric, + -1 /*-1 meaning no condition*/, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, + tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + SamplingInfo samplingInfo; + samplingInfo.shardCount = shardCount; + translateFieldMatcher(sampledGaugeMetric.dimensional_sampling_info().sampled_what_field(), + &samplingInfo.sampledWhatFields); + gaugeProducer.setSamplingInfo(samplingInfo); + gaugeProducer.prepareFirstBucket(); + + LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); + CreateRepeatedValueLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 10, 5); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + + triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + + // Check dump report. + ProtoOutputStream output; + std::set<string> strSet; + int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; + gaugeProducer.onDumpReport(dumpReportTimeNs, true /* include current buckets */, true, + NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + backfillDimensionPath(&report); + backfillStartEndTimestamp(&report); + backfillAggregatedAtoms(&report); + + EXPECT_TRUE(report.has_gauge_metrics()); + StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; + sortMetricDataByDimensionsValue(report.gauge_metrics(), &gaugeMetrics); + ASSERT_EQ(2, gaugeMetrics.data_size()); + EXPECT_EQ(0, report.gauge_metrics().skipped_size()); + + // Only Uid 1 and 3 are logged. (odd hash value) + (offset of 5) % (shard count of 2) = 0 + GaugeMetricData data = gaugeMetrics.data(0); + ValidateUidDimension(data.dimensions_in_what(), tagId, 1001); + ValidateGaugeBucketTimes(data.bucket_info(0), bucketStartTimeNs, dumpReportTimeNs, + {bucketStartTimeNs + 10, bucketStartTimeNs + 20}); + + data = gaugeMetrics.data(1); + ValidateUidDimension(data.dimensions_in_what(), tagId, 1003); + ValidateGaugeBucketTimes(data.bucket_info(0), bucketStartTimeNs, dumpReportTimeNs, + {bucketStartTimeNs + 10, bucketStartTimeNs + 20}); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/statsd/tests/metrics/NumericValueMetricProducer_test.cpp b/statsd/tests/metrics/NumericValueMetricProducer_test.cpp index f4e7cd9c..f2b50c32 100644 --- a/statsd/tests/metrics/NumericValueMetricProducer_test.cpp +++ b/statsd/tests/metrics/NumericValueMetricProducer_test.cpp @@ -147,6 +147,22 @@ public: stateGroupMap); } + static sp<NumericValueMetricProducer> createValueProducerWithSampling( + sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric, + const int pullAtomId = tagId) { + sp<NumericValueMetricProducer> valueProducer = createValueProducer( + pullerManager, metric, pullAtomId, /*conditionAfterFirstBucketPrepared=*/nullopt, + /*slicedStateAtoms=*/{}, /*stateGroupMap=*/{}, bucketStartTimeNs, bucketStartTimeNs, + /*eventMatcherWizard=*/nullptr); + + SamplingInfo samplingInfo; + samplingInfo.shardCount = metric.dimensional_sampling_info().shard_count(); + translateFieldMatcher(metric.dimensional_sampling_info().sampled_what_field(), + &samplingInfo.sampledWhatFields); + valueProducer->setSamplingInfo(samplingInfo); + return valueProducer; + } + static sp<NumericValueMetricProducer> createValueProducerWithBucketParams( sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric, const int64_t timeBaseNs, const int64_t startTimeNs, const int pullAtomId = tagId) { @@ -349,7 +365,7 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsNoCondition) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // empty since bucket is flushed ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); // dimInfos holds the base @@ -363,7 +379,7 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsNoCondition) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); // empty since bucket is cleared ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); // dimInfos holds the base @@ -378,7 +394,7 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsNoCondition) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket4StartTimeNs); // empty since bucket is cleared ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); // dimInfos holds the base @@ -425,7 +441,7 @@ TEST_P(NumericValueMetricProducerTest_PartialBucket, TestPartialBucketCreated) { vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 2)); - valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // Partial buckets created in 2nd bucket. switch (GetParam()) { @@ -474,7 +490,7 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsWithFiltering) { allData.clear(); allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 3, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // empty since bucket is cleared ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); // dimInfos holds the base @@ -488,14 +504,14 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsWithFiltering) { allData.clear(); allData.push_back(CreateTwoValueLogEvent(tagId, bucket3StartTimeNs + 1, 4, 23)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); // No new data seen, so data has been cleared. ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(0UL, valueProducer->mDimInfos.size()); allData.clear(); allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket4StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0]; @@ -525,7 +541,7 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // empty since bucket is cleared ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); // dimInfos holds the base @@ -539,7 +555,7 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { allData.clear(); // 10 is less than 11, so we reset and keep 10 as the value. allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); // empty since the bucket is flushed. ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); @@ -551,7 +567,7 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket4StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0]; @@ -578,7 +594,7 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // empty since bucket is cleared ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); // mDimInfos holds the base @@ -592,7 +608,7 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { allData.clear(); // 10 is less than 11, so we reset. 10 only updates the base. allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0]; @@ -602,7 +618,7 @@ TEST(NumericValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket4StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0]; @@ -664,7 +680,7 @@ TEST(NumericValueMetricProducerTest, TestEventsWithNonSlicedCondition) { vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}, {0}, {bucketStartTimeNs}, {bucket2StartTimeNs}); @@ -777,7 +793,7 @@ TEST_P(NumericValueMetricProducerTest_PartialBucket, TestPulledValue) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); switch (GetParam()) { @@ -795,7 +811,7 @@ TEST_P(NumericValueMetricProducerTest_PartialBucket, TestPulledValue) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 150)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); EXPECT_EQ(bucket3StartTimeNs, valueProducer->mCurrentBucketStartTimeNs); EXPECT_EQ(2, valueProducer->getCurrentBucketNum()); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30}, @@ -823,7 +839,7 @@ TEST(NumericValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); @@ -1141,7 +1157,7 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryNoCondition) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // empty since bucket is finished ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); @@ -1155,7 +1171,7 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryNoCondition) { // pull 2 at correct time allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); // empty since bucket is finished ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); @@ -1172,7 +1188,7 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryNoCondition) { // The new bucket is back to normal. allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket6StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0]; @@ -1253,7 +1269,7 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryWithCondition) { // since the condition turned to off before this pull finish, it has no effect vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {1}, {bucketStartTimeNs}, {bucket2StartTimeNs}); @@ -1339,7 +1355,7 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryWithCondition2) { // for the new bucket since it was just pulled. vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs + 50); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); @@ -1354,7 +1370,7 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundaryWithCondition2) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 160)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); assertPastBucketValuesSingleKey( valueProducer->mPastBuckets, {20, 30}, {bucketSizeNs - 8, bucketSizeNs - 24}, {1, -1}, @@ -1480,6 +1496,7 @@ TEST(NumericValueMetricProducerTest, TestPushedAggregateAvg) { EXPECT_TRUE( std::abs(valueProducer->mPastBuckets.begin()->second.back().aggregates[0].double_value - 12.5) < epsilon); + EXPECT_EQ(2, valueProducer->mPastBuckets.begin()->second.back().sampleSizes[0]); } TEST(NumericValueMetricProducerTest, TestPushedAggregateSum) { @@ -1742,7 +1759,7 @@ TEST(NumericValueMetricProducerTest, TestUseZeroDefaultBase) { allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4)); allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(2UL, valueProducer->mDimInfos.size()); EXPECT_EQ(true, base1.has_value()); @@ -1808,7 +1825,7 @@ TEST(NumericValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4)); allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(2UL, valueProducer->mDimInfos.size()); EXPECT_EQ(true, base1.has_value()); @@ -1831,7 +1848,7 @@ TEST(NumericValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { // This pull is incomplete since it's missing dimension 1. Will cause mDimInfos to be trimmed allData.clear(); allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket4StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); @@ -1845,7 +1862,7 @@ TEST(NumericValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { allData.clear(); allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 13)); allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 1, 5)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket5StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(2UL, valueProducer->mDimInfos.size()); @@ -1898,7 +1915,7 @@ TEST(NumericValueMetricProducerTest, TestTrimUnusedDimensionKey) { allData.clear(); allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4)); allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(2UL, valueProducer->mDimInfos.size()); @@ -1924,7 +1941,7 @@ TEST(NumericValueMetricProducerTest, TestTrimUnusedDimensionKey) { // next pull somehow did not happen, skip to end of bucket 3 allData.clear(); allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket4StartTimeNs); // Only one dimension left. One was trimmed. ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); @@ -1939,7 +1956,7 @@ TEST(NumericValueMetricProducerTest, TestTrimUnusedDimensionKey) { allData.clear(); allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14)); allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 1, 14)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket5StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(2UL, valueProducer->mDimInfos.size()); @@ -1947,7 +1964,7 @@ TEST(NumericValueMetricProducerTest, TestTrimUnusedDimensionKey) { allData.clear(); allData.push_back(CreateTwoValueLogEvent(tagId, bucket6StartTimeNs + 1, 1, 19)); allData.push_back(CreateTwoValueLogEvent(tagId, bucket6StartTimeNs + 1, 2, 20)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket6StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(2UL, valueProducer->mDimInfos.size()); @@ -2009,7 +2026,7 @@ TEST(NumericValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange EXPECT_EQ(0, curInterval.sampleSize); vector<shared_ptr<LogEvent>> allData; - valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_FAIL, bucket2StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); EXPECT_EQ(false, curBase.has_value()); @@ -2197,7 +2214,7 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditio vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucketStartTimeNs); // This will fail and should invalidate the whole bucket since we do not have all the data // needed to compute the metric value when the screen was on. @@ -2207,7 +2224,7 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditio // Bucket end. allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); @@ -2275,7 +2292,7 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHi vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // First bucket added to mSkippedBuckets after flush. ASSERT_EQ(1UL, valueProducer->mSkippedBuckets.size()); @@ -2335,7 +2352,7 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPull vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_FAIL, bucketStartTimeNs); valueProducer->onConditionChanged(false, bucketStartTimeNs + 2); valueProducer->onConditionChanged(true, bucketStartTimeNs + 3); @@ -2343,7 +2360,7 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPull // Bucket end. allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); @@ -2412,7 +2429,7 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFai vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucketStartTimeNs); valueProducer->onConditionChanged(false, bucketStartTimeNs + 2); valueProducer->onConditionChanged(true, bucketStartTimeNs + 3); @@ -2420,7 +2437,7 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFai // Bucket end. allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140)); - valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_FAIL, bucket2StartTimeNs); valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); @@ -2474,7 +2491,7 @@ TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) { vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); EXPECT_EQ(valueProducer->mDimInfos.begin()->second.seenNewData, false); @@ -2484,7 +2501,7 @@ TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) { // Bucket 3 empty. allData.clear(); allData.push_back(CreateNoValuesLogEvent(tagId, bucket3StartTimeNs + 1)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); // Data has been trimmed. ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); ASSERT_EQ(1UL, valueProducer->mSkippedBuckets.size()); @@ -2494,7 +2511,7 @@ TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) { // Bucket 4 start. allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 150)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket4StartTimeNs); ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); ASSERT_EQ(2UL, valueProducer->mSkippedBuckets.size()); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); @@ -2503,7 +2520,7 @@ TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) { // Bucket 5 start. allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket5StartTimeNs + 1, 170)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket5StartTimeNs); assertPastBucketValuesSingleKey( valueProducer->mPastBuckets, {107, 20}, {bucketSizeNs, bucketSizeNs}, {0, 0}, {bucketStartTimeNs, bucket4StartTimeNs}, {bucket2StartTimeNs, bucket5StartTimeNs}); @@ -2574,7 +2591,7 @@ TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0]; @@ -2632,7 +2649,7 @@ TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { // End of bucket vector<shared_ptr<LogEvent>> allData; allData.clear(); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(0UL, valueProducer->mDimInfos.size()); @@ -2669,7 +2686,7 @@ TEST(NumericValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 2)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // Key 1 should be removed from mDimInfos since in not present in the most pull. ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); @@ -2737,7 +2754,7 @@ TEST_P(NumericValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLast vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 4)); // Pull fails and arrives late. - valueProducer->onDataPulled(allData, /** fails */ false, bucket3StartTimeNs + 1); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_FAIL, bucket3StartTimeNs + 1); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {partialBucketSplitTimeNs - bucketStartTimeNs}, {0}, {bucketStartTimeNs}, {partialBucketSplitTimeNs}); @@ -2781,7 +2798,7 @@ TEST(NumericValueMetricProducerTest, TestBucketBoundariesOnConditionChange) { // End of first bucket vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 4)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs + 1); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); @@ -2813,11 +2830,11 @@ TEST(NumericValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) { vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucketStartTimeNs + 30); allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // Bucket should have been completed. assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs}, {0}, @@ -2843,11 +2860,11 @@ TEST(NumericValueMetricProducerTest, TestLateOnDataPulledWithDiff) { vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucketStartTimeNs + 30); allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // Bucket should have been completed. assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs}, {0}, @@ -2934,7 +2951,7 @@ TEST(NumericValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs + 1); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2}, {0}, {bucketStartTimeNs}, {bucket2StartTimeNs}); @@ -2978,18 +2995,18 @@ TEST(NumericValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) { vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 3, 10)); - valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs + 3); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_FAIL, bucketStartTimeNs + 3); allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20)); - valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_FAIL, bucket2StartTimeNs); valueProducer->onConditionChanged(false, bucket2StartTimeNs + 8); valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 30)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // There was not global base available so all buckets are invalid. assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}, {}); @@ -3018,7 +3035,7 @@ TEST(NumericValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, tagId, 2, 2)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ProtoOutputStream output; std::set<string> strSet; @@ -3083,7 +3100,7 @@ TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) { vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 30); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs + 30); // Bucket should have been completed. assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs}, {30}, @@ -3134,7 +3151,7 @@ TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_withMultipleCondition // Now the alarm is delivered. Condition is off though. vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8}, {0}, {bucketStartTimeNs}, {bucket2StartTimeNs}); @@ -3166,7 +3183,7 @@ TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) { // Now the alarm is delivered. Condition is on. vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8}, {0}, {bucketStartTimeNs}, {bucket2StartTimeNs}); @@ -3188,7 +3205,7 @@ TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) // Now the alarm is delivered. Condition is off though. vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // Condition was always false. assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}, {}); @@ -3222,7 +3239,7 @@ TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_withFailure) { // Now the alarm is delivered. Condition is off though. vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(0UL, valueProducer->mDimInfos.size()); @@ -3311,7 +3328,7 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEv // Bucket boundary pull. vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15)); - valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs + 1); // Late condition change event. valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100); @@ -3378,7 +3395,7 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateE // Bucket boundary pull. vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15)); - valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs + 1); allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 20)); @@ -3722,7 +3739,7 @@ TEST(NumericValueMetricProducerTest_BucketDrop, TestConditionUnknownMultipleBuck vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 3)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // This bucket is also dropped due to condition unknown. int64_t conditionChangeTimeNs = bucket2StartTimeNs + 10 * NS_PER_SEC; @@ -4720,7 +4737,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensi vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 10)); allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 15)); - valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs + 1); // Ensure the MetricDimensionKeys for the current state are kept. ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); @@ -5756,7 +5773,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMultipleDimensions) { CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 16 /* tag */)); allData.push_back( CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 13, 8 /*tag*/)); - valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs + 1); // Buckets flushed. MetricDimensionKeys not corresponding to the current state are removed. ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); @@ -5966,7 +5983,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithCondition) { vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); @@ -6130,7 +6147,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithConditionFalseMultipleBu // Pull at end of first bucket. vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(1UL, valueProducer->mDimInfos.size()); @@ -6143,7 +6160,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithConditionFalseMultipleBu // Pull at end of second bucket. Since no new data is seen, mDimInfos will be cleared. allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 15)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(0UL, valueProducer->mDimInfos.size()); @@ -6339,7 +6356,7 @@ TEST(NumericValueMetricProducerTest, TestSlicedStateWithMultipleDimensionsMissin CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 14 /* tag */)); allData.push_back( CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 16 /* tag */)); - valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs + 1); // Buckets flushed. MetricDimensionKeys not corresponding to the current state are removed. ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); @@ -6527,7 +6544,7 @@ TEST(NumericValueMetricProducerTest, TestUploadThreshold) { allData.clear(); allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, 1 /*uid*/, 21, 21)); allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, 2 /*uid*/, 20, 5)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // Check dump report. ProtoOutputStream output; @@ -6596,7 +6613,8 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullWhileC // first delayed pull on the bucket #1 edge allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, + bucket2StartTimeNs + pullDelayNs); // the delayed pull did close the first bucket with condition duration == bucketSizeNs assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs}, @@ -6605,7 +6623,7 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullWhileC // second pull on the bucket #2 boundary on time allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 15)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); // the second pull did close the second bucket with condition duration == bucketSizeNs assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5, 5}, @@ -6646,7 +6664,8 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullWhileC vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + delayNs, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + delayNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, + bucket2StartTimeNs + delayNs); // first delayed pull on the bucket #1 edge // the delayed pull did close the first bucket with condition duration == conditionDurationNs @@ -6660,7 +6679,7 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullWhileC allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); // second pull on the bucket #2 edge is on time assertPastBucketValuesSingleKey( @@ -6716,7 +6735,7 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestLatePullOnCondition vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 30)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); // second pull on the bucket #2 edge is on time // the pull did close the second bucket with condition where @@ -6819,7 +6838,7 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullWithCo vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket1LatePullNs, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket1LatePullNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket1LatePullNs); // first delayed pull on the bucket #1 edge // the delayed pull did close the first bucket with condition duration == bucketSizeNs @@ -6833,7 +6852,7 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullWithCo // will force delayed pull & bucket #2 close allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2LatePullNs, 25)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2LatePullNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2LatePullNs); // second delayed pull on the bucket #2 edge // the pull did close the second bucket with condition true @@ -6850,7 +6869,7 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullWithCo // will force pull on time & bucket #3 close allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs, 40)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket4StartTimeNs); // the pull did close the third bucket with condition true assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5, 10, 15}, @@ -6887,7 +6906,8 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullNoCond // first delayed pull on the bucket #1 edge allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, + bucket2StartTimeNs + pullDelayNs); // the delayed pull did close the first bucket with condition duration == bucketSizeNs assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs}, @@ -6896,7 +6916,7 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullNoCond // second pull on the bucket #2 boundary on time allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 15)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); // the second pull did close the second bucket with condition duration == bucketSizeNs assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5, 5}, @@ -6907,7 +6927,7 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullNoCond // third pull on the bucket #3 boundary on time allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs, 20)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket4StartTimeNs); // the third pull did close the third bucket with condition duration == bucketSizeNs assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5, 5, 5}, @@ -6939,7 +6959,8 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullNoCond // first delayed pull on the bucket #1 edge with delay allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, + bucket2StartTimeNs + pullDelayNs); // the delayed pull did close the first bucket which is skipped // skipped due to bucket does not contains any value @@ -6949,7 +6970,7 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullNoCond // second pull on the bucket #2 boundary on time allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 15)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); // the second pull did close the second bucket with condition duration == bucketSizeNs assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, @@ -6958,7 +6979,7 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullNoCond // third pull on the bucket #3 boundary on time allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs, 20)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket4StartTimeNs); // the third pull did close the third bucket with condition duration == bucketSizeNs assertPastBucketValuesSingleKey( @@ -6999,7 +7020,8 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestThresholdNotDefined // first delayed pull on the bucket #1 edge allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, + bucket2StartTimeNs + pullDelayNs); // the delayed pull did close the first bucket with condition duration == bucketSizeNs // and the condition correction == pull delay @@ -7055,7 +7077,8 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestThresholdDefinedZer // first delayed pull on the bucket #1 edge allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, + bucket2StartTimeNs + pullDelayNs); // the delayed pull did close the first bucket with condition duration == bucketSizeNs assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs}, @@ -7112,7 +7135,8 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestThresholdUploadPass // first delayed pull on the bucket #1 edge allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, + bucket2StartTimeNs + pullDelayNs); // the delayed pull did close the first bucket with condition duration == bucketSizeNs assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs}, @@ -7121,7 +7145,7 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestThresholdUploadPass // second pull on the bucket #2 boundary on time allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 15)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs); // the second pull did close the second bucket with condition duration == bucketSizeNs assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5, 5}, @@ -7182,7 +7206,8 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestThresholdUploadPass // first delayed pull on the bucket #1 edge allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, + bucket2StartTimeNs + pullDelayNs); // the delayed pull did close the first bucket with condition duration == bucketSizeNs assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs}, @@ -7239,7 +7264,8 @@ TEST(NumericValueMetricProducerTest_ConditionCorrection, TestThresholdUploadSkip // first delayed pull on the bucket #1 edge allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, + bucket2StartTimeNs + pullDelayNs); // the delayed pull did close the first bucket with condition duration == bucketSizeNs assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs}, @@ -7422,7 +7448,7 @@ TEST(NumericValueMetricProducerTest, TestSubsetDimensions) { allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, 1 /*uid*/, 11, 7)); allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, 2 /*uid*/, 8, 5)); allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, 2 /*uid*/, 9, 7)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(2UL, valueProducer->mDimInfos.size()); @@ -7501,7 +7527,7 @@ TEST(NumericValueMetricProducerTest, TestRepeatedValueFieldAndDimensions) { allData.clear(); allData.push_back(makeRepeatedUidLogEvent(tagId, bucket2StartTimeNs + 1, {1, 10}, 5, {5, 7})); allData.push_back(makeRepeatedUidLogEvent(tagId, bucket2StartTimeNs + 1, {2, 10}, 5, {7, 5})); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs); // Check dump report. ProtoOutputStream output; @@ -7538,6 +7564,184 @@ TEST(NumericValueMetricProducerTest, TestRepeatedValueFieldAndDimensions) { 0); // Summed diffs of 7, 14 } +TEST(NumericValueMetricProducerTest, TestSampleSize) { + sp<EventMatcherWizard> eventMatcherWizard = + createEventMatcherWizard(tagId, logEventMatcherIndex); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + + ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric(); + + /*Sample size is added automatically with ValueMetric::AVG*/ + metric.set_aggregation_type(ValueMetric::AVG); + sp<NumericValueMetricProducer> valueProducerAvg = + NumericValueMetricProducerTestHelper::createValueProducerNoConditions( + pullerManager, metric, /*pullAtomId=*/-1); + + /*Sample size is not added automatically with non-ValueMetric::AVG aggregation types*/ + metric.set_aggregation_type(ValueMetric::SUM); + sp<NumericValueMetricProducer> valueProducerSum = + NumericValueMetricProducerTestHelper::createValueProducerNoConditions( + pullerManager, metric, /*pullAtomId=*/-1); + + /*Sample size is added when include_sample_size bool is set to true*/ + metric.set_include_sample_size(true); + sp<NumericValueMetricProducer> valueProducerSumWithSampleSize = + NumericValueMetricProducerTestHelper::createValueProducerNoConditions( + pullerManager, metric, /*pullAtomId=*/-1); + + LogEvent event1(/*uid=*/0, /*pid=*/0); + LogEvent event2(/*uid=*/0, /*pid=*/0); + LogEvent event3(/*uid=*/0, /*pid=*/0); + CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); + CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15); + CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 20, 20); + valueProducerAvg->onMatchedLogEvent(1 /*log matcher index*/, event1); + valueProducerAvg->onMatchedLogEvent(1 /*log matcher index*/, event2); + valueProducerSum->onMatchedLogEvent(1 /*log matcher index*/, event1); + valueProducerSum->onMatchedLogEvent(1 /*log matcher index*/, event2); + valueProducerSum->onMatchedLogEvent(1 /*log matcher index*/, event3); + valueProducerSumWithSampleSize->onMatchedLogEvent(1 /*log matcher index*/, event1); + valueProducerSumWithSampleSize->onMatchedLogEvent(1 /*log matcher index*/, event2); + valueProducerSumWithSampleSize->onMatchedLogEvent(1 /*log matcher index*/, event3); + + NumericValueMetricProducer::Interval curInterval; + ASSERT_EQ(1UL, valueProducerAvg->mCurrentSlicedBucket.size()); + curInterval = valueProducerAvg->mCurrentSlicedBucket.begin()->second.intervals[0]; + EXPECT_EQ(2, curInterval.sampleSize); + ASSERT_EQ(1UL, valueProducerSum->mCurrentSlicedBucket.size()); + curInterval = valueProducerSum->mCurrentSlicedBucket.begin()->second.intervals[0]; + EXPECT_EQ(3, curInterval.sampleSize); + ASSERT_EQ(1UL, valueProducerSumWithSampleSize->mCurrentSlicedBucket.size()); + curInterval = valueProducerSumWithSampleSize->mCurrentSlicedBucket.begin()->second.intervals[0]; + EXPECT_EQ(3, curInterval.sampleSize); + + valueProducerAvg->flushIfNeededLocked(bucket2StartTimeNs); + valueProducerSum->flushIfNeededLocked(bucket2StartTimeNs); + valueProducerSumWithSampleSize->flushIfNeededLocked(bucket2StartTimeNs); + + // Start dump report and check output. + ProtoOutputStream outputAvg; + std::set<string> strSetAvg; + valueProducerAvg->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC, + true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, + &strSetAvg, &outputAvg); + + StatsLogReport reportAvg = outputStreamToProto(&outputAvg); + ASSERT_EQ(1, reportAvg.value_metrics().data_size()); + + ValueMetricData data = reportAvg.value_metrics().data(0); + ASSERT_EQ(1, data.bucket_info_size()); + ASSERT_EQ(1, data.bucket_info(0).values_size()); + EXPECT_EQ(2, data.bucket_info(0).values(0).sample_size()); + EXPECT_TRUE(std::abs(data.bucket_info(0).values(0).value_double() - 12.5) < epsilon); + + // Start dump report and check output. + ProtoOutputStream outputSum; + std::set<string> strSetSum; + valueProducerSum->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC, + true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, + &strSetSum, &outputSum); + + StatsLogReport reportSum = outputStreamToProto(&outputSum); + ASSERT_EQ(1, reportSum.value_metrics().data_size()); + + data = reportSum.value_metrics().data(0); + ASSERT_EQ(1, data.bucket_info_size()); + ASSERT_EQ(1, data.bucket_info(0).values_size()); + EXPECT_EQ(45, data.bucket_info(0).values(0).value_long()); + EXPECT_FALSE(data.bucket_info(0).values(0).has_sample_size()); + + // Start dump report and check output. + ProtoOutputStream outputSumWithSampleSize; + std::set<string> strSetSumWithSampleSize; + valueProducerSumWithSampleSize->onDumpReport( + bucket2StartTimeNs + 50 * NS_PER_SEC, true /* include recent buckets */, true, + NO_TIME_CONSTRAINTS, &strSetSumWithSampleSize, &outputSumWithSampleSize); + + StatsLogReport reportSumWithSampleSize = outputStreamToProto(&outputSumWithSampleSize); + ASSERT_EQ(1, reportSumWithSampleSize.value_metrics().data_size()); + + data = reportSumWithSampleSize.value_metrics().data(0); + ASSERT_EQ(1, data.bucket_info_size()); + ASSERT_EQ(1, data.bucket_info(0).values_size()); + EXPECT_EQ(3, data.bucket_info(0).values(0).sample_size()); + EXPECT_EQ(45, data.bucket_info(0).values(0).value_long()); +} + +TEST(NumericValueMetricProducerTest, TestDimensionalSampling) { + ShardOffsetProvider::getInstance().setShardOffset(5); + + int shardCount = 2; + ValueMetric sampledValueMetric = NumericValueMetricProducerTestHelper::createMetric(); + *sampledValueMetric.mutable_dimensions_in_what() = CreateDimensions(tagId, {1 /*uid*/}); + *sampledValueMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateDimensions(tagId, {1 /*uid*/}); + sampledValueMetric.mutable_dimensional_sampling_info()->set_shard_count(shardCount); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + + EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) + // First field is a dimension field and sampled what field. + // Second field is the value field. + // NumericValueMetricProducer initialized. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 1, 1001, 5, 10)); + data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 1, 1002, 10, 10)); + data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 1, 1003, 15, 10)); + return true; + })) + // Dump report pull. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back( + makeUidLogEvent(tagId, bucketStartTimeNs + 10000000000, 1001, 6, 10)); + data->push_back( + makeUidLogEvent(tagId, bucketStartTimeNs + 10000000000, 1002, 12, 10)); + data->push_back( + makeUidLogEvent(tagId, bucketStartTimeNs + 10000000000, 1003, 18, 10)); + return true; + })); + + sp<NumericValueMetricProducer> valueProducer = + NumericValueMetricProducerTestHelper::createValueProducerWithSampling( + pullerManager, sampledValueMetric); + + // Check dump report. + ProtoOutputStream output; + std::set<string> strSet; + int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; + valueProducer->onDumpReport(dumpReportTimeNs, true /* include current buckets */, true, + NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + backfillDimensionPath(&report); + backfillStartEndTimestamp(&report); + EXPECT_TRUE(report.has_value_metrics()); + StatsLogReport::ValueMetricDataWrapper valueMetrics; + sortMetricDataByDimensionsValue(report.value_metrics(), &valueMetrics); + ASSERT_EQ(2, valueMetrics.data_size()); + EXPECT_EQ(0, report.value_metrics().skipped_size()); + + // Only Uid 1, 3, 4 are logged. (odd hash value) + (offset of 5) % (shard count of 2) = 0 + ValueMetricData data = valueMetrics.data(0); + ValidateUidDimension(data.dimensions_in_what(), tagId, 1001); + ASSERT_EQ(1, data.bucket_info_size()); + ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + 10000000000, + {1}, -1, + 0); // Diff of 5 and 6 + + data = valueMetrics.data(1); + ValidateUidDimension(data.dimensions_in_what(), tagId, 1003); + ASSERT_EQ(1, data.bucket_info_size()); + ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + 10000000000, + {3}, -1, + 0); // Diff of 15 and 18 +} + } // namespace statsd } // namespace os } // namespace android diff --git a/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp index 16180c06..e083cab2 100644 --- a/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp +++ b/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp @@ -25,7 +25,6 @@ #include "src/condition/CombinationConditionTracker.h" #include "src/condition/SimpleConditionTracker.h" -#include "src/flags/FlagProvider.h" #include "src/matchers/CombinationAtomMatchingTracker.h" #include "src/metrics/DurationMetricProducer.h" #include "src/metrics/GaugeMetricProducer.h" @@ -83,14 +82,16 @@ map<int64_t, uint64_t> oldStateHashes; set<int64_t> noReportMetricIds; bool initConfig(const StatsdConfig& config) { - return initStatsdConfig( - key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseNs, timeBaseNs, allTagIdsToMatchersMap, oldAtomMatchingTrackers, - oldAtomMatchingTrackerMap, oldConditionTrackers, oldConditionTrackerMap, - oldMetricProducers, oldMetricProducerMap, oldAnomalyTrackers, oldAlarmTrackers, - tmpConditionToMetricMap, tmpTrackerToMetricMap, tmpTrackerToConditionMap, - tmpActivationAtomTrackerToMetricMap, tmpDeactivationAtomTrackerToMetricMap, - oldAlertTrackerMap, metricsWithActivation, oldStateHashes, noReportMetricIds); + // initStatsdConfig returns nullopt if config is valid + return !initStatsdConfig( + key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseNs, timeBaseNs, allTagIdsToMatchersMap, oldAtomMatchingTrackers, + oldAtomMatchingTrackerMap, oldConditionTrackers, oldConditionTrackerMap, + oldMetricProducers, oldMetricProducerMap, oldAnomalyTrackers, oldAlarmTrackers, + tmpConditionToMetricMap, tmpTrackerToMetricMap, tmpTrackerToConditionMap, + tmpActivationAtomTrackerToMetricMap, tmpDeactivationAtomTrackerToMetricMap, + oldAlertTrackerMap, metricsWithActivation, oldStateHashes, noReportMetricIds) + .has_value(); } vector<int> filterMatcherIndexesById(const vector<sp<AtomMatchingTracker>>& atomMatchingTrackers, @@ -132,11 +133,6 @@ public: oldStateHashes.clear(); noReportMetricIds.clear(); StateManager::getInstance().clear(); - FlagProvider::getInstance().overrideFuncs(&isAtLeastSFuncTrue, &getServerFlagFuncTrue); - } - - void TearDown() override { - FlagProvider::getInstance().resetOverrides(); } }; @@ -153,9 +149,10 @@ TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) { vector<bool> cycleTracker(1, false); unordered_map<int64_t, int> newAtomMatchingTrackerMap; newAtomMatchingTrackerMap[matcherId] = 0; - EXPECT_TRUE(determineMatcherUpdateStatus(config, 0, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)); + EXPECT_EQ(determineMatcherUpdateStatus(config, 0, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newAtomMatchingTrackerMap, + matchersToUpdate, cycleTracker), + nullopt); EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE); } @@ -177,9 +174,10 @@ TEST_F(ConfigUpdateTest, TestSimpleMatcherReplace) { vector<bool> cycleTracker(1, false); unordered_map<int64_t, int> newAtomMatchingTrackerMap; newAtomMatchingTrackerMap[matcherId] = 0; - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)); + EXPECT_EQ(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newAtomMatchingTrackerMap, + matchersToUpdate, cycleTracker), + nullopt); EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE); } @@ -201,9 +199,10 @@ TEST_F(ConfigUpdateTest, TestSimpleMatcherNew) { vector<bool> cycleTracker(1, false); unordered_map<int64_t, int> newAtomMatchingTrackerMap; newAtomMatchingTrackerMap[matcherId] = 0; - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)); + EXPECT_EQ(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newAtomMatchingTrackerMap, + matchersToUpdate, cycleTracker), + nullopt); EXPECT_EQ(matchersToUpdate[0], UPDATE_NEW); } @@ -241,9 +240,10 @@ TEST_F(ConfigUpdateTest, TestCombinationMatcherPreserve) { vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN); vector<bool> cycleTracker(3, false); // Only update the combination. It should recurse the two child matchers and preserve all 3. - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)); + EXPECT_EQ(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newAtomMatchingTrackerMap, + matchersToUpdate, cycleTracker), + nullopt); EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE); EXPECT_EQ(matchersToUpdate[1], UPDATE_PRESERVE); EXPECT_EQ(matchersToUpdate[2], UPDATE_PRESERVE); @@ -285,9 +285,10 @@ TEST_F(ConfigUpdateTest, TestCombinationMatcherReplace) { vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN); vector<bool> cycleTracker(3, false); // Only update the combination. The simple matchers should not be evaluated. - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)); + EXPECT_EQ(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newAtomMatchingTrackerMap, + matchersToUpdate, cycleTracker), + nullopt); EXPECT_EQ(matchersToUpdate[0], UPDATE_UNKNOWN); EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE); EXPECT_EQ(matchersToUpdate[2], UPDATE_UNKNOWN); @@ -329,9 +330,10 @@ TEST_F(ConfigUpdateTest, TestCombinationMatcherDepsChange) { vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN); vector<bool> cycleTracker(3, false); // Only update the combination. - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newAtomMatchingTrackerMap, - matchersToUpdate, cycleTracker)); + EXPECT_EQ(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newAtomMatchingTrackerMap, + matchersToUpdate, cycleTracker), + nullopt); // Matcher 2 and matcher3 must be reevaluated. Matcher 1 might, but does not need to be. EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE); EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE); @@ -402,9 +404,11 @@ TEST_F(ConfigUpdateTest, TestUpdateMatchers) { unordered_map<int64_t, int> newAtomMatchingTrackerMap; vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers; set<int64_t> replacedMatchers; - EXPECT_TRUE(updateAtomMatchingTrackers( - newConfig, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, newTagIds, - newAtomMatchingTrackerMap, newAtomMatchingTrackers, replacedMatchers)); + EXPECT_EQ(updateAtomMatchingTrackers(newConfig, uidMap, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newTagIds, + newAtomMatchingTrackerMap, newAtomMatchingTrackers, + replacedMatchers), + nullopt); ASSERT_EQ(newTagIds.size(), 3); EXPECT_EQ(newTagIds.count(10), 1); @@ -507,9 +511,10 @@ TEST_F(ConfigUpdateTest, TestSimpleConditionPreserve) { vector<bool> cycleTracker(1, false); unordered_map<int64_t, int> newConditionTrackerMap; newConditionTrackerMap[predicate.id()] = 0; - EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, cycleTracker)); + EXPECT_EQ(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, cycleTracker), + nullopt); EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE); } @@ -533,9 +538,10 @@ TEST_F(ConfigUpdateTest, TestSimpleConditionReplace) { vector<bool> cycleTracker(1, false); unordered_map<int64_t, int> newConditionTrackerMap; newConditionTrackerMap[predicate.id()] = 0; - EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, cycleTracker)); + EXPECT_EQ(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, cycleTracker), + nullopt); EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE); } @@ -560,9 +566,10 @@ TEST_F(ConfigUpdateTest, TestSimpleConditionDepsChange) { vector<bool> cycleTracker(1, false); unordered_map<int64_t, int> newConditionTrackerMap; newConditionTrackerMap[predicate.id()] = 0; - EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, cycleTracker)); + EXPECT_EQ(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, cycleTracker), + nullopt); EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE); } @@ -602,9 +609,10 @@ TEST_F(ConfigUpdateTest, TestCombinationConditionPreserve) { vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN); vector<bool> cycleTracker(3, false); // Only update the combination. It should recurse the two child predicates and preserve all 3. - EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, cycleTracker)); + EXPECT_EQ(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, cycleTracker), + nullopt); EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE); EXPECT_EQ(conditionsToUpdate[1], UPDATE_PRESERVE); EXPECT_EQ(conditionsToUpdate[2], UPDATE_PRESERVE); @@ -648,9 +656,10 @@ TEST_F(ConfigUpdateTest, TestCombinationConditionReplace) { vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN); vector<bool> cycleTracker(3, false); // Only update the combination. The simple conditions should not be evaluated. - EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, cycleTracker)); + EXPECT_EQ(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, cycleTracker), + nullopt); EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE); EXPECT_EQ(conditionsToUpdate[1], UPDATE_UNKNOWN); EXPECT_EQ(conditionsToUpdate[2], UPDATE_UNKNOWN); @@ -693,9 +702,10 @@ TEST_F(ConfigUpdateTest, TestCombinationConditionDepsChange) { vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN); vector<bool> cycleTracker(3, false); // Only update the combination. Simple2 and combination1 must be evaluated. - EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap, - oldConditionTrackers, newConditionTrackerMap, - replacedMatchers, conditionsToUpdate, cycleTracker)); + EXPECT_EQ(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, cycleTracker), + nullopt); EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE); EXPECT_EQ(conditionsToUpdate[1], UPDATE_REPLACE); } @@ -830,10 +840,11 @@ TEST_F(ConfigUpdateTest, TestUpdateConditions) { unordered_map<int, vector<int>> trackerToConditionMap; vector<ConditionState> conditionCache; set<int64_t> replacedConditions; - EXPECT_TRUE(updateConditions(key, newConfig, newAtomMatchingTrackerMap, replacedMatchers, - oldConditionTrackerMap, oldConditionTrackers, - newConditionTrackerMap, newConditionTrackers, - trackerToConditionMap, conditionCache, replacedConditions)); + EXPECT_EQ(updateConditions(key, newConfig, newAtomMatchingTrackerMap, replacedMatchers, + oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap, + newConditionTrackers, trackerToConditionMap, conditionCache, + replacedConditions), + nullopt); unordered_map<int64_t, int> expectedConditionTrackerMap = { {simple1Id, simple1Index}, {simple2Id, simple2Index}, @@ -964,8 +975,9 @@ TEST_F(ConfigUpdateTest, TestUpdateStates) { unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; map<int64_t, uint64_t> newStateProtoHashes; set<int64_t> replacedStates; - EXPECT_TRUE(updateStates(newConfig, oldStateHashes, stateAtomIdMap, allStateGroupMaps, - newStateProtoHashes, replacedStates)); + EXPECT_EQ(updateStates(newConfig, oldStateHashes, stateAtomIdMap, allStateGroupMaps, + newStateProtoHashes, replacedStates), + nullopt); EXPECT_THAT(replacedStates, ContainerEq(set({state1Id, state3Id}))); unordered_map<int64_t, int> expectedStateAtomIdMap = { @@ -1003,10 +1015,11 @@ TEST_F(ConfigUpdateTest, TestEventMetricPreserve) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE); } @@ -1039,10 +1052,11 @@ TEST_F(ConfigUpdateTest, TestEventMetricActivationAdded) { unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}}; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1068,10 +1082,11 @@ TEST_F(ConfigUpdateTest, TestEventMetricWhatChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1097,10 +1112,11 @@ TEST_F(ConfigUpdateTest, TestEventMetricConditionChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1133,10 +1149,11 @@ TEST_F(ConfigUpdateTest, TestMetricConditionLinkDepsChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{linkPredicate.id()}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{linkPredicate.id()}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1168,10 +1185,11 @@ TEST_F(ConfigUpdateTest, TestEventMetricActivationDepsChange) { unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}}; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {startMatcher.id()}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {startMatcher.id()}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1201,10 +1219,11 @@ TEST_F(ConfigUpdateTest, TestCountMetricPreserve) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE); } @@ -1234,10 +1253,11 @@ TEST_F(ConfigUpdateTest, TestCountMetricDefinitionChange) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1264,10 +1284,11 @@ TEST_F(ConfigUpdateTest, TestCountMetricWhatChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1294,10 +1315,11 @@ TEST_F(ConfigUpdateTest, TestCountMetricConditionChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1324,10 +1346,11 @@ TEST_F(ConfigUpdateTest, TestCountMetricStateChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{sliceState.id()}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{sliceState.id()}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1350,10 +1373,11 @@ TEST_F(ConfigUpdateTest, TestGaugeMetricPreserve) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE); } @@ -1372,10 +1396,11 @@ TEST_F(ConfigUpdateTest, TestGaugeMetricDefinitionChange) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1391,10 +1416,11 @@ TEST_F(ConfigUpdateTest, TestGaugeMetricWhatChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1417,10 +1443,11 @@ TEST_F(ConfigUpdateTest, TestGaugeMetricConditionChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1438,10 +1465,11 @@ TEST_F(ConfigUpdateTest, TestGaugeMetricTriggerEventChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {triggerEvent.id()}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {triggerEvent.id()}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1466,10 +1494,11 @@ TEST_F(ConfigUpdateTest, TestDurationMetricPreserve) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE); } @@ -1490,10 +1519,11 @@ TEST_F(ConfigUpdateTest, TestDurationMetricDefinitionChange) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, /*replacedMatchers*/ {}, - /*replacedConditions=*/{}, /*replacedStates=*/{}, - metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, /*replacedMatchers*/ {}, + /*replacedConditions=*/{}, /*replacedStates=*/{}, + metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1512,10 +1542,11 @@ TEST_F(ConfigUpdateTest, TestDurationMetricWhatChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{what.id()}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{what.id()}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1536,10 +1567,11 @@ TEST_F(ConfigUpdateTest, TestDurationMetricConditionChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{condition.id()}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{condition.id()}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1562,10 +1594,11 @@ TEST_F(ConfigUpdateTest, TestDurationMetricStateChange) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{sliceState.id()}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{sliceState.id()}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1589,10 +1622,11 @@ TEST_F(ConfigUpdateTest, TestValueMetricPreserve) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE); } @@ -1609,10 +1643,11 @@ TEST_F(ConfigUpdateTest, TestValueMetricDefinitionChange) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1626,10 +1661,11 @@ TEST_F(ConfigUpdateTest, TestValueMetricWhatChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1650,10 +1686,11 @@ TEST_F(ConfigUpdateTest, TestValueMetricConditionChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1671,10 +1708,11 @@ TEST_F(ConfigUpdateTest, TestValueMetricStateChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{sliceState.id()}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{sliceState.id()}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1696,10 +1734,11 @@ TEST_F(ConfigUpdateTest, TestKllMetricPreserve) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate{UPDATE_UNKNOWN}; - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE); } @@ -1717,10 +1756,11 @@ TEST_F(ConfigUpdateTest, TestKllMetricDefinitionChange) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate{UPDATE_UNKNOWN}; - EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, - metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1734,10 +1774,11 @@ TEST_F(ConfigUpdateTest, TestKllMetricWhatChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate{UPDATE_UNKNOWN}; - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1759,10 +1800,11 @@ TEST_F(ConfigUpdateTest, TestKllMetricConditionChanged) { unordered_map<int64_t, int> metricToActivationMap; vector<UpdateStatus> metricsToUpdate{UPDATE_UNKNOWN}; - EXPECT_TRUE(determineAllMetricUpdateStatuses( - config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, - /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, - /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, + /*replacedStates=*/{}, metricsToUpdate), + nullopt); EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } @@ -1915,15 +1957,17 @@ TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) { unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; vector<int> metricsWithActivation; set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, - newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, - newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{}, - /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, - newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); + EXPECT_EQ(updateMetrics(key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, + new StatsPullerManager(), oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers, + newConditionTrackerMap, replacedConditions, newConditionTrackers, + conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{}, + /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, + newMetricProducerMap, newMetricProducers, conditionToMetricMap, + trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, + replacedMetrics), + nullopt); unordered_map<int64_t, int> expectedMetricProducerMap = { {event1Id, event1Index}, {event2Id, event2Index}, {event3Id, event3Index}, @@ -2133,7 +2177,7 @@ TEST_F(ConfigUpdateTest, TestUpdateCountMetrics) { unordered_map<int64_t, int> stateAtomIdMap; unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; map<int64_t, uint64_t> stateProtoHashes; - EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)); + EXPECT_EQ(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes), nullopt); EXPECT_EQ(stateAtomIdMap[state2Id], util::BATTERY_SAVER_MODE_STATE_CHANGED); // Output data structures to validate. @@ -2146,15 +2190,17 @@ TEST_F(ConfigUpdateTest, TestUpdateCountMetrics) { unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; vector<int> metricsWithActivation; set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, - newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{}, - newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates, - oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers, - conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); + EXPECT_EQ(updateMetrics(key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, + new StatsPullerManager(), oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers, + newConditionTrackerMap, /*replacedConditions=*/{}, newConditionTrackers, + conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates, + oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, + newMetricProducers, conditionToMetricMap, trackerToMetricMap, + noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, + replacedMetrics), + nullopt); unordered_map<int64_t, int> expectedMetricProducerMap = { {count1Id, count1Index}, {count2Id, count2Index}, {count3Id, count3Index}, @@ -2357,15 +2403,17 @@ TEST_F(ConfigUpdateTest, TestUpdateGaugeMetrics) { unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; vector<int> metricsWithActivation; set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, - newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{}, - newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{}, - /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, - newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); + EXPECT_EQ(updateMetrics(key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, + new StatsPullerManager(), oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers, + newConditionTrackerMap, /*replacedConditions=*/{}, newConditionTrackers, + conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{}, + /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, + newMetricProducerMap, newMetricProducers, conditionToMetricMap, + trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, + replacedMetrics), + nullopt); unordered_map<int64_t, int> expectedMetricProducerMap = { {gauge1Id, gauge1Index}, {gauge2Id, gauge2Index}, {gauge3Id, gauge3Index}, @@ -2630,16 +2678,18 @@ TEST_F(ConfigUpdateTest, TestUpdateDurationMetrics) { vector<Predicate> conditionProtos(5); reverse_copy(config.predicate().begin(), config.predicate().end(), conditionProtos.begin()); for (int i = 0; i < newConditionTrackers.size(); i++) { - EXPECT_TRUE(newConditionTrackers[i]->onConfigUpdated( - conditionProtos, i, newConditionTrackers, newAtomMatchingTrackerMap, - newConditionTrackerMap)); + EXPECT_EQ(newConditionTrackers[i]->onConfigUpdated(conditionProtos, i, newConditionTrackers, + newAtomMatchingTrackerMap, + newConditionTrackerMap), + nullopt); } vector<bool> cycleTracker(5, false); fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated); for (int i = 0; i < newConditionTrackers.size(); i++) { - EXPECT_TRUE(newConditionTrackers[i]->init(conditionProtos, newConditionTrackers, - newConditionTrackerMap, cycleTracker, - conditionCache)); + EXPECT_EQ( + newConditionTrackers[i]->init(conditionProtos, newConditionTrackers, + newConditionTrackerMap, cycleTracker, conditionCache), + nullopt); } // Predicate5 should be true since 2 uids have wakelocks EXPECT_EQ(conditionCache, vector({kTrue, kFalse, kUnknown, kUnknown, kUnknown})); @@ -2664,7 +2714,7 @@ TEST_F(ConfigUpdateTest, TestUpdateDurationMetrics) { unordered_map<int64_t, int> stateAtomIdMap; unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; map<int64_t, uint64_t> stateProtoHashes; - EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)); + EXPECT_EQ(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes), nullopt); // Output data structures to validate. unordered_map<int64_t, int> newMetricProducerMap; @@ -2676,15 +2726,17 @@ TEST_F(ConfigUpdateTest, TestUpdateDurationMetrics) { unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; vector<int> metricsWithActivation; set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{}, - newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, - newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates, - oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers, - conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); + EXPECT_EQ(updateMetrics(key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, + new StatsPullerManager(), oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, /*replacedMatchers=*/{}, + newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, + newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, + replacedStates, oldMetricProducerMap, oldMetricProducers, + newMetricProducerMap, newMetricProducers, conditionToMetricMap, + trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, + replacedMetrics), + nullopt); unordered_map<int64_t, int> expectedMetricProducerMap = { {duration1Id, duration1Index}, {duration2Id, duration2Index}, @@ -2930,7 +2982,7 @@ TEST_F(ConfigUpdateTest, TestUpdateValueMetrics) { unordered_map<int64_t, int> stateAtomIdMap; unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; map<int64_t, uint64_t> stateProtoHashes; - EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)); + EXPECT_EQ(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes), nullopt); // Output data structures to validate. unordered_map<int64_t, int> newMetricProducerMap; @@ -2942,15 +2994,17 @@ TEST_F(ConfigUpdateTest, TestUpdateValueMetrics) { unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; vector<int> metricsWithActivation; set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{}, - newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, - newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates, - oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers, - conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); + EXPECT_EQ(updateMetrics(key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, + new StatsPullerManager(), oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, /*replacedMatchers=*/{}, + newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, + newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, + replacedStates, oldMetricProducerMap, oldMetricProducers, + newMetricProducerMap, newMetricProducers, conditionToMetricMap, + trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, + replacedMetrics), + nullopt); unordered_map<int64_t, int> expectedMetricProducerMap = { {value1Id, value1Index}, {value2Id, value2Index}, {value3Id, value3Index}, @@ -3157,15 +3211,17 @@ TEST_F(ConfigUpdateTest, TestUpdateKllMetrics) { unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; vector<int> metricsWithActivation; set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{}, - newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, - newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{}, - /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, - newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); + EXPECT_EQ(updateMetrics( + key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, + new StatsPullerManager(), oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, /*replacedMatchers=*/{}, newAtomMatchingTrackers, + newConditionTrackerMap, replacedConditions, newConditionTrackers, + conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{}, + /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, + newMetricProducerMap, newMetricProducers, conditionToMetricMap, + trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, replacedMetrics), + nullopt); unordered_map<int64_t, int> expectedMetricProducerMap = { {kll1Id, kll1Index}, {kll2Id, kll2Index}, {kll3Id, kll3Index}, @@ -3328,15 +3384,17 @@ TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) { unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; vector<int> metricsWithActivation; set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, - newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, - newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{}, - /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, - newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); + EXPECT_EQ(updateMetrics(key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, + new StatsPullerManager(), oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers, + newConditionTrackerMap, replacedConditions, newConditionTrackers, + conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{}, + /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, + newMetricProducerMap, newMetricProducers, conditionToMetricMap, + trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, + replacedMetrics), + nullopt); // Verify event activation/deactivation maps. ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 3); @@ -3488,15 +3546,17 @@ TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) { unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; vector<int> metricsWithActivation; set<int64_t> replacedMetrics; - EXPECT_TRUE(updateMetrics( - key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), - oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, - newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{}, - newConditionTrackers, conditionCache, /*stateAtomIdMap*/ {}, /*allStateGroupMaps=*/{}, - /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, - newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); + EXPECT_EQ(updateMetrics(key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, + new StatsPullerManager(), oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers, + newConditionTrackerMap, /*replacedConditions=*/{}, newConditionTrackers, + conditionCache, /*stateAtomIdMap*/ {}, /*allStateGroupMaps=*/{}, + /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, + newMetricProducerMap, newMetricProducers, conditionToMetricMap, + trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, + replacedMetrics), + nullopt); unordered_map<int64_t, int> expectedMetricProducerMap = { {countMetricId, countMetricIndex}, {durationMetricId, durationMetricIndex}, @@ -3581,8 +3641,9 @@ TEST_F(ConfigUpdateTest, TestAlertPreserve) { EXPECT_TRUE(initConfig(config)); UpdateStatus updateStatus = UPDATE_UNKNOWN; - EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers, - /*replacedMetrics*/ {}, updateStatus)); + EXPECT_EQ(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers, + /*replacedMetrics*/ {}, updateStatus), + nullopt); EXPECT_EQ(updateStatus, UPDATE_PRESERVE); } @@ -3599,8 +3660,9 @@ TEST_F(ConfigUpdateTest, TestAlertMetricChanged) { EXPECT_TRUE(initConfig(config)); UpdateStatus updateStatus = UPDATE_UNKNOWN; - EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers, - /*replacedMetrics*/ {metric.id()}, updateStatus)); + EXPECT_EQ(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers, + /*replacedMetrics*/ {metric.id()}, updateStatus), + nullopt); EXPECT_EQ(updateStatus, UPDATE_REPLACE); } @@ -3618,8 +3680,9 @@ TEST_F(ConfigUpdateTest, TestAlertDefinitionChanged) { alert.set_num_buckets(2); UpdateStatus updateStatus = UPDATE_UNKNOWN; - EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers, - /*replacedMetrics*/ {}, updateStatus)); + EXPECT_EQ(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers, + /*replacedMetrics*/ {}, updateStatus), + nullopt); EXPECT_EQ(updateStatus, UPDATE_REPLACE); } @@ -3714,24 +3777,26 @@ TEST_F(ConfigUpdateTest, TestUpdateAlerts) { vector<int> metricsWithActivation; set<int64_t> replacedMetrics; int64_t currentTimeNs = 12345; - EXPECT_TRUE(updateMetrics( - key, config, /*timeBaseNs=*/123, currentTimeNs, new StatsPullerManager(), - oldAtomMatchingTrackerMap, oldAtomMatchingTrackerMap, /*replacedMatchers*/ {}, - oldAtomMatchingTrackers, oldConditionTrackerMap, /*replacedConditions=*/{}, - oldConditionTrackers, {ConditionState::kUnknown}, /*stateAtomIdMap*/ {}, - /*allStateGroupMaps=*/{}, - /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, - newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, replacedMetrics)); + EXPECT_EQ(updateMetrics( + key, config, /*timeBaseNs=*/123, currentTimeNs, new StatsPullerManager(), + oldAtomMatchingTrackerMap, oldAtomMatchingTrackerMap, /*replacedMatchers*/ {}, + oldAtomMatchingTrackers, oldConditionTrackerMap, /*replacedConditions=*/{}, + oldConditionTrackers, {ConditionState::kUnknown}, /*stateAtomIdMap*/ {}, + /*allStateGroupMaps=*/{}, + /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, + newMetricProducerMap, newMetricProducers, conditionToMetricMap, + trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, replacedMetrics), + nullopt); EXPECT_EQ(replacedMetrics, set<int64_t>({countMetricId})); unordered_map<int64_t, int> newAlertTrackerMap; vector<sp<AnomalyTracker>> newAnomalyTrackers; - EXPECT_TRUE(updateAlerts(config, currentTimeNs, newMetricProducerMap, replacedMetrics, - oldAlertTrackerMap, oldAnomalyTrackers, anomalyAlarmMonitor, - newMetricProducers, newAlertTrackerMap, newAnomalyTrackers)); + EXPECT_EQ(updateAlerts(config, currentTimeNs, newMetricProducerMap, replacedMetrics, + oldAlertTrackerMap, oldAnomalyTrackers, anomalyAlarmMonitor, + newMetricProducers, newAlertTrackerMap, newAnomalyTrackers), + nullopt); unordered_map<int64_t, int> expectedAlertMap = { {alert1Id, alert1Index}, @@ -3826,8 +3891,9 @@ TEST_F(ConfigUpdateTest, TestUpdateAlarms) { // Update time is 2 seconds after the base time. int64_t currentTimeNs = timeBaseNs + 2 * NS_PER_SEC; vector<sp<AlarmTracker>> newAlarmTrackers; - EXPECT_TRUE(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs, - newAlarmTrackers)); + EXPECT_EQ(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs, + newAlarmTrackers), + nullopt); ASSERT_EQ(newAlarmTrackers.size(), 3); // Config is updated 2 seconds after statsd start @@ -3857,8 +3923,9 @@ TEST_F(ConfigUpdateTest, TestUpdateAlarms) { // Do another update 60 seconds after config creation time, after the offsets of each alarm. currentTimeNs = timeBaseNs + 60 * NS_PER_SEC; newAlarmTrackers.clear(); - EXPECT_TRUE(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs, - newAlarmTrackers)); + EXPECT_EQ(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs, + newAlarmTrackers), + nullopt); ASSERT_EQ(newAlarmTrackers.size(), 3); // Config is updated one minute after statsd start. @@ -3868,6 +3935,175 @@ TEST_F(ConfigUpdateTest, TestUpdateAlarms) { EXPECT_EQ(newAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10 + 10000); } +TEST_F(ConfigUpdateTest, TestMetricHasMultipleActivations) { + StatsdConfig config; + int64_t metricId = 1; + auto metric_activation1 = config.add_metric_activation(); + metric_activation1->set_metric_id(metricId); + metric_activation1->set_activation_type(ACTIVATE_IMMEDIATELY); + EXPECT_TRUE(initConfig(config)); + + auto metric_activation2 = config.add_metric_activation(); + metric_activation2->set_metric_id(metricId); + metric_activation2->set_activation_type(ACTIVATE_IMMEDIATELY); + + unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; + unordered_map<int64_t, int> newAtomMatchingTrackerMap; + unordered_map<int64_t, int> newConditionTrackerMap; + unordered_map<int64_t, int> newMetricProducerMap; + unordered_map<int64_t, int> stateAtomIdMap; + unordered_map<int, vector<int>> conditionToMetricMap; + unordered_map<int, vector<int>> trackerToMetricMap; + unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; + unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; + set<int64_t> noReportMetricIds; + vector<int> metricsWithActivation; + vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers; + vector<sp<ConditionTracker>> newConditionTrackers; + vector<sp<MetricProducer>> newMetricProducers; + vector<ConditionState> conditionCache; + set<int64_t> replacedMetrics; + set<int64_t> replacedMatchers; + set<int64_t> replacedConditions; + set<int64_t> replacedStates; + EXPECT_EQ(updateMetrics(key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, + new StatsPullerManager(), oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers, + newConditionTrackerMap, replacedConditions, newConditionTrackers, + conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates, + oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, + newMetricProducers, conditionToMetricMap, trackerToMetricMap, + noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, + replacedMetrics), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_HAS_MULTIPLE_ACTIVATIONS, metricId)); +} + +TEST_F(ConfigUpdateTest, TestNoReportMetricNotFound) { + StatsdConfig config; + int64_t metricId = 1; + config.add_no_report_metric(metricId); + + unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; + unordered_map<int64_t, int> newAtomMatchingTrackerMap; + unordered_map<int64_t, int> newConditionTrackerMap; + unordered_map<int64_t, int> newMetricProducerMap; + unordered_map<int64_t, int> stateAtomIdMap; + unordered_map<int, vector<int>> conditionToMetricMap; + unordered_map<int, vector<int>> trackerToMetricMap; + unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; + unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; + set<int64_t> noReportMetricIds; + vector<int> metricsWithActivation; + vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers; + vector<sp<ConditionTracker>> newConditionTrackers; + vector<sp<MetricProducer>> newMetricProducers; + vector<ConditionState> conditionCache; + set<int64_t> replacedMetrics; + set<int64_t> replacedMatchers; + set<int64_t> replacedConditions; + set<int64_t> replacedStates; + EXPECT_EQ(updateMetrics(key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, + new StatsPullerManager(), oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers, + newConditionTrackerMap, replacedConditions, newConditionTrackers, + conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates, + oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, + newMetricProducers, conditionToMetricMap, trackerToMetricMap, + noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, + replacedMetrics), + InvalidConfigReason(INVALID_CONFIG_REASON_NO_REPORT_METRIC_NOT_FOUND, metricId)); +} + +TEST_F(ConfigUpdateTest, TestMetricSlicedStateAtomAllowedFromAnyUid) { + StatsdConfig config; + CountMetric* metric = config.add_count_metric(); + *metric = createCountMetric(/*name=*/"Count", /*what=*/StringToId("ScreenTurnedOn"), + /*condition=*/nullopt, /*states=*/{}); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_state() = CreateScreenState(); + metric->add_slice_by_state(StringToId("ScreenState")); + EXPECT_TRUE(initConfig(config)); + + config.add_whitelisted_atom_ids(util::SCREEN_STATE_CHANGED); + + unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; + unordered_map<int64_t, int> newAtomMatchingTrackerMap; + unordered_map<int64_t, int> newConditionTrackerMap; + unordered_map<int64_t, int> newMetricProducerMap; + unordered_map<int64_t, int> stateAtomIdMap; + unordered_map<int, vector<int>> conditionToMetricMap; + unordered_map<int, vector<int>> trackerToMetricMap; + unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; + unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; + set<int64_t> noReportMetricIds; + vector<int> metricsWithActivation; + vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers; + vector<sp<ConditionTracker>> newConditionTrackers; + vector<sp<MetricProducer>> newMetricProducers; + vector<ConditionState> conditionCache; + set<int64_t> replacedMetrics; + set<int64_t> replacedMatchers; + set<int64_t> replacedConditions; + set<int64_t> replacedStates; + + newAtomMatchingTrackerMap[StringToId("ScreenTurnedOn")] = 0; + stateAtomIdMap[StringToId("ScreenState")] = util::SCREEN_STATE_CHANGED; + EXPECT_EQ( + updateMetrics( + key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, + new StatsPullerManager(), oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, + replacedMatchers, newAtomMatchingTrackers, newConditionTrackerMap, + replacedConditions, newConditionTrackers, conditionCache, stateAtomIdMap, + allStateGroupMaps, replacedStates, oldMetricProducerMap, oldMetricProducers, + newMetricProducerMap, newMetricProducers, conditionToMetricMap, + trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, replacedMetrics), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_SLICED_STATE_ATOM_ALLOWED_FROM_ANY_UID, + StringToId("Count"))); +} + +TEST_F(ConfigUpdateTest, TestMatcherDuplicate) { + StatsdConfig config; + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + EXPECT_TRUE(initConfig(config)); + + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + + unordered_map<int, vector<int>> newTagIds; + unordered_map<int64_t, int> newAtomMatchingTrackerMap; + vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers; + set<int64_t> replacedMatchers; + EXPECT_EQ(updateAtomMatchingTrackers( + config, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, newTagIds, + newAtomMatchingTrackerMap, newAtomMatchingTrackers, replacedMatchers), + createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_MATCHER_DUPLICATE, + StringToId("ScreenTurnedOn"))); +} + +TEST_F(ConfigUpdateTest, TestConditionDuplicate) { + StatsdConfig config; + *config.add_predicate() = CreateScreenIsOnPredicate(); + EXPECT_TRUE(initConfig(config)); + + *config.add_predicate() = CreateScreenIsOnPredicate(); + + unordered_map<int64_t, int> newAtomMatchingTrackerMap; + unordered_map<int64_t, int> newConditionTrackerMap; + vector<sp<ConditionTracker>> newConditionTrackers; + unordered_map<int, vector<int>> trackerToConditionMap; + vector<ConditionState> conditionCache; + set<int64_t> replacedConditions; + set<int64_t> replacedMatchers; + EXPECT_EQ(updateConditions(key, config, newAtomMatchingTrackerMap, replacedMatchers, + oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap, + newConditionTrackers, trackerToConditionMap, conditionCache, + replacedConditions), + createInvalidConfigReasonWithPredicate(INVALID_CONFIG_REASON_CONDITION_DUPLICATE, + StringToId("ScreenIsOn"))); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp index 8aa7f469..a5732b30 100644 --- a/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp +++ b/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp @@ -51,9 +51,43 @@ namespace statsd { namespace { const ConfigKey kConfigKey(0, 12345); +const long timeBaseSec = 1000; const long kAlertId = 3; -const long timeBaseSec = 1000; +sp<UidMap> uidMap = new UidMap(); +sp<StatsPullerManager> pullerManager = new StatsPullerManager(); +sp<AlarmMonitor> anomalyAlarmMonitor; +sp<AlarmMonitor> periodicAlarmMonitor; +unordered_map<int, vector<int>> allTagIdsToMatchersMap; +vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; +unordered_map<int64_t, int> atomMatchingTrackerMap; +vector<sp<ConditionTracker>> allConditionTrackers; +unordered_map<int64_t, int> conditionTrackerMap; +vector<sp<MetricProducer>> allMetricProducers; +unordered_map<int64_t, int> metricProducerMap; +vector<sp<AnomalyTracker>> allAnomalyTrackers; +unordered_map<int64_t, int> alertTrackerMap; +vector<sp<AlarmTracker>> allAlarmTrackers; +unordered_map<int, vector<int>> conditionToMetricMap; +unordered_map<int, vector<int>> trackerToMetricMap; +unordered_map<int, vector<int>> trackerToConditionMap; +unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; +unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; +vector<int> metricsWithActivation; +map<int64_t, uint64_t> stateProtoHashes; +set<int64_t> noReportMetricIds; + +optional<InvalidConfigReason> initConfig(const StatsdConfig& config) { + // initStatsdConfig returns nullopt if config is valid + return initStatsdConfig( + kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, timeBaseSec, allTagIdsToMatchersMap, allAtomMatchingTrackers, + atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, allMetricProducers, + metricProducerMap, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, + trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, + stateProtoHashes, noReportMetricIds); +} StatsdConfig buildGoodConfig() { StatsdConfig config; @@ -63,7 +97,7 @@ StatsdConfig buildGoodConfig() { eventMatcher->set_id(StringToId("SCREEN_IS_ON")); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); + simpleAtomMatcher->set_atom_id(SCREEN_STATE_ATOM_ID); simpleAtomMatcher->add_field_value_matcher()->set_field( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( @@ -73,7 +107,7 @@ StatsdConfig buildGoodConfig() { eventMatcher->set_id(StringToId("SCREEN_IS_OFF")); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); + simpleAtomMatcher->set_atom_id(SCREEN_STATE_ATOM_ID); simpleAtomMatcher->add_field_value_matcher()->set_field( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( @@ -91,7 +125,7 @@ StatsdConfig buildGoodConfig() { metric->set_id(3); metric->set_what(StringToId("SCREEN_IS_ON")); metric->set_bucket(ONE_MINUTE); - metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/); + metric->mutable_dimensions_in_what()->set_field(SCREEN_STATE_ATOM_ID); metric->mutable_dimensions_in_what()->add_child()->set_field(1); config.add_no_report_metric(3); @@ -113,7 +147,7 @@ StatsdConfig buildCircleMatchers() { eventMatcher->set_id(StringToId("SCREEN_IS_ON")); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); + simpleAtomMatcher->set_atom_id(SCREEN_STATE_ATOM_ID); simpleAtomMatcher->add_field_value_matcher()->set_field( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( @@ -135,14 +169,13 @@ StatsdConfig buildAlertWithUnknownMetric() { StatsdConfig config; config.set_id(12345); - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_ON")); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); CountMetric* metric = config.add_count_metric(); metric->set_id(3); - metric->set_what(StringToId("SCREEN_IS_ON")); + metric->set_what(StringToId("ScreenTurnedOn")); metric->set_bucket(ONE_MINUTE); - metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/); + metric->mutable_dimensions_in_what()->set_field(SCREEN_STATE_ATOM_ID); metric->mutable_dimensions_in_what()->add_child()->set_field(1); auto alert = config.add_alert(); @@ -162,7 +195,7 @@ StatsdConfig buildMissingMatchers() { eventMatcher->set_id(StringToId("SCREEN_IS_ON")); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); + simpleAtomMatcher->set_atom_id(SCREEN_STATE_ATOM_ID); simpleAtomMatcher->add_field_value_matcher()->set_field( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( @@ -246,7 +279,7 @@ StatsdConfig buildCirclePredicates() { eventMatcher->set_id(StringToId("SCREEN_IS_ON")); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); + simpleAtomMatcher->set_atom_id(SCREEN_STATE_ATOM_ID); simpleAtomMatcher->add_field_value_matcher()->set_field( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( @@ -256,7 +289,7 @@ StatsdConfig buildCirclePredicates() { eventMatcher->set_id(StringToId("SCREEN_IS_OFF")); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); + simpleAtomMatcher->set_atom_id(SCREEN_STATE_ATOM_ID); simpleAtomMatcher->add_field_value_matcher()->set_field( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( @@ -378,39 +411,34 @@ StatsdConfig buildConfigWithDifferentPredicates() { } } // anonymous namespace -TEST(MetricsManagerTest, TestInitialConditions) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildConfigWithDifferentPredicates(); - unordered_map<int, vector<int>> allTagIdsToMatchersMap; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - vector<sp<AnomalyTracker>> allAnomalyTrackers; - vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - unordered_map<int, vector<int>> trackerToConditionMap; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - - EXPECT_TRUE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIdsToMatchersMap, allAtomMatchingTrackers, - atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, allMetricProducers, - metricProducerMap, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); +class MetricsManagerUtilTest : public ::testing::Test { +public: + void SetUp() override { + allTagIdsToMatchersMap.clear(); + allAtomMatchingTrackers.clear(); + atomMatchingTrackerMap.clear(); + allConditionTrackers.clear(); + conditionTrackerMap.clear(); + allMetricProducers.clear(); + metricProducerMap.clear(); + allAnomalyTrackers.clear(); + allAlarmTrackers.clear(); + conditionToMetricMap.clear(); + trackerToMetricMap.clear(); + trackerToConditionMap.clear(); + activationAtomTrackerToMetricMap.clear(); + deactivationAtomTrackerToMetricMap.clear(); + alertTrackerMap.clear(); + metricsWithActivation.clear(); + stateProtoHashes.clear(); + noReportMetricIds.clear(); + StateManager::getInstance().clear(); + } +}; + +TEST_F(MetricsManagerUtilTest, TestInitialConditions) { + // initConfig returns nullopt if config is valid + EXPECT_EQ(initConfig(buildConfigWithDifferentPredicates()), nullopt); ASSERT_EQ(4u, allMetricProducers.size()); ASSERT_EQ(5u, allConditionTrackers.size()); @@ -431,44 +459,15 @@ TEST(MetricsManagerTest, TestInitialConditions) { EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[3]->mCondition); EXPECT_EQ(allTagIdsToMatchersMap.size(), 3); - EXPECT_EQ(allTagIdsToMatchersMap[util::SCREEN_STATE_CHANGED].size(), 2); + EXPECT_EQ(allTagIdsToMatchersMap[SCREEN_STATE_ATOM_ID].size(), 2); EXPECT_EQ(allTagIdsToMatchersMap[util::PLUGGED_STATE_CHANGED].size(), 2); EXPECT_EQ(allTagIdsToMatchersMap[util::SUBSYSTEM_SLEEP_STATE].size(), 1); } -TEST(MetricsManagerTest, TestGoodConfig) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; +TEST_F(MetricsManagerUtilTest, TestGoodConfig) { StatsdConfig config = buildGoodConfig(); - unordered_map<int, vector<int>> allTagIdsToMatchersMap; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - vector<sp<AnomalyTracker>> allAnomalyTrackers; - vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - unordered_map<int, vector<int>> trackerToConditionMap; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - - EXPECT_TRUE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIdsToMatchersMap, allAtomMatchingTrackers, - atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, allMetricProducers, - metricProducerMap, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); + // initConfig returns nullopt if config is valid + EXPECT_EQ(initConfig(config), nullopt); ASSERT_EQ(1u, allMetricProducers.size()); EXPECT_THAT(metricProducerMap, UnorderedElementsAre(Pair(config.count_metric(0).id(), 0))); ASSERT_EQ(1u, allAnomalyTrackers.size()); @@ -478,247 +477,744 @@ TEST(MetricsManagerTest, TestGoodConfig) { EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0); } -TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildDimensionMetricsWithMultiTags(); - unordered_map<int, vector<int>> allTagIdsToMatchersMap; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - vector<sp<AnomalyTracker>> allAnomalyTrackers; - vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - unordered_map<int, vector<int>> trackerToConditionMap; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - - EXPECT_FALSE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIdsToMatchersMap, allAtomMatchingTrackers, - atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, allMetricProducers, - metricProducerMap, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); +TEST_F(MetricsManagerUtilTest, TestDimensionMetricsWithMultiTags) { + EXPECT_EQ(initConfig(buildDimensionMetricsWithMultiTags()), + createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_METRIC_MATCHER_MORE_THAN_ONE_ATOM, /*metric id=*/3, + StringToId("BATTERY_LOW"))); } -TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildCircleMatchers(); - unordered_map<int, vector<int>> allTagIdsToMatchersMap; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - vector<sp<AnomalyTracker>> allAnomalyTrackers; - vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - unordered_map<int, vector<int>> trackerToConditionMap; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - - EXPECT_FALSE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIdsToMatchersMap, allAtomMatchingTrackers, - atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, allMetricProducers, - metricProducerMap, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); +TEST_F(MetricsManagerUtilTest, TestCircleLogMatcherDependency) { + optional<InvalidConfigReason> expectedInvalidConfigReason = + createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_MATCHER_CYCLE, + StringToId("SCREEN_ON_OR_OFF")); + expectedInvalidConfigReason->matcherIds.push_back(StringToId("SCREEN_ON_OR_OFF")); + + EXPECT_EQ(initConfig(buildCircleMatchers()), expectedInvalidConfigReason); } -TEST(MetricsManagerTest, TestMissingMatchers) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildMissingMatchers(); - unordered_map<int, vector<int>> allTagIdsToMatchersMap; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - vector<sp<AnomalyTracker>> allAnomalyTrackers; - vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - unordered_map<int, vector<int>> trackerToConditionMap; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - EXPECT_FALSE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIdsToMatchersMap, allAtomMatchingTrackers, - atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, allMetricProducers, - metricProducerMap, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); +TEST_F(MetricsManagerUtilTest, TestMissingMatchers) { + optional<InvalidConfigReason> expectedInvalidConfigReason = + createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_MATCHER_CHILD_NOT_FOUND, + StringToId("SCREEN_ON_OR_OFF")); + expectedInvalidConfigReason->matcherIds.push_back(StringToId("ABC")); + + EXPECT_EQ(initConfig(buildMissingMatchers()), expectedInvalidConfigReason); } -TEST(MetricsManagerTest, TestMissingPredicate) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildMissingPredicate(); - unordered_map<int, vector<int>> allTagIdsToMatchersMap; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - vector<sp<AnomalyTracker>> allAnomalyTrackers; - vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - unordered_map<int, vector<int>> trackerToConditionMap; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - EXPECT_FALSE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIdsToMatchersMap, allAtomMatchingTrackers, - atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, allMetricProducers, - metricProducerMap, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); +TEST_F(MetricsManagerUtilTest, TestMissingPredicate) { + EXPECT_EQ( + initConfig(buildMissingPredicate()), + createInvalidConfigReasonWithPredicate(INVALID_CONFIG_REASON_METRIC_CONDITION_NOT_FOUND, + /*metric id=*/3, StringToId("SOME_CONDITION"))); } -TEST(MetricsManagerTest, TestCirclePredicateDependency) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildCirclePredicates(); - unordered_map<int, vector<int>> allTagIdsToMatchersMap; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - vector<sp<AnomalyTracker>> allAnomalyTrackers; - vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - unordered_map<int, vector<int>> trackerToConditionMap; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - - EXPECT_FALSE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIdsToMatchersMap, allAtomMatchingTrackers, - atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, allMetricProducers, - metricProducerMap, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); +TEST_F(MetricsManagerUtilTest, TestCirclePredicateDependency) { + optional<InvalidConfigReason> expectedInvalidConfigReason = + createInvalidConfigReasonWithPredicate(INVALID_CONFIG_REASON_CONDITION_CYCLE, + StringToId("SCREEN_IS_EITHER_ON_OFF")); + expectedInvalidConfigReason->conditionIds.push_back(StringToId("SCREEN_IS_EITHER_ON_OFF")); + + EXPECT_EQ(initConfig(buildCirclePredicates()), expectedInvalidConfigReason); } -TEST(MetricsManagerTest, testAlertWithUnknownMetric) { - sp<UidMap> uidMap = new UidMap(); - sp<StatsPullerManager> pullerManager = new StatsPullerManager(); - sp<AlarmMonitor> anomalyAlarmMonitor; - sp<AlarmMonitor> periodicAlarmMonitor; - StatsdConfig config = buildAlertWithUnknownMetric(); - unordered_map<int, vector<int>> allTagIdsToMatchersMap; - vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> atomMatchingTrackerMap; - vector<sp<ConditionTracker>> allConditionTrackers; - unordered_map<int64_t, int> conditionTrackerMap; - vector<sp<MetricProducer>> allMetricProducers; - unordered_map<int64_t, int> metricProducerMap; - vector<sp<AnomalyTracker>> allAnomalyTrackers; - vector<sp<AlarmTracker>> allAlarmTrackers; - unordered_map<int, vector<int>> conditionToMetricMap; - unordered_map<int, vector<int>> trackerToMetricMap; - unordered_map<int, vector<int>> trackerToConditionMap; - unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; - unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; - unordered_map<int64_t, int> alertTrackerMap; - vector<int> metricsWithActivation; - map<int64_t, uint64_t> stateProtoHashes; - std::set<int64_t> noReportMetricIds; - - EXPECT_FALSE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIdsToMatchersMap, allAtomMatchingTrackers, - atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, allMetricProducers, - metricProducerMap, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - stateProtoHashes, noReportMetricIds)); +TEST_F(MetricsManagerUtilTest, TestAlertWithUnknownMetric) { + EXPECT_EQ(initConfig(buildAlertWithUnknownMetric()), + createInvalidConfigReasonWithAlert(INVALID_CONFIG_REASON_ALERT_METRIC_NOT_FOUND, + /*metric id=*/2, /*matcher id=*/3)); +} + +TEST_F(MetricsManagerUtilTest, TestMetricWithMultipleActivations) { + StatsdConfig config; + int64_t metricId = 1; + auto metric_activation1 = config.add_metric_activation(); + metric_activation1->set_metric_id(metricId); + metric_activation1->set_activation_type(ACTIVATE_IMMEDIATELY); + auto metric_activation2 = config.add_metric_activation(); + metric_activation2->set_metric_id(metricId); + metric_activation2->set_activation_type(ACTIVATE_IMMEDIATELY); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_HAS_MULTIPLE_ACTIVATIONS, metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestCountMetricMissingIdOrWhat) { + StatsdConfig config; + int64_t metricId = 1; + CountMetric* metric = config.add_count_metric(); + metric->set_id(metricId); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_MISSING_ID_OR_WHAT, metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestCountMetricConditionlinkNoCondition) { + StatsdConfig config; + CountMetric* metric = config.add_count_metric(); + *metric = createCountMetric(/*name=*/"Count", /*what=*/StringToId("ScreenTurnedOn"), + /*condition=*/nullopt, /*states=*/{}); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + + auto link = metric->add_links(); + link->set_condition(1); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_CONDITIONLINK_NO_CONDITION, + StringToId("Count"))); +} + +TEST_F(MetricsManagerUtilTest, TestDurationMetricMissingIdOrWhat) { + StatsdConfig config; + int64_t metricId = 1; + DurationMetric* metric = config.add_duration_metric(); + metric->set_id(metricId); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_MISSING_ID_OR_WHAT, metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestDurationMetricConditionlinkNoCondition) { + StatsdConfig config; + DurationMetric* metric = config.add_duration_metric(); + *metric = createDurationMetric(/*name=*/"Duration", /*what=*/StringToId("ScreenIsOn"), + /*condition=*/nullopt, /*states=*/{}); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + *config.add_predicate() = CreateScreenIsOnPredicate(); + + auto link = metric->add_links(); + link->set_condition(1); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_CONDITIONLINK_NO_CONDITION, + StringToId("Duration"))); +} + +TEST_F(MetricsManagerUtilTest, TestGaugeMetricMissingIdOrWhat) { + StatsdConfig config; + int64_t metricId = 1; + GaugeMetric* metric = config.add_gauge_metric(); + metric->set_id(metricId); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_MISSING_ID_OR_WHAT, metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestGaugeMetricConditionlinkNoCondition) { + StatsdConfig config; + GaugeMetric* metric = config.add_gauge_metric(); + *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("ScreenTurnedOn"), + /*samplingType=*/GaugeMetric_SamplingType_FIRST_N_SAMPLES, + /*condition=*/nullopt, /*triggerEvent=*/nullopt); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + + auto link = metric->add_links(); + link->set_condition(1); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_CONDITIONLINK_NO_CONDITION, + StringToId("Gauge"))); +} + +TEST_F(MetricsManagerUtilTest, TestEventMetricMissingIdOrWhat) { + StatsdConfig config; + int64_t metricId = 1; + EventMetric* metric = config.add_event_metric(); + metric->set_id(metricId); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_MISSING_ID_OR_WHAT, metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestEventMetricConditionlinkNoCondition) { + StatsdConfig config; + EventMetric* metric = config.add_event_metric(); + *metric = createEventMetric(/*name=*/"Event", /*what=*/StringToId("ScreenTurnedOn"), + /*condition=*/nullopt); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + + auto link = metric->add_links(); + link->set_condition(1); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_CONDITIONLINK_NO_CONDITION, + StringToId("Event"))); +} + +TEST_F(MetricsManagerUtilTest, TestNumericValueMetricMissingIdOrWhat) { + StatsdConfig config; + int64_t metricId = 1; + ValueMetric* metric = config.add_value_metric(); + metric->set_id(metricId); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_MISSING_ID_OR_WHAT, metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestNumericValueMetricConditionlinkNoCondition) { + StatsdConfig config; + ValueMetric* metric = config.add_value_metric(); + *metric = createValueMetric(/*name=*/"NumericValue", /*what=*/CreateScreenTurnedOnAtomMatcher(), + /*valueField=*/2, /*condition=*/nullopt, /*states=*/{}); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + + auto link = metric->add_links(); + link->set_condition(1); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_CONDITIONLINK_NO_CONDITION, + StringToId("NumericValue"))); +} + +TEST_F(MetricsManagerUtilTest, TestKllMetricMissingIdOrWhat) { + StatsdConfig config; + int64_t metricId = 1; + KllMetric* metric = config.add_kll_metric(); + metric->set_id(metricId); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_MISSING_ID_OR_WHAT, metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestKllMetricConditionlinkNoCondition) { + StatsdConfig config; + KllMetric* metric = config.add_kll_metric(); + *metric = createKllMetric(/*name=*/"Kll", /*what=*/CreateScreenTurnedOnAtomMatcher(), + /*valueField=*/2, /*condition=*/nullopt); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + + auto link = metric->add_links(); + link->set_condition(1); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_CONDITIONLINK_NO_CONDITION, + StringToId("Kll"))); +} + +TEST_F(MetricsManagerUtilTest, TestMetricMatcherNotFound) { + StatsdConfig config; + *config.add_count_metric() = + createCountMetric(/*name=*/"Count", /*what=*/StringToId("SOME MATCHER"), + /*condition=*/nullopt, /*states=*/{}); + + EXPECT_EQ(initConfig(config), createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_METRIC_MATCHER_NOT_FOUND, + StringToId("Count"), StringToId("SOME MATCHER"))); +} + +TEST_F(MetricsManagerUtilTest, TestMetricConditionLinkNotFound) { + StatsdConfig config; + CountMetric* metric = config.add_count_metric(); + *metric = createCountMetric(/*name=*/"Count", /*what=*/StringToId("ScreenTurnedOn"), + /*condition=*/StringToId("ScreenIsOn"), /*states=*/{}); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_predicate() = CreateScreenIsOnPredicate(); + + auto link = metric->add_links(); + link->set_condition(StringToId("SOME CONDITION")); + + EXPECT_EQ(initConfig(config), createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_METRIC_CONDITION_LINK_NOT_FOUND, + StringToId("Count"), StringToId("SOME CONDITION"))); +} + +TEST_F(MetricsManagerUtilTest, TestMetricStateNotFound) { + StatsdConfig config; + *config.add_count_metric() = + createCountMetric(/*name=*/"Count", /*what=*/StringToId("ScreenTurnedOn"), + /*condition=*/nullopt, /*states=*/{StringToId("SOME STATE")}); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + + EXPECT_EQ(initConfig(config), + createInvalidConfigReasonWithState(INVALID_CONFIG_REASON_METRIC_STATE_NOT_FOUND, + StringToId("Count"), StringToId("SOME STATE"))); +} + +TEST_F(MetricsManagerUtilTest, TestMetricStatelinkNoState) { + StatsdConfig config; + CountMetric* metric = config.add_count_metric(); + *metric = createCountMetric(/*name=*/"Count", /*what=*/StringToId("ScreenTurnedOn"), + /*condition=*/nullopt, /*states=*/{}); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + + auto link = metric->add_state_link(); + link->set_state_atom_id(2); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_STATELINK_NO_STATE, + StringToId("Count"))); +} + +TEST_F(MetricsManagerUtilTest, TestMetricBadThreshold) { + StatsdConfig config; + CountMetric* metric = config.add_count_metric(); + *metric = createCountMetric(/*name=*/"Count", /*what=*/StringToId("ScreenTurnedOn"), + /*condition=*/nullopt, /*states=*/{}); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + + metric->mutable_threshold()->set_lt_float(1.0); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_BAD_THRESHOLD, StringToId("Count"))); +} + +TEST_F(MetricsManagerUtilTest, TestMetricActivationMatcherNotFound) { + StatsdConfig config; + *config.add_count_metric() = + createCountMetric(/*name=*/"Count", /*what=*/StringToId("ScreenTurnedOn"), + /*condition=*/nullopt, /*states=*/{}); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + auto metric_activation = config.add_metric_activation(); + metric_activation->set_metric_id(StringToId("Count")); + metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); + auto event_activation = metric_activation->add_event_activation(); + + event_activation->set_atom_matcher_id(StringToId("SOME_MATCHER")); + + EXPECT_EQ(initConfig(config), createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_METRIC_ACTIVATION_MATCHER_NOT_FOUND, + StringToId("Count"), StringToId("SOME_MATCHER"))); +} + +TEST_F(MetricsManagerUtilTest, TestMetricDeactivationMatcherNotFound) { + StatsdConfig config; + *config.add_count_metric() = + createCountMetric(/*name=*/"Count", /*what=*/StringToId("ScreenTurnedOn"), + /*condition=*/nullopt, /*states=*/{}); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + auto metric_activation = config.add_metric_activation(); + metric_activation->set_metric_id(StringToId("Count")); + metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); + auto event_activation = metric_activation->add_event_activation(); + event_activation->set_atom_matcher_id(StringToId("ScreenTurnedOn")); + + event_activation->set_deactivation_atom_matcher_id(StringToId("SOME_MATCHER")); + + EXPECT_EQ(initConfig(config), + createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_METRIC_DEACTIVATION_MATCHER_NOT_FOUND, + StringToId("Count"), StringToId("SOME_MATCHER"))); +} + +TEST_F(MetricsManagerUtilTest, TestMetricSlicedStateAtomAllowedFromAnyUid) { + StatsdConfig config; + CountMetric* metric = config.add_count_metric(); + *metric = createCountMetric(/*name=*/"Count", /*what=*/StringToId("ScreenTurnedOn"), + /*condition=*/nullopt, /*states=*/{}); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + + *config.add_state() = CreateScreenState(); + metric->add_slice_by_state(StringToId("ScreenState")); + config.add_whitelisted_atom_ids(util::SCREEN_STATE_CHANGED); + + EXPECT_EQ( + initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_SLICED_STATE_ATOM_ALLOWED_FROM_ANY_UID, + StringToId("Count"))); +} + +TEST_F(MetricsManagerUtilTest, TestDurationMetricWhatNotSimple) { + StatsdConfig config; + *config.add_duration_metric() = + createDurationMetric(/*name=*/"Duration", /*what=*/StringToId("ScreenIsEitherOnOff"), + /*condition=*/nullopt, /*states=*/{}); + *config.add_predicate() = CreateScreenIsOnPredicate(); + *config.add_predicate() = CreateScreenIsOffPredicate(); + + auto condition = config.add_predicate(); + condition->set_id(StringToId("ScreenIsEitherOnOff")); + Predicate_Combination* combination = condition->mutable_combination(); + combination->set_operation(LogicalOperation::OR); + combination->add_predicate(StringToId("ScreenIsOn")); + combination->add_predicate(StringToId("ScreenIsOff")); + + EXPECT_EQ(initConfig(config), + createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_DURATION_METRIC_WHAT_NOT_SIMPLE, StringToId("Duration"), + StringToId("ScreenIsEitherOnOff"))); +} + +TEST_F(MetricsManagerUtilTest, TestDurationMetricWhatNotFound) { + StatsdConfig config; + int64_t metricId = 1; + DurationMetric* metric = config.add_duration_metric(); + metric->set_id(metricId); + + metric->set_what(StringToId("SOME WHAT")); + + EXPECT_EQ(initConfig(config), createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_DURATION_METRIC_WHAT_NOT_FOUND, + metricId, StringToId("SOME WHAT"))); +} + +TEST_F(MetricsManagerUtilTest, TestDurationMetricMissingStart) { + StatsdConfig config; + *config.add_duration_metric() = + createDurationMetric(/*name=*/"Duration", /*what=*/StringToId("SCREEN_IS_ON"), + /*condition=*/nullopt, /*states=*/{}); + auto condition = config.add_predicate(); + condition->set_id(StringToId("SCREEN_IS_ON")); + + SimplePredicate* simplePredicate = condition->mutable_simple_predicate(); + simplePredicate->set_stop(StringToId("SCREEN_IS_OFF")); + + EXPECT_EQ(initConfig(config), createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_DURATION_METRIC_MISSING_START, + StringToId("Duration"), StringToId("SCREEN_IS_ON"))); +} + +TEST_F(MetricsManagerUtilTest, TestDurationMetricMaxSparseHasSpliceByState) { + StatsdConfig config; + DurationMetric* metric = config.add_duration_metric(); + *metric = createDurationMetric(/*name=*/"Duration", /*what=*/StringToId("ScreenIsOn"), + /*condition=*/nullopt, /*states=*/{}); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + *config.add_predicate() = CreateScreenIsOnPredicate(); + *config.add_state() = CreateScreenState(); + + metric->add_slice_by_state(StringToId("ScreenState")); + metric->set_aggregation_type(DurationMetric::MAX_SPARSE); + + EXPECT_EQ( + initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_DURATION_METRIC_MAX_SPARSE_HAS_SLICE_BY_STATE, + StringToId("Duration"))); } -TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerInvalidMatcher) { +TEST_F(MetricsManagerUtilTest, TestValueMetricMissingValueField) { + StatsdConfig config; + int64_t metricId = 1; + ValueMetric* metric = config.add_value_metric(); + metric->set_id(metricId); + metric->set_what(1); + + EXPECT_EQ( + initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_MISSING_VALUE_FIELD, metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestValueMetricValueFieldHasPositionAll) { + StatsdConfig config; + int64_t metricId = 1; + ValueMetric* metric = config.add_value_metric(); + metric->set_id(metricId); + metric->set_what(1); + + metric->mutable_value_field()->set_position(ALL); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_VALUE_FIELD_HAS_POSITION_ALL, + metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestValueMetricHasIncorrectValueField) { + StatsdConfig config; + int64_t metricId = 1; + ValueMetric* metric = config.add_value_metric(); + metric->set_id(metricId); + metric->set_what(1); + + metric->mutable_value_field()->set_position(ANY); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_HAS_INCORRECT_VALUE_FIELD, + metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestKllMetricMissingKllField) { + StatsdConfig config; + int64_t metricId = 1; + KllMetric* metric = config.add_kll_metric(); + metric->set_id(metricId); + metric->set_what(1); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_KLL_METRIC_MISSING_KLL_FIELD, metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestKllMetricKllFieldHasPositionAll) { + StatsdConfig config; + int64_t metricId = 1; + KllMetric* metric = config.add_kll_metric(); + metric->set_id(metricId); + metric->set_what(1); + + metric->mutable_kll_field()->set_position(ALL); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_KLL_METRIC_KLL_FIELD_HAS_POSITION_ALL, + metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestKllMetricHasIncorrectKllField) { + StatsdConfig config; + int64_t metricId = 1; + KllMetric* metric = config.add_kll_metric(); + metric->set_id(metricId); + metric->set_what(1); + + metric->mutable_kll_field()->set_position(ANY); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_KLL_METRIC_HAS_INCORRECT_KLL_FIELD, + metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestGaugeMetricIncorrectFieldFilter) { + StatsdConfig config; + int64_t metricId = 1; + GaugeMetric* metric = config.add_gauge_metric(); + metric->set_id(metricId); + metric->set_what(1); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_GAUGE_METRIC_INCORRECT_FIELD_FILTER, + metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestGaugeMetricTriggerNoPullAtom) { + StatsdConfig config; + int64_t metricId = 1; + GaugeMetric* metric = config.add_gauge_metric(); + metric->set_id(metricId); + metric->set_what(StringToId("ScreenTurnedOn")); + metric->mutable_gauge_fields_filter()->set_include_all(true); + metric->set_trigger_event(1); + + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + + EXPECT_EQ( + initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_GAUGE_METRIC_TRIGGER_NO_PULL_ATOM, metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestGaugeMetricTriggerNoFirstNSamples) { + StatsdConfig config; + int64_t metricId = 1; + GaugeMetric* metric = config.add_gauge_metric(); + metric->set_id(metricId); + metric->set_what(StringToId("Matcher")); + *config.add_atom_matcher() = + CreateSimpleAtomMatcher(/*name=*/"Matcher", /*atomId=*/util::SUBSYSTEM_SLEEP_STATE); + + metric->mutable_gauge_fields_filter()->set_include_all(true); + metric->set_trigger_event(StringToId("Matcher")); + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_GAUGE_METRIC_TRIGGER_NO_FIRST_N_SAMPLES, + metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestGaugeMetricFirstNSamplesWithWrongEvent) { + StatsdConfig config; + int64_t metricId = 1; + GaugeMetric* metric = config.add_gauge_metric(); + metric->set_id(metricId); + metric->set_what(StringToId("Matcher")); + *config.add_atom_matcher() = + CreateSimpleAtomMatcher(/*name=*/"Matcher", /*atomId=*/util::SUBSYSTEM_SLEEP_STATE); + + metric->mutable_gauge_fields_filter()->set_include_all(true); + metric->set_sampling_type(GaugeMetric_SamplingType_FIRST_N_SAMPLES); + + EXPECT_EQ( + initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_GAUGE_METRIC_FIRST_N_SAMPLES_WITH_WRONG_EVENT, + metricId)); +} + +TEST_F(MetricsManagerUtilTest, TestMatcherDuplicate) { + StatsdConfig config; + + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + + EXPECT_EQ(initConfig(config), + createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_MATCHER_DUPLICATE, + StringToId("ScreenTurnedOn"))); +} + +TEST_F(MetricsManagerUtilTest, TestMatcherNoOperation) { + StatsdConfig config; + int64_t matcherId = 1; + + AtomMatcher* matcher = config.add_atom_matcher(); + matcher->set_id(matcherId); + matcher->mutable_combination()->add_matcher(StringToId("ScreenTurnedOn")); + + EXPECT_EQ(initConfig(config), createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_NO_OPERATION, matcherId)); +} + +TEST_F(MetricsManagerUtilTest, TestMatcherNotOperationIsNotUnary) { + StatsdConfig config; + int64_t matcherId = 1; + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + + AtomMatcher* matcher = config.add_atom_matcher(); + matcher->set_id(matcherId); + matcher->mutable_combination()->set_operation(LogicalOperation::NOT); + matcher->mutable_combination()->add_matcher(StringToId("ScreenTurnedOn")); + matcher->mutable_combination()->add_matcher(StringToId("ScreenTurnedOff")); + + EXPECT_EQ(initConfig(config), + createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_NOT_OPERATION_IS_NOT_UNARY, matcherId)); +} + +TEST_F(MetricsManagerUtilTest, TestConditionChildNotFound) { + StatsdConfig config; + int64_t conditionId = 1; + int64_t childConditionId = 2; + + Predicate* condition = config.add_predicate(); + condition->set_id(conditionId); + condition->mutable_combination()->set_operation(LogicalOperation::NOT); + condition->mutable_combination()->add_predicate(childConditionId); + + optional<InvalidConfigReason> expectedInvalidConfigReason = + createInvalidConfigReasonWithPredicate(INVALID_CONFIG_REASON_CONDITION_CHILD_NOT_FOUND, + conditionId); + expectedInvalidConfigReason->conditionIds.push_back(childConditionId); + EXPECT_EQ(initConfig(config), expectedInvalidConfigReason); +} + +TEST_F(MetricsManagerUtilTest, TestConditionDuplicate) { + StatsdConfig config; + *config.add_predicate() = CreateScreenIsOnPredicate(); + *config.add_predicate() = CreateScreenIsOnPredicate(); + + EXPECT_EQ(initConfig(config), + createInvalidConfigReasonWithPredicate(INVALID_CONFIG_REASON_CONDITION_DUPLICATE, + StringToId("ScreenIsOn"))); +} + +TEST_F(MetricsManagerUtilTest, TestConditionNoOperation) { + StatsdConfig config; + int64_t conditionId = 1; + *config.add_predicate() = CreateScreenIsOnPredicate(); + + Predicate* condition = config.add_predicate(); + condition->set_id(conditionId); + condition->mutable_combination()->add_predicate(StringToId("ScreenIsOn")); + + EXPECT_EQ(initConfig(config), + createInvalidConfigReasonWithPredicate(INVALID_CONFIG_REASON_CONDITION_NO_OPERATION, + conditionId)); +} + +TEST_F(MetricsManagerUtilTest, TestConditionNotOperationIsNotUnary) { + StatsdConfig config; + int64_t conditionId = 1; + *config.add_predicate() = CreateScreenIsOnPredicate(); + *config.add_predicate() = CreateScreenIsOffPredicate(); + + Predicate* condition = config.add_predicate(); + condition->set_id(conditionId); + condition->mutable_combination()->set_operation(LogicalOperation::NOT); + condition->mutable_combination()->add_predicate(StringToId("ScreenIsOn")); + condition->mutable_combination()->add_predicate(StringToId("ScreenIsOff")); + + EXPECT_EQ(initConfig(config), + createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_NOT_OPERATION_IS_NOT_UNARY, conditionId)); +} + +TEST_F(MetricsManagerUtilTest, TestSubscriptionRuleNotFoundAlert) { + StatsdConfig config; + int64_t alertId = 1; + *config.add_subscription() = createSubscription("Subscription", Subscription::ALERT, alertId); + + EXPECT_EQ(initConfig(config), createInvalidConfigReasonWithSubscriptionAndAlert( + INVALID_CONFIG_REASON_SUBSCRIPTION_RULE_NOT_FOUND, + StringToId("Subscription"), alertId)); +} + +TEST_F(MetricsManagerUtilTest, TestSubscriptionRuleNotFoundAlarm) { + StatsdConfig config; + int64_t alarmId = 1; + *config.add_subscription() = createSubscription("Subscription", Subscription::ALARM, alarmId); + + EXPECT_EQ(initConfig(config), createInvalidConfigReasonWithSubscriptionAndAlarm( + INVALID_CONFIG_REASON_SUBSCRIPTION_RULE_NOT_FOUND, + StringToId("Subscription"), alarmId)); +} + +TEST_F(MetricsManagerUtilTest, TestSubscriptionSubscriberInfoMissing) { + StatsdConfig config; + Subscription subscription = + createSubscription("Subscription", Subscription::ALERT, /*alert id=*/1); + subscription.clear_subscriber_information(); + *config.add_subscription() = subscription; + + EXPECT_EQ(initConfig(config), + createInvalidConfigReasonWithSubscription( + INVALID_CONFIG_REASON_SUBSCRIPTION_SUBSCRIBER_INFO_MISSING, + StringToId("Subscription"))); +} + +TEST_F(MetricsManagerUtilTest, TestAlarmPeriodLessThanOrEqualZero) { + StatsdConfig config; + *config.add_alarm() = createAlarm("Alarm", /*offset=*/1, /*period=*/-1); + + EXPECT_EQ(initConfig(config), + createInvalidConfigReasonWithAlarm( + INVALID_CONFIG_REASON_ALARM_PERIOD_LESS_THAN_OR_EQUAL_ZERO, + StringToId("Alarm"))); +} + +TEST_F(MetricsManagerUtilTest, TestAlarmOffsetLessThanOrEqualZero) { + StatsdConfig config; + *config.add_alarm() = createAlarm("Alarm", /*offset=*/-1, /*period=*/1); + + EXPECT_EQ(initConfig(config), + createInvalidConfigReasonWithAlarm( + INVALID_CONFIG_REASON_ALARM_OFFSET_LESS_THAN_OR_EQUAL_ZERO, + StringToId("Alarm"))); +} + +TEST_F(MetricsManagerUtilTest, TestCreateAtomMatchingTrackerInvalidMatcher) { sp<UidMap> uidMap = new UidMap(); AtomMatcher matcher; // Matcher has no contents_case (simple/combination), so it is invalid. matcher.set_id(21); - EXPECT_EQ(createAtomMatchingTracker(matcher, 0, uidMap), nullptr); + optional<InvalidConfigReason> invalidConfigReason; + EXPECT_EQ(createAtomMatchingTracker(matcher, 0, uidMap, invalidConfigReason), nullptr); + EXPECT_EQ(invalidConfigReason, + createInvalidConfigReasonWithMatcher( + INVALID_CONFIG_REASON_MATCHER_MALFORMED_CONTENTS_CASE, matcher.id())); } -TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerSimple) { +TEST_F(MetricsManagerUtilTest, TestCreateAtomMatchingTrackerSimple) { int index = 1; int64_t id = 123; sp<UidMap> uidMap = new UidMap(); AtomMatcher matcher; matcher.set_id(id); SimpleAtomMatcher* simpleAtomMatcher = matcher.mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(util::SCREEN_STATE_CHANGED); + simpleAtomMatcher->set_atom_id(SCREEN_STATE_ATOM_ID); simpleAtomMatcher->add_field_value_matcher()->set_field( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( android::view::DisplayStateEnum::DISPLAY_STATE_ON); - sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, index, uidMap); + optional<InvalidConfigReason> invalidConfigReason; + sp<AtomMatchingTracker> tracker = + createAtomMatchingTracker(matcher, index, uidMap, invalidConfigReason); EXPECT_NE(tracker, nullptr); + EXPECT_EQ(invalidConfigReason, nullopt); EXPECT_TRUE(tracker->mInitialized); EXPECT_EQ(tracker->getId(), id); EXPECT_EQ(tracker->mIndex, index); const set<int>& atomIds = tracker->getAtomIds(); ASSERT_EQ(atomIds.size(), 1); - EXPECT_EQ(atomIds.count(util::SCREEN_STATE_CHANGED), 1); + EXPECT_EQ(atomIds.count(SCREEN_STATE_ATOM_ID), 1); } -TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination) { +TEST_F(MetricsManagerUtilTest, TestCreateAtomMatchingTrackerCombination) { int index = 1; int64_t id = 123; sp<UidMap> uidMap = new UidMap(); @@ -729,8 +1225,11 @@ TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination) { combination->add_matcher(123); combination->add_matcher(223); - sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, index, uidMap); + optional<InvalidConfigReason> invalidConfigReason; + sp<AtomMatchingTracker> tracker = + createAtomMatchingTracker(matcher, index, uidMap, invalidConfigReason); EXPECT_NE(tracker, nullptr); + EXPECT_EQ(invalidConfigReason, nullopt); // Combination matchers need to be initialized first. EXPECT_FALSE(tracker->mInitialized); @@ -740,16 +1239,21 @@ TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination) { ASSERT_EQ(atomIds.size(), 0); } -TEST(MetricsManagerTest, TestCreateConditionTrackerInvalid) { +TEST_F(MetricsManagerUtilTest, TestCreateConditionTrackerInvalid) { const ConfigKey key(123, 456); // Predicate has no contents_case (simple/combination), so it is invalid. Predicate predicate; predicate.set_id(21); unordered_map<int64_t, int> atomTrackerMap; - EXPECT_EQ(createConditionTracker(key, predicate, 0, atomTrackerMap), nullptr); + optional<InvalidConfigReason> invalidConfigReason; + EXPECT_EQ(createConditionTracker(key, predicate, 0, atomTrackerMap, invalidConfigReason), + nullptr); + EXPECT_EQ(invalidConfigReason, + createInvalidConfigReasonWithPredicate( + INVALID_CONFIG_REASON_CONDITION_MALFORMED_CONTENTS_CASE, predicate.id())); } -TEST(MetricsManagerTest, TestCreateConditionTrackerSimple) { +TEST_F(MetricsManagerUtilTest, TestCreateConditionTrackerSimple) { int index = 1; int64_t id = 987; const ConfigKey key(123, 456); @@ -769,7 +1273,10 @@ TEST(MetricsManagerTest, TestCreateConditionTrackerSimple) { atomTrackerMap[stopMatcherId] = stopMatcherIndex; atomTrackerMap[stopAllMatcherId] = stopAllMatcherIndex; - sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap); + optional<InvalidConfigReason> invalidConfigReason; + sp<ConditionTracker> tracker = + createConditionTracker(key, predicate, index, atomTrackerMap, invalidConfigReason); + EXPECT_EQ(invalidConfigReason, nullopt); EXPECT_EQ(tracker->getConditionId(), id); EXPECT_EQ(tracker->isSliced(), false); EXPECT_TRUE(tracker->IsSimpleCondition()); @@ -780,7 +1287,7 @@ TEST(MetricsManagerTest, TestCreateConditionTrackerSimple) { ASSERT_EQ(interestedMatchers.count(stopAllMatcherIndex), 1); } -TEST(MetricsManagerTest, TestCreateConditionTrackerCombination) { +TEST_F(MetricsManagerUtilTest, TestCreateConditionTrackerCombination) { int index = 1; int64_t id = 987; const ConfigKey key(123, 456); @@ -794,12 +1301,15 @@ TEST(MetricsManagerTest, TestCreateConditionTrackerCombination) { // Combination conditions must be initialized to set most state. unordered_map<int64_t, int> atomTrackerMap; - sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap); + optional<InvalidConfigReason> invalidConfigReason; + sp<ConditionTracker> tracker = + createConditionTracker(key, predicate, index, atomTrackerMap, invalidConfigReason); + EXPECT_EQ(invalidConfigReason, nullopt); EXPECT_EQ(tracker->getConditionId(), id); EXPECT_FALSE(tracker->IsSimpleCondition()); } -TEST(MetricsManagerTest, TestCreateAnomalyTrackerInvalidMetric) { +TEST_F(MetricsManagerUtilTest, TestCreateAnomalyTrackerInvalidMetric) { Alert alert; alert.set_id(123); alert.set_metric_id(1); @@ -808,13 +1318,17 @@ TEST(MetricsManagerTest, TestCreateAnomalyTrackerInvalidMetric) { sp<AlarmMonitor> anomalyAlarmMonitor; vector<sp<MetricProducer>> metricProducers; + optional<InvalidConfigReason> invalidConfigReason; // Pass in empty metric producers, causing an error. EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, UPDATE_NEW, /*updateTime=*/123, {}, - metricProducers), + metricProducers, invalidConfigReason), nullopt); + EXPECT_EQ(invalidConfigReason, + createInvalidConfigReasonWithAlert(INVALID_CONFIG_REASON_ALERT_METRIC_NOT_FOUND, + alert.metric_id(), alert.id())); } -TEST(MetricsManagerTest, TestCreateAnomalyTrackerNoThreshold) { +TEST_F(MetricsManagerUtilTest, TestCreateAnomalyTrackerNoThreshold) { int64_t metricId = 1; Alert alert; alert.set_id(123); @@ -828,12 +1342,16 @@ TEST(MetricsManagerTest, TestCreateAnomalyTrackerNoThreshold) { vector<sp<MetricProducer>> metricProducers({new CountMetricProducer( kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)}); sp<AlarmMonitor> anomalyAlarmMonitor; + optional<InvalidConfigReason> invalidConfigReason; EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, UPDATE_NEW, /*updateTime=*/123, - {{1, 0}}, metricProducers), + {{1, 0}}, metricProducers, invalidConfigReason), nullopt); + EXPECT_EQ(invalidConfigReason, + createInvalidConfigReasonWithAlert(INVALID_CONFIG_REASON_ALERT_THRESHOLD_MISSING, + alert.id())); } -TEST(MetricsManagerTest, TestCreateAnomalyTrackerMissingBuckets) { +TEST_F(MetricsManagerUtilTest, TestCreateAnomalyTrackerMissingBuckets) { int64_t metricId = 1; Alert alert; alert.set_id(123); @@ -847,12 +1365,16 @@ TEST(MetricsManagerTest, TestCreateAnomalyTrackerMissingBuckets) { vector<sp<MetricProducer>> metricProducers({new CountMetricProducer( kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)}); sp<AlarmMonitor> anomalyAlarmMonitor; + optional<InvalidConfigReason> invalidConfigReason; EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, UPDATE_NEW, /*updateTime=*/123, - {{1, 0}}, metricProducers), + {{1, 0}}, metricProducers, invalidConfigReason), nullopt); + EXPECT_EQ(invalidConfigReason, + createInvalidConfigReasonWithAlert( + INVALID_CONFIG_REASON_ALERT_INVALID_TRIGGER_OR_NUM_BUCKETS, alert.id())); } -TEST(MetricsManagerTest, TestCreateAnomalyTrackerGood) { +TEST_F(MetricsManagerUtilTest, TestCreateAnomalyTrackerGood) { int64_t metricId = 1; Alert alert; alert.set_id(123); @@ -867,12 +1389,14 @@ TEST(MetricsManagerTest, TestCreateAnomalyTrackerGood) { vector<sp<MetricProducer>> metricProducers({new CountMetricProducer( kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)}); sp<AlarmMonitor> anomalyAlarmMonitor; + optional<InvalidConfigReason> invalidConfigReason; EXPECT_NE(createAnomalyTracker(alert, anomalyAlarmMonitor, UPDATE_NEW, /*updateTime=*/123, - {{1, 0}}, metricProducers), + {{1, 0}}, metricProducers, invalidConfigReason), nullopt); + EXPECT_EQ(invalidConfigReason, nullopt); } -TEST(MetricsManagerTest, TestCreateAnomalyTrackerDurationTooLong) { +TEST_F(MetricsManagerUtilTest, TestCreateAnomalyTrackerDurationTooLong) { int64_t metricId = 1; Alert alert; alert.set_id(123); @@ -892,12 +1416,16 @@ TEST(MetricsManagerTest, TestCreateAnomalyTrackerDurationTooLong) { 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, 0x0123456789, dimensions, 0, 0)}); sp<AlarmMonitor> anomalyAlarmMonitor; + optional<InvalidConfigReason> invalidConfigReason; EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, UPDATE_NEW, /*updateTime=*/123, - {{1, 0}}, metricProducers), + {{1, 0}}, metricProducers, invalidConfigReason), nullopt); + EXPECT_EQ(invalidConfigReason, + createInvalidConfigReasonWithAlert(INVALID_CONFIG_REASON_ALERT_CANNOT_ADD_ANOMALY, + alert.metric_id(), alert.id())); } -TEST(MetricsManagerTest, TestCreateDurationProducerDimensionsInWhatInvalid) { +TEST_F(MetricsManagerUtilTest, TestCreateDurationProducerDimensionsInWhatInvalid) { StatsdConfig config; config.add_allowed_log_source("AID_ROOT"); *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); @@ -934,6 +1462,326 @@ TEST(MetricsManagerTest, TestCreateDurationProducerDimensionsInWhatInvalid) { EXPECT_FALSE(metricsManager->isConfigValid()); } +TEST_F(MetricsManagerUtilTest, TestSampledMetrics) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + + AtomMatcher appCrashMatcher = + CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED); + *config.add_atom_matcher() = appCrashMatcher; + + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + + AtomMatcher bleScanResultReceivedMatcher = CreateSimpleAtomMatcher( + "BleScanResultReceivedAtomMatcher", util::BLE_SCAN_RESULT_RECEIVED); + *config.add_atom_matcher() = bleScanResultReceivedMatcher; + + Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *config.add_predicate() = holdingWakelockPredicate; + + CountMetric sampledCountMetric = + createCountMetric("CountSampledAppCrashesPerUid", appCrashMatcher.id(), nullopt, {}); + *sampledCountMetric.mutable_dimensions_in_what() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); + *sampledCountMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); + sampledCountMetric.mutable_dimensional_sampling_info()->set_shard_count(2); + *config.add_count_metric() = sampledCountMetric; + + CountMetric unsampledCountMetric = + createCountMetric("CountAppCrashesPerUid", appCrashMatcher.id(), nullopt, {}); + *unsampledCountMetric.mutable_dimensions_in_what() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); + *config.add_count_metric() = unsampledCountMetric; + + DurationMetric sampledDurationMetric = createDurationMetric( + "DurationSampledWakelockPerUid", holdingWakelockPredicate.id(), nullopt, {}); + *sampledDurationMetric.mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *sampledDurationMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + sampledDurationMetric.mutable_dimensional_sampling_info()->set_shard_count(4); + *config.add_duration_metric() = sampledDurationMetric; + + DurationMetric unsampledDurationMetric = createDurationMetric( + "DurationWakelockPerUid", holdingWakelockPredicate.id(), nullopt, {}); + unsampledDurationMetric.set_aggregation_type(DurationMetric::SUM); + *unsampledDurationMetric.mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *config.add_duration_metric() = unsampledDurationMetric; + + ValueMetric sampledValueMetric = + createValueMetric("ValueSampledBleScanResultsPerUid", bleScanResultReceivedMatcher, + /*num_results=*/2, nullopt, {}); + *sampledValueMetric.mutable_dimensions_in_what() = + CreateDimensions(util::BLE_SCAN_RESULT_RECEIVED, {1 /* uid */}); + *sampledValueMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateDimensions(util::BLE_SCAN_RESULT_RECEIVED, {1 /*uid*/}); + sampledValueMetric.mutable_dimensional_sampling_info()->set_shard_count(6); + *config.add_value_metric() = sampledValueMetric; + + ValueMetric unsampledValueMetric = + createValueMetric("ValueBleScanResultsPerUid", bleScanResultReceivedMatcher, + /*num_results=*/2, nullopt, {}); + *unsampledValueMetric.mutable_dimensions_in_what() = + CreateDimensions(util::BLE_SCAN_RESULT_RECEIVED, {1 /* uid */}); + *config.add_value_metric() = unsampledValueMetric; + + KllMetric sampledKllMetric = + createKllMetric("KllSampledBleScanResultsPerUid", bleScanResultReceivedMatcher, + /*num_results=*/2, nullopt); + *sampledKllMetric.mutable_dimensions_in_what() = + CreateDimensions(util::BLE_SCAN_RESULT_RECEIVED, {1 /* uid */}); + *sampledKllMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateDimensions(util::BLE_SCAN_RESULT_RECEIVED, {1 /*uid*/}); + sampledKllMetric.mutable_dimensional_sampling_info()->set_shard_count(8); + *config.add_kll_metric() = sampledKllMetric; + + KllMetric unsampledKllMetric = createKllMetric( + "KllBleScanResultsPerUid", bleScanResultReceivedMatcher, /*num_results=*/2, nullopt); + *unsampledKllMetric.mutable_dimensions_in_what() = + CreateDimensions(util::BLE_SCAN_RESULT_RECEIVED, {1 /* uid */}); + *config.add_kll_metric() = unsampledKllMetric; + + GaugeMetric sampledGaugeMetric = + createGaugeMetric("GaugeSampledAppCrashesPerUid", appCrashMatcher.id(), + GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt); + *sampledGaugeMetric.mutable_dimensions_in_what() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /* uid */}); + *sampledGaugeMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); + sampledGaugeMetric.mutable_dimensional_sampling_info()->set_shard_count(10); + *config.add_gauge_metric() = sampledGaugeMetric; + + GaugeMetric unsampledGaugeMetric = + createGaugeMetric("GaugeAppCrashesPerUid", appCrashMatcher.id(), + GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt); + *unsampledGaugeMetric.mutable_dimensions_in_what() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /* uid */}); + *config.add_gauge_metric() = unsampledGaugeMetric; + + ConfigKey key(123, 987); + uint64_t timeNs = 456; + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> periodicAlarmMonitor; + sp<UidMap> uidMap; + sp<MetricsManager> metricsManager = + new MetricsManager(key, config, timeNs, timeNs, uidMap, pullerManager, + anomalyAlarmMonitor, periodicAlarmMonitor); + ASSERT_TRUE(metricsManager->isConfigValid()); + ASSERT_EQ(10, metricsManager->mAllMetricProducers.size()); + + sp<MetricProducer> sampledCountMetricProducer = metricsManager->mAllMetricProducers[0]; + sp<MetricProducer> unsampledCountMetricProducer = metricsManager->mAllMetricProducers[1]; + sp<MetricProducer> sampledDurationMetricProducer = metricsManager->mAllMetricProducers[2]; + sp<MetricProducer> unsampledDurationMetricProducer = metricsManager->mAllMetricProducers[3]; + sp<MetricProducer> sampledValueMetricProducer = metricsManager->mAllMetricProducers[4]; + sp<MetricProducer> unsampledValueMetricProducer = metricsManager->mAllMetricProducers[5]; + sp<MetricProducer> sampledKllMetricProducer = metricsManager->mAllMetricProducers[6]; + sp<MetricProducer> unsampledKllMetricProducer = metricsManager->mAllMetricProducers[7]; + sp<MetricProducer> sampledGaugeMetricProducer = metricsManager->mAllMetricProducers[8]; + sp<MetricProducer> unsampledGaugeMetricProducer = metricsManager->mAllMetricProducers[9]; + + // Check shard count is set correctly for sampled metrics or set to default. + EXPECT_EQ(2, sampledCountMetricProducer->mShardCount); + EXPECT_EQ(0, unsampledCountMetricProducer->mShardCount); + EXPECT_EQ(4, sampledDurationMetricProducer->mShardCount); + EXPECT_EQ(0, unsampledDurationMetricProducer->mShardCount); + EXPECT_EQ(6, sampledValueMetricProducer->mShardCount); + EXPECT_EQ(0, unsampledValueMetricProducer->mShardCount); + EXPECT_EQ(8, sampledKllMetricProducer->mShardCount); + EXPECT_EQ(0, unsampledKllMetricProducer->mShardCount); + EXPECT_EQ(10, sampledGaugeMetricProducer->mShardCount); + EXPECT_EQ(0, unsampledGaugeMetricProducer->mShardCount); + + // Check sampled what fields is set correctly or empty. + EXPECT_EQ(1, sampledCountMetricProducer->mSampledWhatFields.size()); + EXPECT_EQ(true, unsampledCountMetricProducer->mSampledWhatFields.empty()); + EXPECT_EQ(1, sampledDurationMetricProducer->mSampledWhatFields.size()); + EXPECT_EQ(true, unsampledDurationMetricProducer->mSampledWhatFields.empty()); + EXPECT_EQ(1, sampledValueMetricProducer->mSampledWhatFields.size()); + EXPECT_EQ(true, unsampledValueMetricProducer->mSampledWhatFields.empty()); + EXPECT_EQ(1, sampledKllMetricProducer->mSampledWhatFields.size()); + EXPECT_EQ(true, unsampledKllMetricProducer->mSampledWhatFields.empty()); + EXPECT_EQ(1, sampledGaugeMetricProducer->mSampledWhatFields.size()); + EXPECT_EQ(true, unsampledGaugeMetricProducer->mSampledWhatFields.empty()); +} + +TEST_F(MetricsManagerUtilTest, TestMetricHasShardCountButNoSampledField) { + AtomMatcher appCrashMatcher = + CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED); + + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = appCrashMatcher; + + CountMetric metric = + createCountMetric("CountSampledAppCrashesPerUid", appCrashMatcher.id(), nullopt, {}); + *metric.mutable_dimensions_in_what() = CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); + metric.mutable_dimensional_sampling_info()->set_shard_count(2); + *config.add_count_metric() = metric; + + EXPECT_EQ(initConfig(config), + InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_DIMENSIONAL_SAMPLING_INFO_MISSING_SAMPLED_FIELD, + metric.id())); +} + +TEST_F(MetricsManagerUtilTest, TestMetricHasSampledFieldIncorrectShardCount) { + AtomMatcher appCrashMatcher = + CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED); + + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = appCrashMatcher; + + CountMetric metric = + createCountMetric("CountSampledAppCrashesPerUid", appCrashMatcher.id(), nullopt, {}); + *metric.mutable_dimensions_in_what() = CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); + *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); + *config.add_count_metric() = metric; + + EXPECT_EQ(initConfig(config), + InvalidConfigReason( + INVALID_CONFIG_REASON_METRIC_DIMENSIONAL_SAMPLING_INFO_INCORRECT_SHARD_COUNT, + metric.id())); +} + +TEST_F(MetricsManagerUtilTest, TestMetricHasMultipleSampledFields) { + AtomMatcher appCrashMatcher = + CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED); + + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = appCrashMatcher; + + CountMetric metric = + createCountMetric("CountSampledAppCrashesPerUid", appCrashMatcher.id(), nullopt, {}); + *metric.mutable_dimensions_in_what() = CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); + *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/, 2 /*event_type*/}); + metric.mutable_dimensional_sampling_info()->set_shard_count(2); + *config.add_count_metric() = metric; + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_SAMPLED_FIELD_INCORRECT_SIZE, + metric.id())); +} + +TEST_F(MetricsManagerUtilTest, TestMetricHasRepeatedSampledField_PositionALL) { + AtomMatcher testAtomReportedMatcher = + CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED); + + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = testAtomReportedMatcher; + + CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField", + testAtomReportedMatcher.id(), nullopt, {}); + *metric.mutable_dimensions_in_what() = CreateRepeatedDimensions( + util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::ALL}); + *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateRepeatedDimensions(util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, + {Position::ALL}); + metric.mutable_dimensional_sampling_info()->set_shard_count(2); + *config.add_count_metric() = metric; + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_SAMPLED_FIELD_INCORRECT_SIZE, + metric.id())); +} + +TEST_F(MetricsManagerUtilTest, TestMetricHasRepeatedSampledField_PositionFIRST) { + AtomMatcher testAtomReportedMatcher = + CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED); + + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = testAtomReportedMatcher; + + CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField", + testAtomReportedMatcher.id(), nullopt, {}); + *metric.mutable_dimensions_in_what() = CreateRepeatedDimensions( + util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::FIRST}); + *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateRepeatedDimensions(util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, + {Position::FIRST}); + metric.mutable_dimensional_sampling_info()->set_shard_count(2); + *config.add_count_metric() = metric; + + EXPECT_EQ(initConfig(config), nullopt); +} + +TEST_F(MetricsManagerUtilTest, TestMetricHasRepeatedSampledField_PositionLAST) { + AtomMatcher testAtomReportedMatcher = + CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED); + + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = testAtomReportedMatcher; + + CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField", + testAtomReportedMatcher.id(), nullopt, {}); + *metric.mutable_dimensions_in_what() = CreateRepeatedDimensions( + util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::LAST}); + *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateRepeatedDimensions(util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, + {Position::LAST}); + metric.mutable_dimensional_sampling_info()->set_shard_count(2); + *config.add_count_metric() = metric; + + EXPECT_EQ(initConfig(config), nullopt); +} + +TEST_F(MetricsManagerUtilTest, TestMetricHasRepeatedSampledField_PositionANY) { + AtomMatcher testAtomReportedMatcher = + CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED); + + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = testAtomReportedMatcher; + + CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField", + testAtomReportedMatcher.id(), nullopt, {}); + *metric.mutable_dimensions_in_what() = CreateRepeatedDimensions( + util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::ANY}); + *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateRepeatedDimensions(util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, + {Position::ALL}); + metric.mutable_dimensional_sampling_info()->set_shard_count(2); + *config.add_count_metric() = metric; + + EXPECT_EQ(initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_SAMPLED_FIELD_INCORRECT_SIZE, + metric.id())); +} + +TEST_F(MetricsManagerUtilTest, TestMetricSampledFieldNotSubsetDimension) { + AtomMatcher appCrashMatcher = + CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED); + + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = appCrashMatcher; + + CountMetric metric = + createCountMetric("CountSampledAppCrashesPerUid", appCrashMatcher.id(), nullopt, {}); + *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() = + CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); + metric.mutable_dimensional_sampling_info()->set_shard_count(2); + *config.add_count_metric() = metric; + + EXPECT_EQ( + initConfig(config), + InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_SAMPLED_FIELDS_NOT_SUBSET_DIM_IN_WHAT, + metric.id())); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/statsd/tests/statsd_test_util.cpp b/statsd/tests/statsd_test_util.cpp index 33b6b568..832c0416 100644 --- a/statsd/tests/statsd_test_util.cpp +++ b/statsd/tests/statsd_test_util.cpp @@ -34,6 +34,25 @@ namespace android { namespace os { namespace statsd { +void StatsServiceConfigTest::sendConfig(const StatsdConfig& config) { + string str; + config.SerializeToString(&str); + std::vector<uint8_t> configAsVec(str.begin(), str.end()); + service->addConfiguration(kConfigKey, configAsVec, kCallingUid); +} + +ConfigMetricsReport StatsServiceConfigTest::getReports(sp<StatsLogProcessor> processor, + int64_t timestamp, bool include_current) { + vector<uint8_t> output; + ConfigKey configKey(kCallingUid, kConfigKey); + processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/, + true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &output); + ConfigMetricsReportList reports; + reports.ParseFromArray(output.data(), output.size()); + EXPECT_EQ(1, reports.reports_size()); + return reports.reports(kCallingUid); +} + StatsLogReport outputStreamToProto(ProtoOutputStream* proto) { vector<uint8_t> bytes; bytes.resize(proto->size()); @@ -86,6 +105,11 @@ AtomMatcher CreateFinishScheduledJobAtomMatcher() { ScheduledJobStateChanged::FINISHED); } +AtomMatcher CreateScheduleScheduledJobAtomMatcher() { + return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobSchedule", + ScheduledJobStateChanged::SCHEDULED); +} + AtomMatcher CreateScreenBrightnessChangedAtomMatcher() { AtomMatcher atom_matcher; atom_matcher.set_id(StringToId("ScreenBrightnessChanged")); @@ -255,10 +279,8 @@ AtomMatcher CreateAppStartOccurredAtomMatcher() { AtomMatcher CreateTestAtomRepeatedStateAtomMatcher(const string& name, TestAtomReported::State state, Position position) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); + AtomMatcher atom_matcher = CreateSimpleAtomMatcher(name, util::TEST_ATOM_REPORTED); auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::TEST_ATOM_REPORTED); auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); field_value_matcher->set_field(14); // Repeated enum field. field_value_matcher->set_eq_int(state); @@ -319,7 +341,7 @@ Predicate CreateScreenIsOnPredicate() { Predicate CreateScreenIsOffPredicate() { Predicate predicate; - predicate.set_id(1111123); + predicate.set_id(StringToId("ScreenIsOff")); predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff")); predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn")); return predicate; @@ -335,7 +357,7 @@ Predicate CreateHoldingWakelockPredicate() { Predicate CreateIsSyncingPredicate() { Predicate predicate; - predicate.set_id(33333333333333); + predicate.set_id(StringToId("IsSyncing")); predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart")); predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd")); return predicate; @@ -592,14 +614,14 @@ ValueMetric createValueMetric(const string& name, const AtomMatcher& what, const return metric; } -KllMetric createKllMetric(const string& name, const AtomMatcher& what, const int valueField, +KllMetric createKllMetric(const string& name, const AtomMatcher& what, const int kllField, const optional<int64_t>& condition) { KllMetric metric; metric.set_id(StringToId(name)); metric.set_what(what.id()); metric.set_bucket(TEN_MINUTES); metric.mutable_kll_field()->set_field(what.simple_atom_matcher().atom_id()); - metric.mutable_kll_field()->add_child()->set_field(valueField); + metric.mutable_kll_field()->add_child()->set_field(kllField); if (condition) { metric.set_condition(condition.value()); } @@ -1020,6 +1042,15 @@ std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs, ScheduledJobStateChanged::FINISHED, timestampNs); } +// Create log event when scheduled job is scheduled. +std::unique_ptr<LogEvent> CreateScheduleScheduledJobEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& jobName) { + return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName, + ScheduledJobStateChanged::SCHEDULED, timestampNs); +} + std::unique_ptr<LogEvent> CreateTestAtomReportedEventVariableRepeatedFields( uint64_t timestampNs, const vector<int>& repeatedIntField, const vector<int64_t>& repeatedLongField, const vector<float>& repeatedFloatField, @@ -1313,6 +1344,22 @@ std::unique_ptr<LogEvent> CreateAppStartOccurredEvent( return logEvent; } +std::unique_ptr<LogEvent> CreateBleScanResultReceivedEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const int numResults) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, util::BLE_SCAN_RESULT_RECEIVED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + writeAttribution(statsEvent, attributionUids, attributionTags); + AStatsEvent_writeInt32(statsEvent, numResults); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); + return logEvent; +} + sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs, const StatsdConfig& config, const ConfigKey& key, const shared_ptr<IPullAtomCallback>& puller, @@ -1692,6 +1739,8 @@ void backfillStringInReport(ConfigMetricsReport *config_report) { backfillStringInDimension(str_map, metric_report->mutable_gauge_metrics()); } else if (metric_report->has_value_metrics()) { backfillStringInDimension(str_map, metric_report->mutable_value_metrics()); + } else if (metric_report->has_kll_metrics()) { + backfillStringInDimension(str_map, metric_report->mutable_kll_metrics()); } } // Backfill the package names. @@ -1764,20 +1813,20 @@ bool backfillDimensionPath(const DimensionsValue& path, } void backfillDimensionPath(StatsLogReport* report) { - if (report->has_dimensions_path_in_what() || report->has_dimensions_path_in_condition()) { + if (report->has_dimensions_path_in_what()) { auto whatPath = report->dimensions_path_in_what(); - auto conditionPath = report->dimensions_path_in_condition(); if (report->has_count_metrics()) { - backfillDimensionPath(whatPath, conditionPath, report->mutable_count_metrics()); + backfillDimensionPath(whatPath, report->mutable_count_metrics()); } else if (report->has_duration_metrics()) { - backfillDimensionPath(whatPath, conditionPath, report->mutable_duration_metrics()); + backfillDimensionPath(whatPath, report->mutable_duration_metrics()); } else if (report->has_gauge_metrics()) { - backfillDimensionPath(whatPath, conditionPath, report->mutable_gauge_metrics()); + backfillDimensionPath(whatPath, report->mutable_gauge_metrics()); } else if (report->has_value_metrics()) { - backfillDimensionPath(whatPath, conditionPath, report->mutable_value_metrics()); + backfillDimensionPath(whatPath, report->mutable_value_metrics()); + } else if (report->has_kll_metrics()) { + backfillDimensionPath(whatPath, report->mutable_kll_metrics()); } report->clear_dimensions_path_in_what(); - report->clear_dimensions_path_in_condition(); } } @@ -2038,7 +2087,7 @@ vector<PackageInfo> buildPackageInfos( return packageInfos; } -StatsdStatsReport_PulledAtomStats getPulledAtomStats() { +StatsdStatsReport_PulledAtomStats getPulledAtomStats(int32_t atom_id) { vector<uint8_t> statsBuffer; StatsdStats::getInstance().dumpStats(&statsBuffer, false /*reset stats*/); StatsdStatsReport statsReport; @@ -2049,7 +2098,12 @@ StatsdStatsReport_PulledAtomStats getPulledAtomStats() { if (statsReport.pulled_atom_stats_size() == 0) { return pulledAtomStats; } - return statsReport.pulled_atom_stats(0); + for (size_t i = 0; i < statsReport.pulled_atom_stats_size(); i++) { + if (statsReport.pulled_atom_stats(i).atom_id() == atom_id) { + return statsReport.pulled_atom_stats(i); + } + } + return pulledAtomStats; } } // namespace statsd diff --git a/statsd/tests/statsd_test_util.h b/statsd/tests/statsd_test_util.h index c4fa1fe4..e262c0c9 100644 --- a/statsd/tests/statsd_test_util.h +++ b/statsd/tests/statsd_test_util.h @@ -23,6 +23,7 @@ #include <gtest/gtest.h> #include "src/StatsLogProcessor.h" +#include "src/StatsService.h" #include "src/flags/FlagProvider.h" #include "src/hash.h" #include "src/logd/LogEvent.h" @@ -48,6 +49,7 @@ using google::protobuf::util::MessageDifferencer; using Status = ::ndk::ScopedAStatus; using PackageInfoSnapshot = UidMapping_PackageInfoSnapshot; using PackageInfo = UidMapping_PackageInfoSnapshot_PackageInfo; +using ::ndk::SharedRefBase; // Wrapper for assertion helpers called from tests to keep track of source location of failures. // Example usage: @@ -89,6 +91,36 @@ public: const StatsDimensionsValueParcel& dimensionsValueParcel)); }; +class StatsServiceConfigTest : public ::testing::Test { +protected: + shared_ptr<StatsService> service; + const int kConfigKey = 789130123; // Randomly chosen + const int kCallingUid = 0; // Randomly chosen + void SetUp() override { + service = SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr); + // Removing config file from data/misc/stats-service and data/misc/stats-data if present + ConfigKey configKey(kCallingUid, kConfigKey); + service->removeConfiguration(kConfigKey, kCallingUid); + service->mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), + false /* include_current_bucket*/, true /* erase_data */, + ADB_DUMP, NO_TIME_CONSTRAINTS, nullptr); + } + + void TearDown() override { + // Cleaning up data/misc/stats-service and data/misc/stats-data + ConfigKey configKey(kCallingUid, kConfigKey); + service->removeConfiguration(kConfigKey, kCallingUid); + service->mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), + false /* include_current_bucket*/, true /* erase_data */, + ADB_DUMP, NO_TIME_CONSTRAINTS, nullptr); + } + + void sendConfig(const StatsdConfig& config); + + ConfigMetricsReport getReports(sp<StatsLogProcessor> processor, int64_t timestamp, + bool include_current = false); +}; + // Converts a ProtoOutputStream to a StatsLogReport proto. StatsLogReport outputStreamToProto(ProtoOutputStream* proto); @@ -107,6 +139,9 @@ AtomMatcher CreateStartScheduledJobAtomMatcher(); // Create AtomMatcher proto for a scheduled job is done. AtomMatcher CreateFinishScheduledJobAtomMatcher(); +// Create AtomMatcher proto for cancelling a scheduled job. +AtomMatcher CreateScheduleScheduledJobAtomMatcher(); + // Create AtomMatcher proto for screen brightness state changed. AtomMatcher CreateScreenBrightnessChangedAtomMatcher(); @@ -272,7 +307,7 @@ GaugeMetric createGaugeMetric(const string& name, const int64_t what, ValueMetric createValueMetric(const string& name, const AtomMatcher& what, const int valueField, const optional<int64_t>& condition, const vector<int64_t>& states); -KllMetric createKllMetric(const string& name, const AtomMatcher& what, const int valueField, +KllMetric createKllMetric(const string& name, const AtomMatcher& what, const int kllField, const optional<int64_t>& condition); Alert createAlert(const string& name, const int64_t metricId, const int buckets, @@ -378,6 +413,12 @@ std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs, const vector<string>& attributionTags, const string& jobName); +// Create log event when scheduled job schedules. +std::unique_ptr<LogEvent> CreateScheduleScheduledJobEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& jobName); + // Create log event when battery saver starts. std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs); // Create log event when battery saver stops. @@ -441,6 +482,11 @@ std::unique_ptr<LogEvent> CreateAppStartOccurredEvent( AppStartOccurred::TransitionType type, const string& activity_name, const string& calling_pkg_name, const bool is_instant_app, int64_t activity_start_msec); +std::unique_ptr<LogEvent> CreateBleScanResultReceivedEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const int numResults); + std::unique_ptr<LogEvent> CreateTestAtomReportedEventVariableRepeatedFields( uint64_t timestampNs, const vector<int>& repeatedIntField, const vector<int64_t>& repeatedLongField, const vector<float>& repeatedFloatField, @@ -535,9 +581,6 @@ void backfillStringInDimension(const std::map<uint64_t, string>& str_map, if (data->has_dimensions_in_what()) { backfillStringInDimension(str_map, data->mutable_dimensions_in_what()); } - if (data->has_dimensions_in_condition()) { - backfillStringInDimension(str_map, data->mutable_dimensions_in_condition()); - } } } @@ -560,9 +603,7 @@ public: }; template <typename T> -void backfillDimensionPath(const DimensionsValue& whatPath, - const DimensionsValue& conditionPath, - T* metricData) { +void backfillDimensionPath(const DimensionsValue& whatPath, T* metricData) { for (int i = 0; i < metricData->data_size(); ++i) { auto data = metricData->mutable_data(i); if (data->dimension_leaf_values_in_what_size() > 0) { @@ -570,11 +611,6 @@ void backfillDimensionPath(const DimensionsValue& whatPath, data->mutable_dimensions_in_what()); data->clear_dimension_leaf_values_in_what(); } - if (data->dimension_leaf_values_in_condition_size() > 0) { - backfillDimensionPath(conditionPath, data->dimension_leaf_values_in_condition(), - data->mutable_dimensions_in_condition()); - data->clear_dimension_leaf_values_in_condition(); - } } } @@ -722,7 +758,7 @@ std::vector<T> concatenate(const vector<T>& a, const vector<T>& b) { return result; } -StatsdStatsReport_PulledAtomStats getPulledAtomStats(); +StatsdStatsReport_PulledAtomStats getPulledAtomStats(int atom_id); } // namespace statsd } // namespace os } // namespace android diff --git a/statsd/tools/localtools/Android.bp b/statsd/tools/localtools/Android.bp index f89e635f..c07cb4ee 100644 --- a/statsd/tools/localtools/Android.bp +++ b/statsd/tools/localtools/Android.bp @@ -50,6 +50,9 @@ java_test_host { "guava", ], data: [ - "test/external-atoms.proto", + "test/data/**/*.*", + ], + data_native_bins: [ + "aprotoc", ], } diff --git a/statsd/tools/localtools/TEST_MAPPING b/statsd/tools/localtools/TEST_MAPPING index 6862ad43..866c77e6 100644 --- a/statsd/tools/localtools/TEST_MAPPING +++ b/statsd/tools/localtools/TEST_MAPPING @@ -7,8 +7,16 @@ "exclude-annotation": "org.junit.Ignore" }, { - // Exclude test methods that require aprotoc host dependencies - "exclude-filter": "com.android.statsd.shelltools.testdrive.ConfigurationTest#testOnePushedFromExternalAtomsProto" + // Exclude test methods that require aprotoc host dependencies - see http://b/118691442 + "exclude-filter": "com.android.statsd.shelltools.testdrive.ConfigurationTest.testOnePushedFromExternalAtomsProto" + }, + { + // Exclude test methods that require aprotoc host dependencies - see http://b/118691442 + "exclude-filter": "com.android.statsd.shelltools.testdrive.ConfigurationTest.testOnePushedFromExternalAtomsProtoWithImportedAtom" + }, + { + // Exclude test methods that require aprotoc host dependencies - see http://b/118691442 + "exclude-filter": "com.android.statsd.shelltools.testdrive.ConfigurationTest.testOnePushedFromExternalAtomsProtoWithImportedEnum" } ] } diff --git a/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java index cc3b0ddf..a2236743 100644 --- a/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java +++ b/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java @@ -81,6 +81,8 @@ public class TestDrive { "com.google.android.cellbroadcastreceiver", "com.google.android.apps.nexuslauncher", "com.google.android.markup", + "com.android.art", + "com.google.android.art", "AID_KEYSTORE", "AID_VIRTUALIZATIONSERVICE", "com.google.android.permissioncontroller", @@ -143,6 +145,8 @@ public class TestDrive { LOGGER.severe("\tWait for Enter key press before collecting report"); LOGGER.severe("-d delay_ms"); LOGGER.severe("\tWait for delay_ms before collecting report, default is 60000 ms"); + LOGGER.severe("-v"); + LOGGER.severe("\tDebug logging level"); } boolean processArgs( @@ -174,6 +178,8 @@ public class TestDrive { mDeviceSerial = args[++first_arg]; } else if (remaining_args >= 2 && arg.equals("-e")) { mPressToContinue = true; + } else if (remaining_args >= 2 && arg.equals("-v")) { + Utils.setUpLogger(LOGGER, true); } else if (remaining_args >= 2 && arg.equals("-d")) { mPressToContinue = false; mReportCollectionDelayMillis = Integer.parseInt(args[++first_arg]); @@ -189,11 +195,6 @@ public class TestDrive { mProtoIncludes.add(HW_ATOMS_PROTO_FILEPATH); } - mDeviceSerial = Utils.chooseDevice(mDeviceSerial, connectedDevices, defaultDevice, LOGGER); - if (mDeviceSerial == null) { - return false; - } - for (; first_arg < args.length; ++first_arg) { String atom = args[first_arg]; try { @@ -203,6 +204,11 @@ public class TestDrive { } } + mDeviceSerial = Utils.chooseDevice(mDeviceSerial, connectedDevices, defaultDevice, LOGGER); + if (mDeviceSerial == null) { + return false; + } + return configuration.hasPulledAtoms() || configuration.hasPushedAtoms(); } @@ -315,8 +321,10 @@ public class TestDrive { private String compileProtoFileIntoDescriptorSet(String protoFileName) { final String protoCompilerBinary = "aprotoc"; final String descSetFlag = "--descriptor_set_out"; - + final String includeImportsFlag = "--include_imports"; + final String includeSourceInfoFlag = "--include_source_info"; final String dsFileName = generateDescriptorSetFileName(protoFileName); + if (dsFileName == null) return null; LOGGER.log(Level.FINE, "Target DescriptorSet File " + dsFileName); @@ -326,11 +334,17 @@ public class TestDrive { cmdArgs.add(protoCompilerBinary); cmdArgs.add(descSetFlag); cmdArgs.add(dsFileName); + cmdArgs.add(includeImportsFlag); + cmdArgs.add(includeSourceInfoFlag); // populate the proto_path argument if (mAndroidBuildTop != null) { cmdArgs.add("-I"); cmdArgs.add(mAndroidBuildTop); + + Path protoBufSrcPath = Paths.get(mAndroidBuildTop, "external/protobuf/src"); + cmdArgs.add("-I"); + cmdArgs.add(protoBufSrcPath.toString()); } Path protoPath = Paths.get(protoFileName); @@ -346,10 +360,8 @@ public class TestDrive { commands = cmdArgs.toArray(commands); Utils.runCommand(null, LOGGER, commands); return dsFileName; - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Error while performing proto compilation: " + e); - } catch (InterruptedException e) { - LOGGER.log(Level.SEVERE, "Error while performing proto compilation: " + e); + } catch (InterruptedException | IOException e) { + LOGGER.severe("Error while performing proto compilation: " + e.getMessage()); } return null; } @@ -379,7 +391,7 @@ public class TestDrive { dsPath = dsPath.resolve(protoPath.getFileName().toString() + ".ds.tmp"); return dsPath.toString(); } catch (IOException e) { - LOGGER.log(Level.INFO, "Could not find file " + protoFileName); + LOGGER.severe("Could not find file " + protoFileName); } return null; } @@ -394,17 +406,54 @@ public class TestDrive { try (InputStream input = new FileInputStream(dsFileName)) { DescriptorProtos.FileDescriptorSet fileDescriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(input); - Descriptors.FileDescriptor fieldOptionsDesc = DescriptorProtos.FieldOptions.getDescriptor().getFile(); - Descriptors.Descriptor atomMsgDesc = Descriptors.FileDescriptor.buildFrom( - fileDescriptorSet.getFile(0), - new Descriptors.FileDescriptor[]{fieldOptionsDesc}).findMessageTypeByName( - "Atom"); + LOGGER.fine("Files count is " + fileDescriptorSet.getFileCount()); + + // preparing dependencies list + List<Descriptors.FileDescriptor> dependencies = + new ArrayList<Descriptors.FileDescriptor>(); + for (int fileIndex = 0; fileIndex < fileDescriptorSet.getFileCount(); fileIndex++) { + LOGGER.fine("Processing file " + fileIndex); + try { + Descriptors.FileDescriptor dep = Descriptors.FileDescriptor.buildFrom( + fileDescriptorSet.getFile(fileIndex), + new Descriptors.FileDescriptor[0]); + dependencies.add(dep); + } catch (Descriptors.DescriptorValidationException e) { + LOGGER.fine("Unable to parse atoms proto file: " + fileName + ". Error: " + + e.getDescription()); + } + } - if (atomMsgDesc.findFieldByNumber(atom) != null) return true; - } catch (Descriptors.DescriptorValidationException | IOException e) { + Descriptors.FileDescriptor[] fileDescriptorDeps = + new Descriptors.FileDescriptor[dependencies.size()]; + fileDescriptorDeps = dependencies.toArray(fileDescriptorDeps); + + // looking for a file with an Atom definition + for (int fileIndex = 0; fileIndex < fileDescriptorSet.getFileCount(); fileIndex++) { + LOGGER.fine("Processing file " + fileIndex); + Descriptors.Descriptor atomMsgDesc = null; + try { + atomMsgDesc = Descriptors.FileDescriptor.buildFrom( + fileDescriptorSet.getFile(fileIndex), fileDescriptorDeps, + true) + .findMessageTypeByName("Atom"); + } catch (Descriptors.DescriptorValidationException e) { + LOGGER.severe("Unable to parse atoms proto file: " + fileName + ". Error: " + + e.getDescription()); + } + + if (atomMsgDesc != null) { + LOGGER.fine("Atom message is located"); + } + + if (atomMsgDesc != null && atomMsgDesc.findFieldByNumber(atom) != null) { + return true; + } + } + } catch (IOException e) { LOGGER.log(Level.WARNING, "Unable to parse atoms proto file: " + fileName, e); } finally { File dsFile = new File(dsFileName); diff --git a/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java b/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java index 1bf02cb2..650128d7 100644 --- a/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java +++ b/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java @@ -57,6 +57,8 @@ public class ConfigurationTest { private final TestDrive.Configuration mConfiguration = new TestDrive.Configuration(); + private final String mProtoFilePath = "test/data/atoms.proto"; + @Test public void testOnePushed() { final int atom = 90; @@ -95,7 +97,7 @@ public class ConfigurationTest { assertFalse(TestDrive.Configuration.isPulledAtom(atom)); List<String> mProtoIncludes = new ArrayList<>(); - mProtoIncludes.add("test/external-atoms.proto"); + mProtoIncludes.add(mProtoFilePath); mConfiguration.addAtom(atom, mProtoIncludes); StatsdConfig config = mConfiguration.createConfig(); @@ -124,6 +126,78 @@ public class ConfigurationTest { assertEquals(0, atomMatchers.size()); } + @Ignore("b/258831376") + @Test + public void testOnePushedFromExternalAtomsProtoWithImportedAtom() { + final int atom = 106001; + assertFalse(TestDrive.Configuration.isPulledAtom(atom)); + + List<String> mProtoIncludes = new ArrayList<>(); + mProtoIncludes.add(mProtoFilePath); + + mConfiguration.addAtom(atom, mProtoIncludes); + StatsdConfig config = mConfiguration.createConfig(); + + //event_metric { + // id: 1111 + // what: 1234567 + //} + //atom_matcher { + // id: 1234567 + // simple_atom_matcher { + // atom_id: 106001 + // } + //} + + assertEquals(1, config.getEventMetricCount()); + assertEquals(0, config.getGaugeMetricCount()); + + assertTrue(mConfiguration.isTrackedMetric(config.getEventMetric(0).getId())); + + final List<StatsdConfigProto.AtomMatcher> atomMatchers = + new ArrayList<>(config.getAtomMatcherList()); + assertEquals(atom, + findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat()) + .getSimpleAtomMatcher().getAtomId()); + assertEquals(0, atomMatchers.size()); + } + + @Ignore("b/258831376") + @Test + public void testOnePushedFromExternalAtomsProtoWithImportedEnum() { + final int atom = 100001; + assertFalse(TestDrive.Configuration.isPulledAtom(atom)); + + List<String> mProtoIncludes = new ArrayList<>(); + mProtoIncludes.add(mProtoFilePath); + + mConfiguration.addAtom(atom, mProtoIncludes); + StatsdConfig config = mConfiguration.createConfig(); + + //event_metric { + // id: 1111 + // what: 1234567 + //} + //atom_matcher { + // id: 1234567 + // simple_atom_matcher { + // atom_id: 100001 + // } + //} + + assertEquals(1, config.getEventMetricCount()); + assertEquals(0, config.getGaugeMetricCount()); + + assertTrue(mConfiguration.isTrackedMetric(config.getEventMetric(0).getId())); + + final List<StatsdConfigProto.AtomMatcher> atomMatchers = + new ArrayList<>(config.getAtomMatcherList()); + assertEquals(atom, + findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat()) + .getSimpleAtomMatcher().getAtomId()); + assertEquals(0, atomMatchers.size()); + } + @Test public void testOnePulled() { final int atom = 10022; diff --git a/statsd/tools/localtools/test/external-atoms.proto b/statsd/tools/localtools/test/data/atoms.proto index 5d115e0f..78d04a56 100644 --- a/statsd/tools/localtools/test/external-atoms.proto +++ b/statsd/tools/localtools/test/data/atoms.proto @@ -21,19 +21,26 @@ package android.tests; option java_package = "android.tests"; option java_outer_classname = "ExternalAtoms"; +import "test/data/enum.proto"; +import "test/data/atoms/external_atoms/external_test_atom.proto"; + /* Allocated atom IDs. */ message Atom { oneof pushed { - // AOSP atom ID range starts at 105000 + AtomWithEnum atom_with_enum = 100001; AOSPAtom aosp_atom = 105999; + external_atoms.TestAtom external_atoms_test_atom = 106001; } - // AOSP atom ID range ends at 109999 } /* A test atom from an AOSP atom ids range */ -message AOSPAtom { - +message AtomWithEnum { optional string reverse_domain_name = 1; + optional android.tests.enum.DemoEnumValues enum_value = 2; +} +/* A test atom from an AOSP atom ids range */ +message AOSPAtom { + optional string reverse_domain_name = 1; optional int32 value = 2; } diff --git a/statsd/tools/localtools/test/data/atoms/external_atoms/external_test_atom.proto b/statsd/tools/localtools/test/data/atoms/external_atoms/external_test_atom.proto new file mode 100644 index 00000000..ae44b86b --- /dev/null +++ b/statsd/tools/localtools/test/data/atoms/external_atoms/external_test_atom.proto @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.tests.external_atoms; + +option java_package = "android.tests.external_atoms"; + +message TestAtom { + optional string reverse_domain_name = 1; + optional int32 int_value = 2; +} diff --git a/statsd/tools/localtools/test/data/enum.proto b/statsd/tools/localtools/test/data/enum.proto new file mode 100644 index 00000000..35c36a20 --- /dev/null +++ b/statsd/tools/localtools/test/data/enum.proto @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.tests.enum; + +enum DemoEnumValues { + DEMO_VALUE1 = 1; + DEMO_VALUE2 = 2; + DEMO_VALUE3 = 3; + DEMO_VALUE4 = 4; +} diff --git a/tests/src/android/cts/statsd/metric/CountMetricsTests.java b/tests/src/android/cts/statsd/metric/CountMetricsTests.java index a641e614..22b4f9ca 100644 --- a/tests/src/android/cts/statsd/metric/CountMetricsTests.java +++ b/tests/src/android/cts/statsd/metric/CountMetricsTests.java @@ -385,6 +385,23 @@ public class CountMetricsTests extends DeviceAtomTestCase { .setWhat(whatMatcherId) .addSliceByState(stateId) .addStateLink(stateLink) + .setDimensionsInWhat( + FieldMatcher.newBuilder() + .setField(whatAtomId) + .addChild(FieldMatcher.newBuilder() + .setField(1) + .setPosition(Position.FIRST) + .addChild(FieldMatcher.newBuilder() + .setField(AttributionNode.UID_FIELD_NUMBER) + ) + ) + .addChild(FieldMatcher.newBuilder() + .setField(2) + ) + .addChild(FieldMatcher.newBuilder() + .setField(3) + ) + ) ) .addAtomMatcher(whatMatcher) .addState(state); |