1 /*
2 * Copyright (C) 2024, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <gtest/gtest.h>
18 #include <gtest_matchers.h>
19
20 #include "src/StatsLogProcessor.h"
21 #include "tests/statsd_test_util.h"
22
23 namespace android {
24 namespace os {
25 namespace statsd {
26
27 #ifdef __ANDROID__
28
29 namespace {
30
31 const int64_t metricId = 123456;
32 const int TEST_ATOM_REPORTED_STRING_FIELD_ID = 5;
33 const int SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID = 1;
34 const int SUBSYSTEM_SLEEP_STATE_SUBNAME_FIELD_ID = 2;
35 const int SUBSYSTEM_SLEEP_STATE_TIME_MILLIS_FIELD_ID = 4;
36 const int SCHEDULED_JOB_STATE_CHANGED_JOB_NAME_FIELD_ID = 2;
37 const int ACTIVITY_FOREGROUND_STATE_CHANGED_UID_FIELD_ID = 1;
38 const int ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID = 2;
39 const int WAKELOCK_STATE_CHANGED_TAG_FIELD_ID = 3;
40 const int ATTRIBUTION_CHAIN_FIELD_ID = 1;
41 const int ATTRIBUTION_TAG_FIELD_ID = 2;
42
CreateTestAtomReportedEventStringDim(uint64_t timestampNs,const string & stringField)43 std::unique_ptr<LogEvent> CreateTestAtomReportedEventStringDim(uint64_t timestampNs,
44 const string& stringField) {
45 return CreateTestAtomReportedEventWithPrimitives(
46 timestampNs, 0 /* intField */, 0l /* longField */, 0.0f /* floatField */, stringField,
47 false /* boolField */, TestAtomReported::OFF /* enumField */);
48 }
49
CreateStatsdConfig()50 StatsdConfig CreateStatsdConfig() {
51 StatsdConfig config;
52 config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
53 config.set_hash_strings_in_metric_report(false);
54
55 return config;
56 }
57
58 } // namespace
59
TEST(StringReplaceE2eTest,TestPushedDimension)60 TEST(StringReplaceE2eTest, TestPushedDimension) {
61 StatsdConfig config = CreateStatsdConfig();
62
63 *config.add_atom_matcher() =
64 CreateSimpleAtomMatcher("TestAtomMatcher", util::TEST_ATOM_REPORTED);
65 FieldValueMatcher* fvm = config.mutable_atom_matcher(0)
66 ->mutable_simple_atom_matcher()
67 ->add_field_value_matcher();
68 fvm->set_field(TEST_ATOM_REPORTED_STRING_FIELD_ID);
69 StringReplacer* stringReplacer = fvm->mutable_replace_string();
70 stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
71 stringReplacer->set_replacement("");
72
73 CountMetric* countMetric = config.add_count_metric();
74 *countMetric = createCountMetric("TestCountMetric", config.atom_matcher(0).id() /* what */,
75 nullopt /* condition */, {} /* states */);
76 countMetric->mutable_dimensions_in_what()->set_field(util::TEST_ATOM_REPORTED);
77 countMetric->mutable_dimensions_in_what()->add_child()->set_field(
78 TEST_ATOM_REPORTED_STRING_FIELD_ID);
79
80 // Initialize StatsLogProcessor.
81 const uint64_t bucketStartTimeNs = 10000000000; // 0:10
82 const uint64_t bucketSizeNs =
83 TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
84 const int uid = 12345;
85 const int64_t cfgId = 98765;
86 ConfigKey cfgKey(uid, cfgId);
87
88 sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
89 bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap());
90
91 std::vector<std::unique_ptr<LogEvent>> events;
92 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 20 * NS_PER_SEC,
93 "dimA" /* stringField */)); // 0:30
94 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 40 * NS_PER_SEC,
95 "dimA123" /* stringField */)); // 0:50
96 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 60 * NS_PER_SEC,
97 "dimA123B" /* stringField */)); // 1:10
98 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 80 * NS_PER_SEC,
99 "dimC0" /* stringField */)); // 1:20
100 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 90 * NS_PER_SEC,
101 "dimC00000" /* stringField */)); // 1:30
102 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 100 * NS_PER_SEC,
103 "dimC" /* stringField */)); // 1:40
104
105 // Send log events to StatsLogProcessor.
106 for (auto& event : events) {
107 processor->OnLogEvent(event.get());
108 }
109
110 // Check dump report.
111 vector<uint8_t> buffer;
112 ConfigMetricsReportList reports;
113 processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
114 FAST, &buffer);
115 ASSERT_GT(buffer.size(), 0);
116 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
117 backfillDimensionPath(&reports);
118 backfillStringInReport(&reports);
119 backfillStartEndTimestamp(&reports);
120
121 ASSERT_EQ(1, reports.reports_size());
122 ASSERT_EQ(1, reports.reports(0).metrics_size());
123 EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
124 StatsLogReport::CountMetricDataWrapper countMetrics;
125 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
126 ASSERT_EQ(3, countMetrics.data_size());
127
128 CountMetricData data = countMetrics.data(0);
129 DimensionsValue dimValue = data.dimensions_in_what();
130 EXPECT_EQ(dimValue.field(), util::TEST_ATOM_REPORTED);
131 ASSERT_EQ(dimValue.value_tuple().dimensions_value_size(), 1);
132 EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).field(),
133 TEST_ATOM_REPORTED_STRING_FIELD_ID);
134 EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).value_str(), "dimA");
135 ASSERT_EQ(1, data.bucket_info_size());
136 EXPECT_EQ(2, data.bucket_info(0).count());
137
138 data = countMetrics.data(1);
139 dimValue = data.dimensions_in_what();
140 EXPECT_EQ(dimValue.field(), util::TEST_ATOM_REPORTED);
141 ASSERT_EQ(dimValue.value_tuple().dimensions_value_size(), 1);
142 EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).field(),
143 TEST_ATOM_REPORTED_STRING_FIELD_ID);
144 EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).value_str(), "dimA123B");
145 ASSERT_EQ(1, data.bucket_info_size());
146 EXPECT_EQ(1, data.bucket_info(0).count());
147
148 data = countMetrics.data(2);
149 dimValue = data.dimensions_in_what();
150 EXPECT_EQ(dimValue.field(), util::TEST_ATOM_REPORTED);
151 ASSERT_EQ(dimValue.value_tuple().dimensions_value_size(), 1);
152 EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).field(),
153 TEST_ATOM_REPORTED_STRING_FIELD_ID);
154 EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).value_str(), "dimC");
155 ASSERT_EQ(1, data.bucket_info_size());
156 EXPECT_EQ(3, data.bucket_info(0).count());
157 }
158
TEST(StringReplaceE2eTest,TestPushedWhat)159 TEST(StringReplaceE2eTest, TestPushedWhat) {
160 StatsdConfig config = CreateStatsdConfig();
161
162 *config.add_atom_matcher() =
163 CreateSimpleAtomMatcher("TestAtomMatcher", util::TEST_ATOM_REPORTED);
164
165 FieldValueMatcher* fvm = config.mutable_atom_matcher(0)
166 ->mutable_simple_atom_matcher()
167 ->add_field_value_matcher();
168 fvm->set_field(TEST_ATOM_REPORTED_STRING_FIELD_ID);
169 StringReplacer* stringReplacer = fvm->mutable_replace_string();
170 stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
171 stringReplacer->set_replacement("");
172
173 *config.add_gauge_metric() = createGaugeMetric(
174 "TestAtomGaugeMetric", config.atom_matcher(0).id() /* what */,
175 GaugeMetric::FIRST_N_SAMPLES, nullopt /* condition */, nullopt /* triggerEvent */);
176
177 // Initialize StatsLogProcessor.
178 const uint64_t bucketStartTimeNs = 10000000000; // 0:10
179 const uint64_t bucketSizeNs =
180 TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000LL;
181 const int uid = 12345;
182 const int64_t cfgId = 98765;
183 ConfigKey cfgKey(uid, cfgId);
184
185 sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
186 bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap());
187
188 std::vector<std::unique_ptr<LogEvent>> events;
189 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 20 * NS_PER_SEC,
190 "dimA" /* stringField */)); // 0:30
191 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 40 * NS_PER_SEC,
192 "dimA123" /* stringField */)); // 0:50
193 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 60 * NS_PER_SEC,
194 "dimA123B" /* stringField */)); // 1:10
195 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 80 * NS_PER_SEC,
196 "dimC0" /* stringField */)); // 1:20
197 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 90 * NS_PER_SEC,
198 "dimC00000" /* stringField */)); // 1:30
199 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 100 * NS_PER_SEC,
200 "dimC" /* stringField */)); // 1:40
201
202 // Send log events to StatsLogProcessor.
203 for (auto& event : events) {
204 processor->OnLogEvent(event.get());
205 }
206
207 // Check dump report.
208 vector<uint8_t> buffer;
209 ConfigMetricsReportList reports;
210 processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
211 FAST, &buffer);
212 ASSERT_GT(buffer.size(), 0);
213 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
214 backfillDimensionPath(&reports);
215 backfillStringInReport(&reports);
216 backfillStartEndTimestamp(&reports);
217 backfillAggregatedAtoms(&reports);
218
219 ASSERT_EQ(1, reports.reports_size());
220 ASSERT_EQ(1, reports.reports(0).metrics_size());
221 EXPECT_TRUE(reports.reports(0).metrics(0).has_gauge_metrics());
222 StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
223 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
224 ASSERT_EQ(gaugeMetrics.data_size(), 1);
225
226 auto data = gaugeMetrics.data(0);
227 ASSERT_EQ(1, data.bucket_info_size());
228
229 ASSERT_EQ(6, data.bucket_info(0).atom_size());
230 EXPECT_EQ(data.bucket_info(0).atom(0).test_atom_reported().string_field(), "dimA");
231 EXPECT_EQ(data.bucket_info(0).atom(1).test_atom_reported().string_field(), "dimA");
232 EXPECT_EQ(data.bucket_info(0).atom(2).test_atom_reported().string_field(), "dimA123B");
233 EXPECT_EQ(data.bucket_info(0).atom(3).test_atom_reported().string_field(), "dimC");
234 EXPECT_EQ(data.bucket_info(0).atom(4).test_atom_reported().string_field(), "dimC");
235 EXPECT_EQ(data.bucket_info(0).atom(5).test_atom_reported().string_field(), "dimC");
236 }
237
TEST(StringReplaceE2eTest,TestPulledDimension)238 TEST(StringReplaceE2eTest, TestPulledDimension) {
239 StatsdConfig config = CreateStatsdConfig();
240
241 *config.add_atom_matcher() =
242 CreateSimpleAtomMatcher("SubsystemMatcher", util::SUBSYSTEM_SLEEP_STATE);
243 FieldValueMatcher* fvm = config.mutable_atom_matcher(0)
244 ->mutable_simple_atom_matcher()
245 ->add_field_value_matcher();
246 fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID);
247 StringReplacer* stringReplacer = fvm->mutable_replace_string();
248 stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
249 stringReplacer->set_replacement("");
250
251 *config.add_gauge_metric() = createGaugeMetric(
252 "SubsystemGaugeMetric", config.atom_matcher(0).id() /* what */,
253 GaugeMetric::RANDOM_ONE_SAMPLE, nullopt /* condition */, nullopt /* triggerEvent */);
254 *config.mutable_gauge_metric(0)->mutable_dimensions_in_what() =
255 CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
256
257 int64_t baseTimeNs = getElapsedRealtimeNs();
258 int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
259 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
260
261 ConfigKey cfgKey;
262 auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
263 SharedRefBase::make<FakeSubsystemSleepCallback>(),
264 util::SUBSYSTEM_SLEEP_STATE);
265 processor->mPullerManager->ForceClearPullerCache();
266
267 // Pulling alarm arrives on time and reset the sequential pulling alarm.
268 processor->informPullAlarmFired(baseTimeNs + 2 * bucketSizeNs + 1);
269
270 ConfigMetricsReportList reports;
271 vector<uint8_t> buffer;
272 processor->onDumpReport(cfgKey, configAddedTimeNs + 3 * bucketSizeNs + 10, false, true,
273 ADB_DUMP, FAST, &buffer);
274 EXPECT_GT(buffer.size(), 0);
275 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
276 backfillDimensionPath(&reports);
277 backfillStringInReport(&reports);
278 backfillStartEndTimestamp(&reports);
279 backfillAggregatedAtoms(&reports);
280 ASSERT_EQ(1, reports.reports_size());
281 ASSERT_EQ(1, reports.reports(0).metrics_size());
282 StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
283 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
284 ASSERT_EQ(gaugeMetrics.data_size(), 1);
285
286 auto data = gaugeMetrics.data(0);
287 EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
288 ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
289 EXPECT_EQ(SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID,
290 data.dimensions_in_what().value_tuple().dimensions_value(0).field());
291
292 // Trailing numbers are trimmed from the dimension: subsystem_name_# --> subsystem_name_
293 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
294 "subsystem_name_");
295 }
296
TEST(StringReplaceE2eTest,TestPulledWhat)297 TEST(StringReplaceE2eTest, TestPulledWhat) {
298 StatsdConfig config = CreateStatsdConfig();
299
300 *config.add_atom_matcher() =
301 CreateSimpleAtomMatcher("SubsystemMatcher", util::SUBSYSTEM_SLEEP_STATE);
302 FieldValueMatcher* fvm = config.mutable_atom_matcher(0)
303 ->mutable_simple_atom_matcher()
304 ->add_field_value_matcher();
305 fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBNAME_FIELD_ID);
306 StringReplacer* stringReplacer = fvm->mutable_replace_string();
307 stringReplacer->set_regex(R"(foo)");
308 stringReplacer->set_replacement("bar");
309
310 *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
311 *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
312
313 *config.add_predicate() = CreateScreenIsOffPredicate();
314
315 *config.add_gauge_metric() =
316 createGaugeMetric("SubsystemGaugeMetric", config.atom_matcher(0).id() /* what */,
317 GaugeMetric::RANDOM_ONE_SAMPLE,
318 config.predicate(0).id() /* condition */, nullopt /* triggerEvent */);
319
320 int64_t baseTimeNs = getElapsedRealtimeNs();
321 int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
322 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
323
324 ConfigKey cfgKey;
325 auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
326 SharedRefBase::make<FakeSubsystemSleepCallback>(),
327 util::SUBSYSTEM_SLEEP_STATE);
328 processor->mPullerManager->ForceClearPullerCache();
329
330 auto screenOffEvent =
331 CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
332 processor->OnLogEvent(screenOffEvent.get());
333
334 // Pulling alarm arrives on time and reset the sequential pulling alarm.
335 processor->informPullAlarmFired(baseTimeNs + 2 * bucketSizeNs + 1);
336
337 ConfigMetricsReportList reports;
338 vector<uint8_t> buffer;
339 processor->onDumpReport(cfgKey, configAddedTimeNs + 3 * bucketSizeNs + 10, false, true,
340 ADB_DUMP, FAST, &buffer);
341 EXPECT_GT(buffer.size(), 0);
342 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
343 backfillDimensionPath(&reports);
344 backfillStringInReport(&reports);
345 backfillStartEndTimestamp(&reports);
346 backfillAggregatedAtoms(&reports);
347 ASSERT_EQ(1, reports.reports_size());
348 ASSERT_EQ(1, reports.reports(0).metrics_size());
349 StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
350 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
351 ASSERT_EQ(gaugeMetrics.data_size(), 1);
352
353 auto data = gaugeMetrics.data(0);
354 ASSERT_EQ(2, data.bucket_info_size());
355
356 ASSERT_EQ(1, data.bucket_info(0).atom_size());
357 EXPECT_EQ(data.bucket_info(0).atom(0).subsystem_sleep_state().subname(),
358 "subsystem_subname bar");
359
360 ASSERT_EQ(1, data.bucket_info(1).atom_size());
361 EXPECT_EQ(data.bucket_info(1).atom(0).subsystem_sleep_state().subname(),
362 "subsystem_subname bar");
363 }
364
TEST(StringReplaceE2eTest,TestCondition)365 TEST(StringReplaceE2eTest, TestCondition) {
366 StatsdConfig config = CreateStatsdConfig();
367
368 AtomMatcher matcher = CreateStartScheduledJobAtomMatcher();
369 FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
370 fvm->set_field(SCHEDULED_JOB_STATE_CHANGED_JOB_NAME_FIELD_ID);
371 fvm->set_eq_string("foo");
372 StringReplacer* stringReplacer = fvm->mutable_replace_string();
373 stringReplacer->set_regex(R"(com.google.)");
374 stringReplacer->set_replacement("");
375 *config.add_atom_matcher() = matcher;
376 matcher = CreateFinishScheduledJobAtomMatcher();
377 fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
378 fvm->set_field(SCHEDULED_JOB_STATE_CHANGED_JOB_NAME_FIELD_ID);
379 fvm->set_eq_string("foo");
380 stringReplacer = fvm->mutable_replace_string();
381 stringReplacer->set_regex(R"(com.google.)");
382 stringReplacer->set_replacement("");
383 *config.add_atom_matcher() = matcher;
384
385 Predicate predicate = CreateScheduledJobPredicate();
386 *config.add_predicate() = predicate;
387
388 matcher = CreateSimpleAtomMatcher("TestAtomMatcher", util::TEST_ATOM_REPORTED);
389 *config.add_atom_matcher() = matcher;
390
391 CountMetric* countMetric = config.add_count_metric();
392 *countMetric = createCountMetric("TestCountMetric", matcher.id() /* what */,
393 predicate.id() /* condition */, {} /* states */);
394
395 // Initialize StatsLogProcessor.
396 const uint64_t bucketStartTimeNs = 10000000000; // 0:10
397 const uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(countMetric->bucket()) * 1000000LL;
398 const int uid = 12345;
399 const int64_t cfgId = 98765;
400 ConfigKey cfgKey(uid, cfgId);
401
402 sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
403 bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap());
404
405 std::vector<std::unique_ptr<LogEvent>> events;
406 events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 20 * NS_PER_SEC,
407 {1001} /* uids */, {"App1"},
408 "com.google.job1")); // 0:30
409 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 30 * NS_PER_SEC,
410 "str" /* stringField */)); // 0:40
411 events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 40 * NS_PER_SEC,
412 {1002} /* uids */, {"App1"},
413 "com.google.foo")); // 0:50
414 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 50 * NS_PER_SEC,
415 "str" /* stringField */)); // 1:00
416 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 60 * NS_PER_SEC,
417 "str" /* stringField */)); // 1:10
418 events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 70 * NS_PER_SEC,
419 {1001} /* uids */, {"App1"},
420 "com.google.job1")); // 1:20
421 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 80 * NS_PER_SEC,
422 "str" /* stringField */)); // 1:30
423 events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 90 * NS_PER_SEC,
424 {1002} /* uids */, {"App1"},
425 "com.google.foo")); // 1:40
426 events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 100 * NS_PER_SEC,
427 "str" /* stringField */)); // 1:50
428
429 // Send log events to StatsLogProcessor.
430 for (auto& event : events) {
431 processor->OnLogEvent(event.get());
432 }
433
434 // Check dump report.
435 vector<uint8_t> buffer;
436 ConfigMetricsReportList reports;
437 processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
438 FAST, &buffer);
439 ASSERT_GT(buffer.size(), 0);
440 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
441 backfillDimensionPath(&reports);
442 backfillStringInReport(&reports);
443 backfillStartEndTimestamp(&reports);
444
445 ASSERT_EQ(1, reports.reports_size());
446 ASSERT_EQ(1, reports.reports(0).metrics_size());
447 ASSERT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
448 StatsLogReport::CountMetricDataWrapper countMetrics;
449 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
450 ASSERT_EQ(1, countMetrics.data_size());
451
452 CountMetricData data = countMetrics.data(0);
453 ASSERT_EQ(1, data.bucket_info_size());
454 EXPECT_EQ(3, data.bucket_info(0).count());
455 }
456
TEST(StringReplaceE2eTest,TestDurationMetric)457 TEST(StringReplaceE2eTest, TestDurationMetric) {
458 StatsdConfig config = CreateStatsdConfig();
459
460 AtomMatcher matcher = CreateAcquireWakelockAtomMatcher();
461 FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
462 fvm->set_field(ATTRIBUTION_CHAIN_FIELD_ID);
463 fvm->set_position(Position::FIRST);
464 fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(ATTRIBUTION_TAG_FIELD_ID);
465 StringReplacer* stringReplacer =
466 fvm->mutable_matches_tuple()->mutable_field_value_matcher(0)->mutable_replace_string();
467 stringReplacer->set_regex(R"([0-9]+)");
468 stringReplacer->set_replacement("#");
469 *config.add_atom_matcher() = matcher;
470
471 matcher = CreateReleaseWakelockAtomMatcher();
472 fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
473 fvm->set_field(ATTRIBUTION_CHAIN_FIELD_ID);
474 fvm->set_position(Position::FIRST);
475 fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(ATTRIBUTION_TAG_FIELD_ID);
476 stringReplacer =
477 fvm->mutable_matches_tuple()->mutable_field_value_matcher(0)->mutable_replace_string();
478 stringReplacer->set_regex(R"([0-9]+)");
479 stringReplacer->set_replacement("#");
480 *config.add_atom_matcher() = matcher;
481
482 matcher = CreateMoveToBackgroundAtomMatcher();
483 fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
484 fvm->set_field(ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID);
485 stringReplacer = fvm->mutable_replace_string();
486 stringReplacer->set_regex(R"([0-9]+)");
487 stringReplacer->set_replacement("#");
488 *config.add_atom_matcher() = matcher;
489
490 matcher = CreateMoveToForegroundAtomMatcher();
491 fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
492 fvm->set_field(ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID);
493 stringReplacer = fvm->mutable_replace_string();
494 stringReplacer->set_regex(R"([0-9]+)");
495 stringReplacer->set_replacement("#");
496 *config.add_atom_matcher() = matcher;
497
498 Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
499 // The predicate is dimensioning by first attribution node by uid and tag.
500 *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
501 CreateAttributionUidAndTagDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
502 *config.add_predicate() = holdingWakelockPredicate;
503
504 Predicate isInBackgroundPredicate = CreateIsInBackgroundPredicate();
505 *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
506 CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED,
507 {ACTIVITY_FOREGROUND_STATE_CHANGED_UID_FIELD_ID,
508 ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID});
509 *config.add_predicate() = isInBackgroundPredicate;
510
511 DurationMetric durationMetric =
512 createDurationMetric("WakelockDuration", holdingWakelockPredicate.id() /* what */,
513 isInBackgroundPredicate.id() /* condition */, {} /* states */);
514 *durationMetric.mutable_dimensions_in_what() =
515 CreateAttributionUidAndTagDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
516 durationMetric.set_bucket(FIVE_MINUTES);
517 *config.add_duration_metric() = durationMetric;
518
519 // Links between wakelock state atom and condition of app is in background.
520 MetricConditionLink* link = durationMetric.add_links();
521 link->set_condition(isInBackgroundPredicate.id());
522 *link->mutable_fields_in_what() =
523 CreateAttributionUidAndTagDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
524 *link->mutable_fields_in_condition() =
525 CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED,
526 {ACTIVITY_FOREGROUND_STATE_CHANGED_UID_FIELD_ID,
527 ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID});
528
529 // Initialize StatsLogProcessor.
530 const uint64_t bucketStartTimeNs = 10000000000; // 0:10
531 const uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(durationMetric.bucket()) * 1000000LL;
532 const int uid = 12345;
533 const int64_t cfgId = 98765;
534 ConfigKey cfgKey(uid, cfgId);
535
536 sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
537 bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap());
538
539 int appUid = 123;
540 std::vector<int> attributionUids1 = {appUid};
541 std::vector<string> attributionTags1 = {"App1"};
542 std::vector<string> attributionTags2 = {"App2"};
543
544 std::vector<std::unique_ptr<LogEvent>> events;
545 events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
546 attributionUids1, attributionTags1,
547 "wl1")); // 0:10
548 events.push_back(CreateActivityForegroundStateChangedEvent(
549 bucketStartTimeNs + 22 * NS_PER_SEC, appUid, "App1", "class_name",
550 ActivityForegroundStateChanged::BACKGROUND)); // 0:22
551 events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 60 * NS_PER_SEC,
552 attributionUids1, attributionTags1,
553 "wl1")); // 1:00
554 events.push_back(CreateActivityForegroundStateChangedEvent(
555 bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC, appUid, "App1", "class_name",
556 ActivityForegroundStateChanged::FOREGROUND)); // 3:15
557 events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + (3 * 60 + 20) * NS_PER_SEC,
558 attributionUids1, attributionTags2,
559 "wl2")); // 3:20
560 events.push_back(CreateActivityForegroundStateChangedEvent(
561 bucketStartTimeNs + (3 * 60 + 30) * NS_PER_SEC, appUid, "App1", "class_name",
562 ActivityForegroundStateChanged::BACKGROUND)); // 3:30
563 events.push_back(CreateActivityForegroundStateChangedEvent(
564 bucketStartTimeNs + (3 * 60 + 40) * NS_PER_SEC, appUid, "App2", "class_name",
565 ActivityForegroundStateChanged::FOREGROUND)); // 3:40
566 events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + (3 * 60 + 50) * NS_PER_SEC,
567 attributionUids1, attributionTags2,
568 "wl2")); // 3:50
569
570 // Send log events to StatsLogProcessor.
571 for (auto& event : events) {
572 processor->OnLogEvent(event.get());
573 }
574
575 vector<uint8_t> buffer;
576 ConfigMetricsReportList reports;
577 processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
578 FAST, &buffer);
579
580 ASSERT_GT(buffer.size(), 0);
581 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
582 backfillDimensionPath(&reports);
583 backfillStringInReport(&reports);
584 backfillStartEndTimestamp(&reports);
585
586 ASSERT_EQ(1, reports.reports_size());
587 ASSERT_EQ(1, reports.reports(0).metrics_size());
588 ASSERT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
589 StatsLogReport::DurationMetricDataWrapper durationMetrics;
590 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
591 &durationMetrics);
592 ASSERT_EQ(1, durationMetrics.data_size());
593
594 DurationMetricData data = durationMetrics.data(0);
595 // Validate dimension value.
596 ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
597 appUid, "App#");
598 // Validate bucket info.
599 ASSERT_EQ(1, data.bucket_info_size());
600
601 auto bucketInfo = data.bucket_info(0);
602 EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
603 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
604 EXPECT_EQ(48 * NS_PER_SEC, bucketInfo.duration_nanos());
605 }
606
TEST(StringReplaceE2eTest,TestMultipleMatchersForAtom)607 TEST(StringReplaceE2eTest, TestMultipleMatchersForAtom) {
608 StatsdConfig config = CreateStatsdConfig();
609
610 {
611 AtomMatcher matcher = CreateSimpleAtomMatcher("Matcher1", util::SUBSYSTEM_SLEEP_STATE);
612 FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
613 fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID);
614 fvm->set_eq_string("subsystem_name_1");
615 fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
616 fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBNAME_FIELD_ID);
617 fvm->set_eq_string("subsystem_subname bar");
618 StringReplacer* stringReplacer = fvm->mutable_replace_string();
619 stringReplacer->set_regex(R"(foo)");
620 stringReplacer->set_replacement("bar");
621 *config.add_atom_matcher() = matcher;
622
623 *config.add_value_metric() =
624 createValueMetric("Value1", matcher, SUBSYSTEM_SLEEP_STATE_TIME_MILLIS_FIELD_ID,
625 nullopt /* condition */, {} /* states */);
626 }
627 {
628 AtomMatcher matcher = CreateSimpleAtomMatcher("Matcher2", util::SUBSYSTEM_SLEEP_STATE);
629 FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
630 fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID);
631 fvm->set_eq_string("subsystem_name_2");
632 fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
633 fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBNAME_FIELD_ID);
634 fvm->set_eq_string("subsystem_subname foo");
635 *config.add_atom_matcher() = matcher;
636
637 *config.add_value_metric() =
638 createValueMetric("Value2", matcher, SUBSYSTEM_SLEEP_STATE_TIME_MILLIS_FIELD_ID,
639 nullopt /* condition */, {} /* states */);
640 }
641
642 int64_t baseTimeNs = getElapsedRealtimeNs();
643 int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
644 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
645
646 ConfigKey cfgKey;
647 auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
648 SharedRefBase::make<FakeSubsystemSleepCallback>(),
649 util::SUBSYSTEM_SLEEP_STATE);
650 processor->mPullerManager->ForceClearPullerCache();
651
652 // Pulling alarm arrives on time and reset the sequential pulling alarm.
653 processor->informPullAlarmFired(baseTimeNs + 2 * bucketSizeNs + 1);
654
655 ConfigMetricsReportList reports;
656 vector<uint8_t> buffer;
657 processor->onDumpReport(cfgKey, configAddedTimeNs + 3 * bucketSizeNs + 10, false, true,
658 ADB_DUMP, FAST, &buffer);
659 EXPECT_GT(buffer.size(), 0);
660 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
661 backfillDimensionPath(&reports);
662 backfillStringInReport(&reports);
663 backfillStartEndTimestamp(&reports);
664 backfillAggregatedAtoms(&reports);
665 ASSERT_EQ(1, reports.reports_size());
666 ASSERT_EQ(2, reports.reports(0).metrics_size());
667
668 {
669 StatsLogReport::ValueMetricDataWrapper valueMetrics;
670 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(),
671 &valueMetrics);
672 ASSERT_EQ(valueMetrics.data_size(), 1);
673
674 ValueMetricData data = valueMetrics.data(0);
675 ASSERT_EQ(data.bucket_info_size(), 1);
676 ASSERT_EQ(data.bucket_info(0).values_size(), 1);
677 }
678 {
679 StatsLogReport::ValueMetricDataWrapper valueMetrics;
680 sortMetricDataByDimensionsValue(reports.reports(0).metrics(1).value_metrics(),
681 &valueMetrics);
682 ASSERT_EQ(valueMetrics.data_size(), 1);
683
684 ValueMetricData data = valueMetrics.data(0);
685 ASSERT_EQ(data.bucket_info_size(), 1);
686 ASSERT_EQ(data.bucket_info(0).values_size(), 1);
687 }
688 }
689
690 } // namespace statsd
691 } // namespace os
692 } // namespace android
693 #else
694 GTEST_LOG_(INFO) << "This test does nothing.\n";
695 #endif
696