| // Copyright (C) 2021 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 <gtest/gtest.h> |
| |
| #include "src/StatsLogProcessor.h" |
| #include "tests/statsd_test_util.h" |
| |
| namespace android { |
| namespace os { |
| namespace statsd { |
| |
| #ifdef __ANDROID__ |
| |
| 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 { |
| key = ConfigKey(123, 987); |
| bucketStartTimeNs = getElapsedRealtimeNs(); |
| bucketSizeNs = TimeUnitToBucketSizeInMillis(TEN_MINUTES) * 1000000LL; |
| whatMatcher = CreateScreenBrightnessChangedAtomMatcher(); |
| metric = createKllMetric("ScreenBrightness", whatMatcher, /*valueField=*/1, |
| /*condition=*/nullopt); |
| |
| config.add_allowed_log_source("AID_ROOT"); |
| |
| *config.add_atom_matcher() = whatMatcher; |
| *config.add_kll_metric() = metric; |
| |
| events.push_back(CreateScreenBrightnessChangedEvent(bucketStartTimeNs + 5 * NS_PER_SEC, 5)); |
| events.push_back( |
| CreateScreenBrightnessChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC, 15)); |
| events.push_back( |
| CreateScreenBrightnessChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 40)); |
| } |
| |
| ConfigKey key; |
| uint64_t bucketStartTimeNs; |
| uint64_t bucketSizeNs; |
| AtomMatcher whatMatcher; |
| KllMetric metric; |
| StatsdConfig config; |
| vector<unique_ptr<LogEvent>> events; |
| }; |
| |
| TEST_F(KllMetricE2eTest, TestSimpleMetric) { |
| 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(), 1); |
| 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); |
| } |
| |
| 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; |
| config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. |
| |
| AtomMatcher testAtomReportedMatcher = |
| CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED); |
| *config.add_atom_matcher() = testAtomReportedMatcher; |
| |
| // Create kll metric. |
| int64_t metricId = 123456; |
| KllMetric* kllMetric = config.add_kll_metric(); |
| kllMetric->set_id(metricId); |
| kllMetric->set_bucket(TimeUnit::FIVE_MINUTES); |
| kllMetric->set_what(testAtomReportedMatcher.id()); |
| *kllMetric->mutable_kll_field() = CreateRepeatedDimensions( |
| util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::ALL}); |
| |
| // 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); |
| |
| // Config initialization fails. |
| 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 |
| |
| } // namespace statsd |
| } // namespace os |
| } // namespace android |