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