1 // Copyright (C) 2021 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <gtest/gtest.h>
16
17 #include "src/StatsLogProcessor.h"
18 #include "tests/statsd_test_util.h"
19
20 namespace android {
21 namespace os {
22 namespace statsd {
23
24 #ifdef __ANDROID__
25
26 using namespace std;
27
28 namespace {
29
CreateTestAtomReportedEvent(const uint64_t timestampNs,const long longField,const string & stringField)30 unique_ptr<LogEvent> CreateTestAtomReportedEvent(const uint64_t timestampNs, const long longField,
31 const string& stringField) {
32 return CreateTestAtomReportedEvent(
33 timestampNs, /* attributionUids */ {1001},
34 /* attributionTags */ {"app1"}, /* intField */ 0, longField, /* floatField */ 0.0f,
35 stringField, /* boolField */ false, TestAtomReported::OFF, /* bytesField */ {},
36 /* repeatedIntField */ {}, /* repeatedLongField */ {}, /* repeatedFloatField */ {},
37 /* repeatedStringField */ {}, /* repeatedBoolField */ {},
38 /* repeatedBoolFieldLength */ 0, /* repeatedEnumField */ {});
39 }
40
41 } // anonymous namespace.
42
43 class KllMetricE2eTest : public ::testing::Test {
44 protected:
SetUp()45 void SetUp() override {
46 key = ConfigKey(123, 987);
47 bucketStartTimeNs = getElapsedRealtimeNs();
48 bucketSizeNs = TimeUnitToBucketSizeInMillis(TEN_MINUTES) * 1000000LL;
49 whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
50 metric = createKllMetric("ScreenBrightness", whatMatcher, /*valueField=*/1,
51 /*condition=*/nullopt);
52
53 config.add_allowed_log_source("AID_ROOT");
54
55 *config.add_atom_matcher() = whatMatcher;
56 *config.add_kll_metric() = metric;
57
58 events.push_back(CreateScreenBrightnessChangedEvent(bucketStartTimeNs + 5 * NS_PER_SEC, 5));
59 events.push_back(
60 CreateScreenBrightnessChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC, 15));
61 events.push_back(
62 CreateScreenBrightnessChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 40));
63 }
64
65 ConfigKey key;
66 uint64_t bucketStartTimeNs;
67 uint64_t bucketSizeNs;
68 AtomMatcher whatMatcher;
69 KllMetric metric;
70 StatsdConfig config;
71 vector<unique_ptr<LogEvent>> events;
72 };
73
TEST_F(KllMetricE2eTest,TestSimpleMetric)74 TEST_F(KllMetricE2eTest, TestSimpleMetric) {
75 const sp<StatsLogProcessor> processor =
76 CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key);
77
78 for (auto& event : events) {
79 processor->OnLogEvent(event.get());
80 }
81
82 uint64_t dumpTimeNs = bucketStartTimeNs + bucketSizeNs;
83 ConfigMetricsReportList reports;
84 vector<uint8_t> buffer;
85 processor->onDumpReport(key, dumpTimeNs, /*include_current_bucket*/ false, true, ADB_DUMP, FAST,
86 &buffer);
87 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
88 backfillDimensionPath(&reports);
89 backfillStringInReport(&reports);
90 backfillStartEndTimestamp(&reports);
91 ASSERT_EQ(reports.reports_size(), 1);
92
93 ConfigMetricsReport report = reports.reports(0);
94 ASSERT_EQ(report.metrics_size(), 1);
95 StatsLogReport metricReport = report.metrics(0);
96 EXPECT_EQ(metricReport.metric_id(), metric.id());
97 EXPECT_TRUE(metricReport.has_kll_metrics());
98 ASSERT_EQ(metricReport.kll_metrics().data_size(), 1);
99 KllMetricData data = metricReport.kll_metrics().data(0);
100 ASSERT_EQ(data.bucket_info_size(), 1);
101 KllBucketInfo bucket = data.bucket_info(0);
102 EXPECT_EQ(bucket.start_bucket_elapsed_nanos(), bucketStartTimeNs);
103 EXPECT_EQ(bucket.end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
104 EXPECT_EQ(bucket.sketches_size(), 1);
105 EXPECT_EQ(metricReport.kll_metrics().skipped_size(), 0);
106 }
107
TEST_F(KllMetricE2eTest,TestMetricWithDimensions)108 TEST_F(KllMetricE2eTest, TestMetricWithDimensions) {
109 whatMatcher = CreateSimpleAtomMatcher("TestAtomReported", util::TEST_ATOM_REPORTED);
110 metric = createKllMetric("TestAtomMetric", whatMatcher, /* kllField */ 3,
111 /* condition */ nullopt);
112
113 *metric.mutable_dimensions_in_what() =
114 CreateDimensions(util::TEST_ATOM_REPORTED, {5 /* string_field */});
115
116 config.clear_atom_matcher();
117 *config.add_atom_matcher() = whatMatcher;
118
119 config.clear_kll_metric();
120 *config.add_kll_metric() = metric;
121
122 events.clear();
123 events.push_back(CreateTestAtomReportedEvent(bucketStartTimeNs + 5 * NS_PER_SEC, 5l, "dim_1"));
124 events.push_back(CreateTestAtomReportedEvent(bucketStartTimeNs + 15 * NS_PER_SEC, 6l, "dim_2"));
125 events.push_back(CreateTestAtomReportedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 7l, "dim_1"));
126
127 const sp<StatsLogProcessor> processor =
128 CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key);
129
130 for (auto& event : events) {
131 processor->OnLogEvent(event.get());
132 }
133
134 uint64_t dumpTimeNs = bucketStartTimeNs + bucketSizeNs;
135 ConfigMetricsReportList reports;
136 vector<uint8_t> buffer;
137 processor->onDumpReport(key, dumpTimeNs, /*include_current_bucket*/ false, true, ADB_DUMP, FAST,
138 &buffer);
139 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
140 backfillDimensionPath(&reports);
141 backfillStringInReport(&reports);
142 backfillStartEndTimestamp(&reports);
143 ASSERT_EQ(reports.reports_size(), 1);
144
145 ConfigMetricsReport report = reports.reports(0);
146 ASSERT_EQ(report.metrics_size(), 1);
147 StatsLogReport metricReport = report.metrics(0);
148 EXPECT_EQ(metricReport.metric_id(), metric.id());
149 EXPECT_TRUE(metricReport.has_kll_metrics());
150 ASSERT_EQ(metricReport.kll_metrics().data_size(), 2);
151
152 KllMetricData data = metricReport.kll_metrics().data(0);
153 ASSERT_EQ(data.bucket_info_size(), 1);
154 KllBucketInfo bucket = data.bucket_info(0);
155 EXPECT_EQ(bucket.start_bucket_elapsed_nanos(), bucketStartTimeNs);
156 EXPECT_EQ(bucket.end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
157 EXPECT_EQ(bucket.sketches_size(), 1);
158 EXPECT_EQ(metricReport.kll_metrics().skipped_size(), 0);
159 EXPECT_EQ(data.dimensions_in_what().field(), util::TEST_ATOM_REPORTED);
160 ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
161 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 5);
162 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), "dim_1");
163
164 data = metricReport.kll_metrics().data(1);
165 ASSERT_EQ(data.bucket_info_size(), 1);
166 bucket = data.bucket_info(0);
167 EXPECT_EQ(bucket.start_bucket_elapsed_nanos(), bucketStartTimeNs);
168 EXPECT_EQ(bucket.end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
169 EXPECT_EQ(bucket.sketches_size(), 1);
170 EXPECT_EQ(metricReport.kll_metrics().skipped_size(), 0);
171 EXPECT_EQ(data.dimensions_in_what().field(), util::TEST_ATOM_REPORTED);
172 ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
173 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 5);
174 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), "dim_2");
175 }
176
TEST_F(KllMetricE2eTest,TestInitWithKllFieldPositionALL)177 TEST_F(KllMetricE2eTest, TestInitWithKllFieldPositionALL) {
178 // Create config.
179 StatsdConfig config;
180 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
181
182 AtomMatcher testAtomReportedMatcher =
183 CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
184 *config.add_atom_matcher() = testAtomReportedMatcher;
185
186 // Create kll metric.
187 int64_t metricId = 123456;
188 KllMetric* kllMetric = config.add_kll_metric();
189 kllMetric->set_id(metricId);
190 kllMetric->set_bucket(TimeUnit::FIVE_MINUTES);
191 kllMetric->set_what(testAtomReportedMatcher.id());
192 *kllMetric->mutable_kll_field() = CreateRepeatedDimensions(
193 util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::ALL});
194
195 // Initialize StatsLogProcessor.
196 const uint64_t bucketStartTimeNs = 10000000000; // 0:10
197 int uid = 12345;
198 int64_t cfgId = 98765;
199 ConfigKey cfgKey(uid, cfgId);
200 sp<StatsLogProcessor> processor =
201 CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
202
203 // Config initialization fails.
204 ASSERT_EQ(0, processor->mMetricsManagers.size());
205 }
206
TEST_F(KllMetricE2eTest,TestDimensionalSampling)207 TEST_F(KllMetricE2eTest, TestDimensionalSampling) {
208 ShardOffsetProvider::getInstance().setShardOffset(5);
209
210 // Create config.
211 StatsdConfig config;
212 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
213
214 AtomMatcher bleScanResultReceivedMatcher = CreateSimpleAtomMatcher(
215 "BleScanResultReceivedAtomMatcher", util::BLE_SCAN_RESULT_RECEIVED);
216 *config.add_atom_matcher() = bleScanResultReceivedMatcher;
217
218 // Create kll metric.
219 KllMetric sampledKllMetric =
220 createKllMetric("KllSampledBleScanResultsPerUid", bleScanResultReceivedMatcher,
221 /*num_results=*/2, nullopt);
222 *sampledKllMetric.mutable_dimensions_in_what() =
223 CreateAttributionUidDimensions(util::BLE_SCAN_RESULT_RECEIVED, {Position::FIRST});
224 *sampledKllMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
225 CreateAttributionUidDimensions(util::BLE_SCAN_RESULT_RECEIVED, {Position::FIRST});
226 sampledKllMetric.mutable_dimensional_sampling_info()->set_shard_count(2);
227 *config.add_kll_metric() = sampledKllMetric;
228
229 // Initialize StatsLogProcessor.
230 const uint64_t bucketStartTimeNs = 10000000000; // 0:10
231 int uid = 12345;
232 int64_t cfgId = 98765;
233 ConfigKey cfgKey(uid, cfgId);
234
235 sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
236 bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap());
237
238 int appUid1 = 1001; // odd hash value
239 int appUid2 = 1002; // even hash value
240 int appUid3 = 1003; // odd hash value
241 std::vector<std::unique_ptr<LogEvent>> events;
242
243 events.push_back(CreateBleScanResultReceivedEvent(bucketStartTimeNs + 20 * NS_PER_SEC,
244 {appUid1}, {"tag1"}, 10));
245 events.push_back(CreateBleScanResultReceivedEvent(bucketStartTimeNs + 40 * NS_PER_SEC,
246 {appUid2}, {"tag2"}, 10));
247 events.push_back(CreateBleScanResultReceivedEvent(bucketStartTimeNs + 60 * NS_PER_SEC,
248 {appUid3}, {"tag3"}, 10));
249
250 events.push_back(CreateBleScanResultReceivedEvent(bucketStartTimeNs + 120 * NS_PER_SEC,
251 {appUid1}, {"tag1"}, 11));
252 events.push_back(CreateBleScanResultReceivedEvent(bucketStartTimeNs + 140 * NS_PER_SEC,
253 {appUid2}, {"tag2"}, 12));
254 events.push_back(CreateBleScanResultReceivedEvent(bucketStartTimeNs + 160 * NS_PER_SEC,
255 {appUid3}, {"tag3"}, 13));
256
257 // Send log events to StatsLogProcessor.
258 for (auto& event : events) {
259 processor->OnLogEvent(event.get());
260 }
261
262 // Check dump report.
263 vector<uint8_t> buffer;
264 ConfigMetricsReportList reports;
265 processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
266 FAST, &buffer);
267 ASSERT_GT(buffer.size(), 0);
268 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
269 backfillDimensionPath(&reports);
270 backfillStringInReport(&reports);
271 backfillStartEndTimestamp(&reports);
272 backfillAggregatedAtoms(&reports);
273
274 ConfigMetricsReport report = reports.reports(0);
275 ASSERT_EQ(report.metrics_size(), 1);
276 StatsLogReport metricReport = report.metrics(0);
277 EXPECT_EQ(metricReport.metric_id(), sampledKllMetric.id());
278 EXPECT_TRUE(metricReport.has_kll_metrics());
279 StatsLogReport::KllMetricDataWrapper kllMetrics;
280 sortMetricDataByDimensionsValue(metricReport.kll_metrics(), &kllMetrics);
281 ASSERT_EQ(kllMetrics.data_size(), 2);
282 EXPECT_EQ(kllMetrics.skipped_size(), 0);
283
284 // Only Uid 1 and 3 are logged. (odd hash value) + (offset of 5) % (shard count of 2) = 0
285 KllMetricData data = kllMetrics.data(0);
286 ValidateAttributionUidDimension(data.dimensions_in_what(), util::BLE_SCAN_RESULT_RECEIVED,
287 appUid1);
288 ValidateKllBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + bucketSizeNs, {2},
289 0);
290
291 data = kllMetrics.data(1);
292 ValidateAttributionUidDimension(data.dimensions_in_what(), util::BLE_SCAN_RESULT_RECEIVED,
293 appUid3);
294 ValidateKllBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + bucketSizeNs, {2},
295 0);
296 }
297
298 #else
299 GTEST_LOG_(INFO) << "This test does nothing.\n";
300 #endif
301
302 } // namespace statsd
303 } // namespace os
304 } // namespace android
305