1 // Copyright (C) 2017 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 "src/stats_log_util.h"
19 #include "tests/statsd_test_util.h"
20
21 #include <vector>
22
23 namespace android {
24 namespace os {
25 namespace statsd {
26
27 #ifdef __ANDROID__
28
29 namespace {
30
CreateDurationMetricConfig_NoLink_SimpleCondition(DurationMetric::AggregationType aggregationType,bool addExtraDimensionInCondition)31 StatsdConfig CreateDurationMetricConfig_NoLink_SimpleCondition(
32 DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
33 StatsdConfig config;
34 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
35 *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
36 *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
37 *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
38 *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
39
40 auto scheduledJobPredicate = CreateScheduledJobPredicate();
41 auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
42 dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
43 dimensions->add_child()->set_field(2); // job name field.
44
45 auto isSyncingPredicate = CreateIsSyncingPredicate();
46 auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
47 *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
48 {Position::FIRST});
49 if (addExtraDimensionInCondition) {
50 syncDimension->add_child()->set_field(2 /* name field*/);
51 }
52
53 *config.add_predicate() = scheduledJobPredicate;
54 *config.add_predicate() = isSyncingPredicate;
55
56 auto metric = config.add_duration_metric();
57 metric->set_bucket(FIVE_MINUTES);
58 metric->set_id(StringToId("scheduledJob"));
59 metric->set_what(scheduledJobPredicate.id());
60 metric->set_condition(isSyncingPredicate.id());
61 metric->set_aggregation_type(aggregationType);
62 auto dimensionWhat = metric->mutable_dimensions_in_what();
63 dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
64 dimensionWhat->add_child()->set_field(2); // job name field.
65 *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
66 android::util::SYNC_STATE_CHANGED, {Position::FIRST});
67 return config;
68 }
69
70 } // namespace
71
TEST(DimensionInConditionE2eTest,TestDurationMetric_NoLink_SimpleCondition)72 TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition) {
73 for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : {true, false}) {
74 for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
75 ConfigKey cfgKey;
76 auto config = CreateDurationMetricConfig_NoLink_SimpleCondition(
77 aggregationType, isDimensionInConditionSubSetOfConditionTrackerDimension);
78 int64_t bucketStartTimeNs = 10000000000;
79 int64_t bucketSizeNs =
80 TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
81
82 auto processor = CreateStatsLogProcessor(
83 bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
84 EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
85 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
86
87 std::vector<AttributionNodeInternal> attributions1 = {
88 CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
89 CreateAttribution(222, "GMSCoreModule2")};
90
91 std::vector<AttributionNodeInternal> attributions2 = {
92 CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
93 CreateAttribution(555, "GMSCoreModule2")};
94
95 std::vector<std::unique_ptr<LogEvent>> events;
96
97 events.push_back(CreateStartScheduledJobEvent(
98 {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 1));
99 events.push_back(CreateFinishScheduledJobEvent(
100 {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101));
101
102 events.push_back(CreateStartScheduledJobEvent(
103 {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201));
104 events.push_back(CreateFinishScheduledJobEvent(
105 {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500));
106
107 events.push_back(CreateStartScheduledJobEvent(
108 {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600));
109 events.push_back(CreateFinishScheduledJobEvent(
110 {CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850));
111
112 events.push_back(CreateStartScheduledJobEvent(
113 {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600));
114 events.push_back(CreateFinishScheduledJobEvent(
115 {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900));
116
117 events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
118 bucketStartTimeNs + 10));
119 events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
120 bucketStartTimeNs + 50));
121
122 events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
123 bucketStartTimeNs + 200));
124 events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
125 bucketStartTimeNs + bucketSizeNs + 300));
126
127 events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc",
128 bucketStartTimeNs + 400));
129 events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
130 bucketStartTimeNs + bucketSizeNs - 1));
131
132 events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
133 bucketStartTimeNs + 401));
134 events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
135 bucketStartTimeNs + bucketSizeNs + 700));
136
137 sortLogEventsByTimestamp(&events);
138
139 for (const auto& event : events) {
140 processor->OnLogEvent(event.get());
141 }
142
143 ConfigMetricsReportList reports;
144 vector<uint8_t> buffer;
145 processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
146 true, ADB_DUMP, FAST, &buffer);
147 EXPECT_TRUE(buffer.size() > 0);
148 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
149 backfillDimensionPath(&reports);
150 backfillStringInReport(&reports);
151 backfillStartEndTimestamp(&reports);
152
153 EXPECT_EQ(reports.reports_size(), 1);
154 EXPECT_EQ(reports.reports(0).metrics_size(), 1);
155 StatsLogReport::DurationMetricDataWrapper metrics;
156 sortMetricDataByDimensionsValue(
157 reports.reports(0).metrics(0).duration_metrics(), &metrics);
158 if (aggregationType == DurationMetric::SUM) {
159 EXPECT_EQ(metrics.data_size(), 4);
160 auto data = metrics.data(0);
161 EXPECT_EQ(data.dimensions_in_what().field(),
162 android::util::SCHEDULED_JOB_STATE_CHANGED);
163 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
164 2); // job name field
165 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
166 "job0"); // job name
167 ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
168 android::util::SYNC_STATE_CHANGED, 111, "App1");
169 EXPECT_EQ(data.bucket_info_size(), 1);
170 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40);
171 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
172 bucketStartTimeNs);
173 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
174 bucketStartTimeNs + bucketSizeNs);
175
176 data = metrics.data(1);
177 EXPECT_EQ(data.dimensions_in_what().field(),
178 android::util::SCHEDULED_JOB_STATE_CHANGED);
179 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
180 2); // job name field
181 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
182 "job1"); // job name
183 ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
184 android::util::SYNC_STATE_CHANGED, 333, "App2");
185 EXPECT_EQ(data.bucket_info_size(), 1);
186 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
187 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
188 bucketStartTimeNs + bucketSizeNs);
189 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
190 bucketStartTimeNs + 2 * bucketSizeNs);
191
192 data = metrics.data(2);
193 EXPECT_EQ(data.dimensions_in_what().field(),
194 android::util::SCHEDULED_JOB_STATE_CHANGED);
195 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
196 2); // job name field
197 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
198 "job2"); // job name
199 ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
200 android::util::SYNC_STATE_CHANGED, 111, "App1");
201 EXPECT_EQ(data.bucket_info_size(), 2);
202 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
203 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
204 bucketStartTimeNs + bucketSizeNs);
205 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
206 EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
207 EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
208 bucketStartTimeNs + bucketSizeNs);
209 EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
210 bucketStartTimeNs + 2 * bucketSizeNs);
211
212 data = metrics.data(3);
213 EXPECT_EQ(data.dimensions_in_what().field(),
214 android::util::SCHEDULED_JOB_STATE_CHANGED);
215 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
216 2); // job name field
217 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
218 "job2"); // job name
219 ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
220 android::util::SYNC_STATE_CHANGED, 333, "App2");
221 EXPECT_EQ(data.bucket_info_size(), 2);
222 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
223 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
224 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
225 bucketStartTimeNs + bucketSizeNs);
226 EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
227 EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
228 bucketStartTimeNs + bucketSizeNs);
229 EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
230 bucketStartTimeNs + 2 * bucketSizeNs);
231 } else {
232 EXPECT_EQ(metrics.data_size(), 4);
233 auto data = metrics.data(0);
234 EXPECT_EQ(data.dimensions_in_what().field(),
235 android::util::SCHEDULED_JOB_STATE_CHANGED);
236 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
237 2); // job name field
238 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
239 "job0"); // job name
240 ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
241 android::util::SYNC_STATE_CHANGED, 111, "App1");
242 EXPECT_EQ(data.bucket_info_size(), 1);
243 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40);
244 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
245 bucketStartTimeNs);
246 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
247 bucketStartTimeNs + bucketSizeNs);
248
249 data = metrics.data(1);
250 EXPECT_EQ(data.dimensions_in_what().field(),
251 android::util::SCHEDULED_JOB_STATE_CHANGED);
252 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
253 2); // job name field
254 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
255 "job1"); // job name
256 ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
257 android::util::SYNC_STATE_CHANGED, 333, "App2");
258 EXPECT_EQ(data.bucket_info_size(), 1);
259 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
260 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
261 bucketStartTimeNs + bucketSizeNs);
262 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
263 bucketStartTimeNs + 2 * bucketSizeNs);
264
265 data = metrics.data(2);
266 EXPECT_EQ(data.dimensions_in_what().field(),
267 android::util::SCHEDULED_JOB_STATE_CHANGED);
268 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
269 2); // job name field
270 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
271 "job2"); // job name
272 ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
273 android::util::SYNC_STATE_CHANGED, 111, "App1");
274 EXPECT_EQ(data.bucket_info_size(), 2);
275 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
276 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
277 bucketStartTimeNs + bucketSizeNs);
278 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201);
279 EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 300);
280 EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
281 bucketStartTimeNs + bucketSizeNs);
282 EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
283 bucketStartTimeNs + 2 * bucketSizeNs);
284
285 data = metrics.data(3);
286 EXPECT_EQ(data.dimensions_in_what().field(),
287 android::util::SCHEDULED_JOB_STATE_CHANGED);
288 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
289 2); // job name field
290 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
291 "job2"); // job name
292 ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
293 android::util::SYNC_STATE_CHANGED, 333, "App2");
294 EXPECT_EQ(data.bucket_info_size(), 2);
295 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 );
296 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
297 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
298 bucketStartTimeNs + bucketSizeNs);
299 EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
300 EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
301 bucketStartTimeNs + bucketSizeNs);
302 EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
303 bucketStartTimeNs + 2 * bucketSizeNs);
304 }
305 }
306 }
307 }
308
309 namespace {
310
createDurationMetric_Link_SimpleConditionConfig(DurationMetric::AggregationType aggregationType,bool addExtraDimensionInCondition)311 StatsdConfig createDurationMetric_Link_SimpleConditionConfig(
312 DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
313 StatsdConfig config;
314 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
315 *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
316 *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
317 *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
318 *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
319
320 auto scheduledJobPredicate = CreateScheduledJobPredicate();
321 auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
322 *dimensions = CreateAttributionUidDimensions(
323 android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
324 dimensions->add_child()->set_field(2); // job name field.
325
326 auto isSyncingPredicate = CreateIsSyncingPredicate();
327 auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
328 *syncDimension = CreateAttributionUidDimensions(
329 android::util::SYNC_STATE_CHANGED, {Position::FIRST});
330 if (addExtraDimensionInCondition) {
331 syncDimension->add_child()->set_field(2 /* name field*/);
332 }
333
334 *config.add_predicate() = scheduledJobPredicate;
335 *config.add_predicate() = isSyncingPredicate;
336
337 auto metric = config.add_duration_metric();
338 metric->set_bucket(FIVE_MINUTES);
339 metric->set_id(StringToId("scheduledJob"));
340 metric->set_what(scheduledJobPredicate.id());
341 metric->set_condition(isSyncingPredicate.id());
342 metric->set_aggregation_type(aggregationType);
343 *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
344 android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
345
346 auto links = metric->add_links();
347 links->set_condition(isSyncingPredicate.id());
348 *links->mutable_fields_in_what() =
349 CreateAttributionUidDimensions(
350 android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
351 *links->mutable_fields_in_condition() =
352 CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
353 return config;
354 }
355
356 } // namespace
357
TEST(DimensionInConditionE2eTest,TestDurationMetric_Link_SimpleCondition)358 TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition) {
359 for (bool isFullLink : {true, false}) {
360 for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
361 ConfigKey cfgKey;
362 auto config = createDurationMetric_Link_SimpleConditionConfig(
363 aggregationType, !isFullLink);
364 int64_t bucketStartTimeNs = 10000000000;
365 int64_t bucketSizeNs =
366 TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
367
368 auto processor = CreateStatsLogProcessor(
369 bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
370 EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
371 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
372
373 std::vector<AttributionNodeInternal> attributions1 = {
374 CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
375 CreateAttribution(222, "GMSCoreModule2")};
376
377 std::vector<AttributionNodeInternal> attributions2 = {
378 CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
379 CreateAttribution(555, "GMSCoreModule2")};
380
381 std::vector<AttributionNodeInternal> attributions3 = {
382 CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
383 CreateAttribution(555, "GMSCoreModule2")};
384
385 std::vector<std::unique_ptr<LogEvent>> events;
386
387 events.push_back(CreateStartScheduledJobEvent(
388 {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
389 events.push_back(CreateFinishScheduledJobEvent(
390 {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
391
392 events.push_back(CreateStartScheduledJobEvent(
393 {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
394 events.push_back(CreateFinishScheduledJobEvent(
395 {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
396 events.push_back(CreateStartScheduledJobEvent(
397 {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
398 events.push_back(
399 CreateFinishScheduledJobEvent({CreateAttribution(333, "App2")}, "job2",
400 bucketStartTimeNs + bucketSizeNs + 850));
401
402 events.push_back(
403 CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
404 bucketStartTimeNs + bucketSizeNs - 2));
405 events.push_back(
406 CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
407 bucketStartTimeNs + bucketSizeNs + 900));
408
409 events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
410 bucketStartTimeNs + 50));
411 events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
412 bucketStartTimeNs + 110));
413
414 events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
415 bucketStartTimeNs + 300));
416 events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
417 bucketStartTimeNs + bucketSizeNs + 700));
418 events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
419 bucketStartTimeNs + 400));
420 events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
421 bucketStartTimeNs + bucketSizeNs - 1));
422
423 events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
424 bucketStartTimeNs + 550));
425 events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
426 bucketStartTimeNs + 800));
427 events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
428 bucketStartTimeNs + bucketSizeNs - 1));
429 events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
430 bucketStartTimeNs + bucketSizeNs + 700));
431
432 sortLogEventsByTimestamp(&events);
433
434 for (const auto& event : events) {
435 processor->OnLogEvent(event.get());
436 }
437
438 ConfigMetricsReportList reports;
439 vector<uint8_t> buffer;
440 processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
441 true, ADB_DUMP, FAST, &buffer);
442 EXPECT_TRUE(buffer.size() > 0);
443 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
444 backfillDimensionPath(&reports);
445 backfillStringInReport(&reports);
446 backfillStartEndTimestamp(&reports);
447
448 EXPECT_EQ(reports.reports_size(), 1);
449 EXPECT_EQ(reports.reports(0).metrics_size(), 1);
450 StatsLogReport::DurationMetricDataWrapper metrics;
451 sortMetricDataByDimensionsValue(
452 reports.reports(0).metrics(0).duration_metrics(), &metrics);
453
454 if (aggregationType == DurationMetric::SUM) {
455 EXPECT_EQ(metrics.data_size(), 3);
456 auto data = metrics.data(0);
457 ValidateAttributionUidDimension(
458 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
459 EXPECT_EQ(data.bucket_info_size(), 1);
460 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
461 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
462 bucketStartTimeNs);
463 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
464 bucketStartTimeNs + bucketSizeNs);
465
466 data = metrics.data(1);
467 ValidateAttributionUidDimension(
468 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
469 EXPECT_EQ(data.bucket_info_size(), 2);
470 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
471 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
472 bucketStartTimeNs + bucketSizeNs);
473 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300 + bucketSizeNs - 600);
474 EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
475 EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
476 bucketStartTimeNs + bucketSizeNs);
477 EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
478 bucketStartTimeNs + 2 * bucketSizeNs);
479
480 data = metrics.data(2);
481 ValidateAttributionUidDimension(
482 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
483 EXPECT_EQ(data.bucket_info_size(), 2);
484 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
485 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
486 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
487 bucketStartTimeNs + bucketSizeNs);
488 EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
489 EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
490 bucketStartTimeNs + bucketSizeNs);
491 EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
492 bucketStartTimeNs + 2 * bucketSizeNs);
493 } else {
494 EXPECT_EQ(metrics.data_size(), 3);
495 auto data = metrics.data(0);
496 ValidateAttributionUidDimension(
497 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
498 EXPECT_EQ(data.bucket_info_size(), 1);
499 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
500 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
501 bucketStartTimeNs);
502 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
503 bucketStartTimeNs + bucketSizeNs);
504
505 data = metrics.data(1);
506 ValidateAttributionUidDimension(
507 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
508 EXPECT_EQ(data.bucket_info_size(), 2);
509 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
510 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
511 bucketStartTimeNs + bucketSizeNs);
512 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300);
513 EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
514 EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
515 bucketStartTimeNs + bucketSizeNs);
516 EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
517 bucketStartTimeNs + 2 * bucketSizeNs);
518
519 data = metrics.data(2);
520 ValidateAttributionUidDimension(
521 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
522 EXPECT_EQ(data.bucket_info_size(), 1);
523 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 701);
524 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
525 bucketStartTimeNs + bucketSizeNs);
526 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
527 bucketStartTimeNs + 2 * bucketSizeNs);
528 }
529 }
530 }
531 }
532
533 namespace {
534
createDurationMetric_PartialLink_SimpleConditionConfig(DurationMetric::AggregationType aggregationType)535 StatsdConfig createDurationMetric_PartialLink_SimpleConditionConfig(
536 DurationMetric::AggregationType aggregationType) {
537 StatsdConfig config;
538 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
539 *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
540 *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
541 *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
542 *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
543
544 auto scheduledJobPredicate = CreateScheduledJobPredicate();
545 auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
546 *dimensions = CreateAttributionUidDimensions(
547 android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
548 dimensions->add_child()->set_field(2); // job name field.
549
550 auto isSyncingPredicate = CreateIsSyncingPredicate();
551 auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
552 *syncDimension = CreateAttributionUidDimensions(
553 android::util::SYNC_STATE_CHANGED, {Position::FIRST});
554 syncDimension->add_child()->set_field(2 /* name field*/);
555
556 *config.add_predicate() = scheduledJobPredicate;
557 *config.add_predicate() = isSyncingPredicate;
558
559 auto metric = config.add_duration_metric();
560 metric->set_bucket(FIVE_MINUTES);
561 metric->set_id(StringToId("scheduledJob"));
562 metric->set_what(scheduledJobPredicate.id());
563 metric->set_condition(isSyncingPredicate.id());
564 metric->set_aggregation_type(aggregationType);
565 *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
566 android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
567 *metric->mutable_dimensions_in_condition() = *syncDimension;
568
569 auto links = metric->add_links();
570 links->set_condition(isSyncingPredicate.id());
571 *links->mutable_fields_in_what() =
572 CreateAttributionUidDimensions(
573 android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
574 *links->mutable_fields_in_condition() =
575 CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
576 return config;
577 }
578
579 } // namespace
580
TEST(DimensionInConditionE2eTest,TestDurationMetric_PartialLink_SimpleCondition)581 TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition) {
582 for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
583 ConfigKey cfgKey;
584 auto config = createDurationMetric_PartialLink_SimpleConditionConfig(
585 aggregationType);
586 int64_t bucketStartTimeNs = 10000000000;
587 int64_t bucketSizeNs =
588 TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
589
590 auto processor = CreateStatsLogProcessor(
591 bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
592 EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
593 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
594
595 std::vector<AttributionNodeInternal> attributions1 = {
596 CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
597 CreateAttribution(222, "GMSCoreModule2")};
598
599 std::vector<AttributionNodeInternal> attributions2 = {
600 CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
601 CreateAttribution(555, "GMSCoreModule2")};
602
603 std::vector<AttributionNodeInternal> attributions3 = {
604 CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
605 CreateAttribution(555, "GMSCoreModule2")};
606
607 std::vector<std::unique_ptr<LogEvent>> events;
608
609 events.push_back(CreateStartScheduledJobEvent(
610 {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
611 events.push_back(CreateFinishScheduledJobEvent(
612 {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
613
614 events.push_back(CreateStartScheduledJobEvent(
615 {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
616 events.push_back(CreateFinishScheduledJobEvent(
617 {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
618 events.push_back(CreateStartScheduledJobEvent(
619 {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
620 events.push_back(CreateFinishScheduledJobEvent(
621 {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + bucketSizeNs + 850));
622
623 events.push_back(
624 CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
625 bucketStartTimeNs + bucketSizeNs - 2));
626 events.push_back(
627 CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
628 bucketStartTimeNs + bucketSizeNs + 900));
629
630 events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
631 bucketStartTimeNs + 50));
632 events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
633 bucketStartTimeNs + 110));
634
635 events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
636 bucketStartTimeNs + 300));
637 events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
638 bucketStartTimeNs + bucketSizeNs + 700));
639 events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
640 bucketStartTimeNs + 400));
641 events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
642 bucketStartTimeNs + bucketSizeNs - 1));
643
644 events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
645 bucketStartTimeNs + 550));
646 events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
647 bucketStartTimeNs + 800));
648 events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
649 bucketStartTimeNs + bucketSizeNs - 1));
650 events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
651 bucketStartTimeNs + bucketSizeNs + 700));
652
653 sortLogEventsByTimestamp(&events);
654
655 for (const auto& event : events) {
656 processor->OnLogEvent(event.get());
657 }
658
659 ConfigMetricsReportList reports;
660 vector<uint8_t> buffer;
661 processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
662 ADB_DUMP, FAST, &buffer);
663 EXPECT_TRUE(buffer.size() > 0);
664 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
665 backfillDimensionPath(&reports);
666 backfillStringInReport(&reports);
667 backfillStartEndTimestamp(&reports);
668
669 EXPECT_EQ(reports.reports_size(), 1);
670 EXPECT_EQ(reports.reports(0).metrics_size(), 1);
671 StatsLogReport::DurationMetricDataWrapper metrics;
672 sortMetricDataByDimensionsValue(
673 reports.reports(0).metrics(0).duration_metrics(), &metrics);
674
675 if (aggregationType == DurationMetric::SUM) {
676 EXPECT_EQ(4, metrics.data_size());
677 auto data = metrics.data(0);
678 ValidateAttributionUidDimension(
679 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
680 ValidateAttributionUidDimension(
681 data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
682 EXPECT_EQ("ReadEmail",
683 data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
684 EXPECT_EQ(data.bucket_info_size(), 1);
685 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
686 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
687 bucketStartTimeNs);
688 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
689 bucketStartTimeNs + bucketSizeNs);
690
691 data = metrics.data(1);
692 ValidateAttributionUidDimension(
693 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
694 ValidateAttributionUidDimension(
695 data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
696 EXPECT_EQ("ReadDoc",
697 data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
698 EXPECT_EQ(data.bucket_info_size(), 1);
699 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
700 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
701 bucketStartTimeNs + bucketSizeNs);
702 EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 1 - 400 - 100);
703
704 data = metrics.data(2);
705 ValidateAttributionUidDimension(
706 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
707 ValidateAttributionUidDimension(
708 data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
709 EXPECT_EQ("ReadEmail",
710 data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
711 EXPECT_EQ(data.bucket_info_size(), 2);
712 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
713 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
714 bucketStartTimeNs + bucketSizeNs);
715 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300 + bucketSizeNs - 600);
716 EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
717 EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
718 bucketStartTimeNs + bucketSizeNs);
719 EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
720 bucketStartTimeNs + 2 * bucketSizeNs);
721
722 data = metrics.data(3);
723 ValidateAttributionUidDimension(
724 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
725 ValidateAttributionUidDimension(
726 data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
727 EXPECT_EQ("ReadDoc",
728 data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
729 EXPECT_EQ(data.bucket_info_size(), 2);
730 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
731 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
732 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
733 bucketStartTimeNs + bucketSizeNs);
734 EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
735 EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
736 bucketStartTimeNs + bucketSizeNs);
737 EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
738 bucketStartTimeNs + 2 * bucketSizeNs);
739 } else {
740 EXPECT_EQ(metrics.data_size(), 4);
741 auto data = metrics.data(0);
742 ValidateAttributionUidDimension(
743 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
744 ValidateAttributionUidDimension(
745 data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
746 EXPECT_EQ("ReadEmail",
747 data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
748 EXPECT_EQ(data.bucket_info_size(), 1);
749 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
750 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
751 bucketStartTimeNs);
752 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
753 bucketStartTimeNs + bucketSizeNs);
754
755 data = metrics.data(1);
756 ValidateAttributionUidDimension(
757 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
758 ValidateAttributionUidDimension(
759 data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
760 EXPECT_EQ("ReadDoc",
761 data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
762 EXPECT_EQ(data.bucket_info_size(), 2);
763 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
764 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
765 bucketStartTimeNs + bucketSizeNs);
766 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
767 EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
768 bucketStartTimeNs + bucketSizeNs);
769 EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
770 bucketStartTimeNs + 2 * bucketSizeNs);
771 EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 1 - 600);
772
773 data = metrics.data(2);
774 ValidateAttributionUidDimension(
775 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
776 ValidateAttributionUidDimension(
777 data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
778 EXPECT_EQ("ReadEmail",
779 data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
780 EXPECT_EQ(data.bucket_info_size(), 2);
781 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
782 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
783 bucketStartTimeNs + bucketSizeNs);
784 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300);
785 EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
786 EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
787 bucketStartTimeNs + bucketSizeNs);
788 EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
789 bucketStartTimeNs + 2 * bucketSizeNs);
790
791 data = metrics.data(3);
792 ValidateAttributionUidDimension(
793 data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
794 ValidateAttributionUidDimension(
795 data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
796 EXPECT_EQ("ReadDoc",
797 data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
798 EXPECT_EQ(data.bucket_info_size(), 1);
799 EXPECT_EQ(data.bucket_info(0).duration_nanos(), 701);
800 EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
801 bucketStartTimeNs + bucketSizeNs);
802 EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
803 bucketStartTimeNs + 2 * bucketSizeNs);
804 }
805 }
806 }
807
808 #else
809 GTEST_LOG_(INFO) << "This test does nothing.\n";
810 #endif
811
812 } // namespace statsd
813 } // namespace os
814 } // namespace android
815