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 <vector>
18
19 #include "src/StatsLogProcessor.h"
20 #include "src/stats_log_util.h"
21 #include "stats_event.h"
22 #include "tests/statsd_test_util.h"
23
24 namespace android {
25 namespace os {
26 namespace statsd {
27
28 #ifdef __ANDROID__
29
30 namespace {
31
CreateStatsdConfigForPushedEvent(const GaugeMetric::SamplingType sampling_type)32 StatsdConfig CreateStatsdConfigForPushedEvent(const GaugeMetric::SamplingType sampling_type) {
33 StatsdConfig config;
34 *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
35 *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
36
37 auto atomMatcher = CreateSimpleAtomMatcher("", util::APP_START_OCCURRED);
38 *config.add_atom_matcher() = atomMatcher;
39
40 auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
41 *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
42 CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
43 *config.add_predicate() = isInBackgroundPredicate;
44
45 auto gaugeMetric = config.add_gauge_metric();
46 gaugeMetric->set_id(123456);
47 gaugeMetric->set_what(atomMatcher.id());
48 gaugeMetric->set_condition(isInBackgroundPredicate.id());
49 gaugeMetric->mutable_gauge_fields_filter()->set_include_all(false);
50 gaugeMetric->set_sampling_type(sampling_type);
51 auto fieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields();
52 fieldMatcher->set_field(util::APP_START_OCCURRED);
53 fieldMatcher->add_child()->set_field(3); // type (enum)
54 fieldMatcher->add_child()->set_field(4); // activity_name(str)
55 fieldMatcher->add_child()->set_field(7); // activity_start_msec(int64)
56 *gaugeMetric->mutable_dimensions_in_what() =
57 CreateDimensions(util::APP_START_OCCURRED, {1 /* uid field */ });
58 gaugeMetric->set_bucket(FIVE_MINUTES);
59
60 auto links = gaugeMetric->add_links();
61 links->set_condition(isInBackgroundPredicate.id());
62 auto dimensionWhat = links->mutable_fields_in_what();
63 dimensionWhat->set_field(util::APP_START_OCCURRED);
64 dimensionWhat->add_child()->set_field(1); // uid field.
65 auto dimensionCondition = links->mutable_fields_in_condition();
66 dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED);
67 dimensionCondition->add_child()->set_field(1); // uid field.
68 return config;
69 }
70
CreateStatsdConfigForRepeatedFieldsPushedEvent(const GaugeMetric::SamplingType sampling_type)71 StatsdConfig CreateStatsdConfigForRepeatedFieldsPushedEvent(
72 const GaugeMetric::SamplingType sampling_type) {
73 StatsdConfig config;
74
75 AtomMatcher testAtomReportedAtomMatcher =
76 CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
77 *config.add_atom_matcher() = testAtomReportedAtomMatcher;
78
79 GaugeMetric* gaugeMetric = config.add_gauge_metric();
80 gaugeMetric->set_id(123456);
81 gaugeMetric->set_what(testAtomReportedAtomMatcher.id());
82 gaugeMetric->set_sampling_type(sampling_type);
83 FieldMatcher* fieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields();
84 fieldMatcher->set_field(util::TEST_ATOM_REPORTED);
85
86 FieldMatcher* childFieldMatcher = fieldMatcher->add_child();
87 childFieldMatcher->set_field(9); // repeated_int_field
88 childFieldMatcher->set_position(Position::FIRST);
89
90 childFieldMatcher = fieldMatcher->add_child();
91 childFieldMatcher->set_field(10); // repeated_long_field
92 childFieldMatcher->set_position(Position::LAST);
93
94 childFieldMatcher = fieldMatcher->add_child();
95 childFieldMatcher->set_field(11); // repeated_float_field
96 childFieldMatcher->set_position(Position::ALL);
97
98 childFieldMatcher = fieldMatcher->add_child();
99 childFieldMatcher->set_field(12); // repeated_string_field
100 childFieldMatcher->set_position(Position::FIRST);
101
102 childFieldMatcher = fieldMatcher->add_child();
103 childFieldMatcher->set_field(13); // repeated_boolean_field
104 childFieldMatcher->set_position(Position::LAST);
105
106 childFieldMatcher = fieldMatcher->add_child();
107 childFieldMatcher->set_field(14); // repeated_enum_field
108 childFieldMatcher->set_position(Position::ALL);
109
110 gaugeMetric->set_bucket(FIVE_MINUTES);
111 return config;
112 }
113
114 } // namespace
115
116 // Setup for test fixture.
117 class GaugeMetricE2ePushedTest : public ::testing::Test {
SetUp()118 void SetUp() override {
119 FlagProvider::getInstance().overrideFuncs(&isAtLeastSFuncTrue);
120 }
121
TearDown()122 void TearDown() override {
123 FlagProvider::getInstance().resetOverrides();
124 }
125
126 public:
127 void doTestRepeatedFieldsForPushedEvent();
128 };
129
TEST_F(GaugeMetricE2ePushedTest,TestMultipleFieldsForPushedEvent)130 TEST_F(GaugeMetricE2ePushedTest, TestMultipleFieldsForPushedEvent) {
131 for (const auto& sampling_type :
132 {GaugeMetric::FIRST_N_SAMPLES, GaugeMetric::RANDOM_ONE_SAMPLE}) {
133 auto config = CreateStatsdConfigForPushedEvent(sampling_type);
134 int64_t bucketStartTimeNs = 10000000000;
135 int64_t bucketSizeNs =
136 TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
137
138 ConfigKey cfgKey;
139 auto processor =
140 CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
141 ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
142 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
143
144 int appUid1 = 123;
145 int appUid2 = 456;
146 std::vector<std::unique_ptr<LogEvent>> events;
147 events.push_back(CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid1));
148 events.push_back(
149 CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid1));
150 events.push_back(
151 CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid1));
152 events.push_back(
153 CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, appUid1));
154
155 events.push_back(CreateAppStartOccurredEvent(
156 bucketStartTimeNs + 10, appUid1, "app1", AppStartOccurred::WARM, "activity_name1",
157 "calling_pkg_name1", true /*is_instant_app*/, 101 /*activity_start_msec*/));
158 events.push_back(CreateAppStartOccurredEvent(
159 bucketStartTimeNs + 20, appUid1, "app1", AppStartOccurred::HOT, "activity_name2",
160 "calling_pkg_name2", true /*is_instant_app*/, 102 /*activity_start_msec*/));
161 events.push_back(CreateAppStartOccurredEvent(
162 bucketStartTimeNs + 30, appUid1, "app1", AppStartOccurred::COLD, "activity_name3",
163 "calling_pkg_name3", true /*is_instant_app*/, 103 /*activity_start_msec*/));
164 events.push_back(CreateAppStartOccurredEvent(
165 bucketStartTimeNs + bucketSizeNs + 30, appUid1, "app1", AppStartOccurred::WARM,
166 "activity_name4", "calling_pkg_name4", true /*is_instant_app*/,
167 104 /*activity_start_msec*/));
168 events.push_back(CreateAppStartOccurredEvent(
169 bucketStartTimeNs + 2 * bucketSizeNs, appUid1, "app1", AppStartOccurred::COLD,
170 "activity_name5", "calling_pkg_name5", true /*is_instant_app*/,
171 105 /*activity_start_msec*/));
172 events.push_back(CreateAppStartOccurredEvent(
173 bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid1, "app1", AppStartOccurred::HOT,
174 "activity_name6", "calling_pkg_name6", false /*is_instant_app*/,
175 106 /*activity_start_msec*/));
176
177 events.push_back(
178 CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 10, appUid2));
179 events.push_back(CreateAppStartOccurredEvent(
180 bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid2, "app2", AppStartOccurred::COLD,
181 "activity_name7", "calling_pkg_name7", true /*is_instant_app*/,
182 201 /*activity_start_msec*/));
183
184 sortLogEventsByTimestamp(&events);
185
186 for (const auto& event : events) {
187 processor->OnLogEvent(event.get());
188 }
189 ConfigMetricsReportList reports;
190 vector<uint8_t> buffer;
191 processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true, ADB_DUMP,
192 FAST, &buffer);
193 EXPECT_TRUE(buffer.size() > 0);
194 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
195 backfillDimensionPath(&reports);
196 backfillStringInReport(&reports);
197 backfillStartEndTimestamp(&reports);
198 backfillAggregatedAtoms(&reports);
199 ASSERT_EQ(1, reports.reports_size());
200 ASSERT_EQ(1, reports.reports(0).metrics_size());
201 EXPECT_TRUE(reports.reports(0).metrics(0).has_estimated_data_bytes());
202 StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
203 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(),
204 &gaugeMetrics);
205 ASSERT_EQ(2, gaugeMetrics.data_size());
206
207 auto data = gaugeMetrics.data(0);
208 EXPECT_EQ(util::APP_START_OCCURRED, data.dimensions_in_what().field());
209 ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
210 EXPECT_EQ(1 /* uid field */,
211 data.dimensions_in_what().value_tuple().dimensions_value(0).field());
212 EXPECT_EQ(appUid1, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
213 ASSERT_EQ(3, data.bucket_info_size());
214 if (sampling_type == GaugeMetric::FIRST_N_SAMPLES) {
215 ASSERT_EQ(2, data.bucket_info(0).atom_size());
216 ASSERT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size());
217 ASSERT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
218 EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
219 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
220 data.bucket_info(0).end_bucket_elapsed_nanos());
221 EXPECT_EQ(AppStartOccurred::HOT,
222 data.bucket_info(0).atom(0).app_start_occurred().type());
223 EXPECT_EQ("activity_name2",
224 data.bucket_info(0).atom(0).app_start_occurred().activity_name());
225 EXPECT_EQ(102L,
226 data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
227 EXPECT_EQ(AppStartOccurred::COLD,
228 data.bucket_info(0).atom(1).app_start_occurred().type());
229 EXPECT_EQ("activity_name3",
230 data.bucket_info(0).atom(1).app_start_occurred().activity_name());
231 EXPECT_EQ(103L,
232 data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis());
233
234 ASSERT_EQ(1, data.bucket_info(1).atom_size());
235 ASSERT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
236 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
237 data.bucket_info(1).start_bucket_elapsed_nanos());
238 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
239 data.bucket_info(1).end_bucket_elapsed_nanos());
240 EXPECT_EQ(AppStartOccurred::WARM,
241 data.bucket_info(1).atom(0).app_start_occurred().type());
242 EXPECT_EQ("activity_name4",
243 data.bucket_info(1).atom(0).app_start_occurred().activity_name());
244 EXPECT_EQ(104L,
245 data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
246
247 ASSERT_EQ(2, data.bucket_info(2).atom_size());
248 ASSERT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
249 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
250 data.bucket_info(2).start_bucket_elapsed_nanos());
251 EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
252 data.bucket_info(2).end_bucket_elapsed_nanos());
253 EXPECT_EQ(AppStartOccurred::COLD,
254 data.bucket_info(2).atom(0).app_start_occurred().type());
255 EXPECT_EQ("activity_name5",
256 data.bucket_info(2).atom(0).app_start_occurred().activity_name());
257 EXPECT_EQ(105L,
258 data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis());
259 EXPECT_EQ(AppStartOccurred::HOT,
260 data.bucket_info(2).atom(1).app_start_occurred().type());
261 EXPECT_EQ("activity_name6",
262 data.bucket_info(2).atom(1).app_start_occurred().activity_name());
263 EXPECT_EQ(106L,
264 data.bucket_info(2).atom(1).app_start_occurred().activity_start_millis());
265 } else {
266 ASSERT_EQ(1, data.bucket_info(0).atom_size());
267 ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
268 EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
269 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
270 data.bucket_info(0).end_bucket_elapsed_nanos());
271 EXPECT_EQ(AppStartOccurred::HOT,
272 data.bucket_info(0).atom(0).app_start_occurred().type());
273 EXPECT_EQ("activity_name2",
274 data.bucket_info(0).atom(0).app_start_occurred().activity_name());
275 EXPECT_EQ(102L,
276 data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
277
278 ASSERT_EQ(1, data.bucket_info(1).atom_size());
279 ASSERT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
280 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
281 data.bucket_info(1).start_bucket_elapsed_nanos());
282 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
283 data.bucket_info(1).end_bucket_elapsed_nanos());
284 EXPECT_EQ(AppStartOccurred::WARM,
285 data.bucket_info(1).atom(0).app_start_occurred().type());
286 EXPECT_EQ("activity_name4",
287 data.bucket_info(1).atom(0).app_start_occurred().activity_name());
288 EXPECT_EQ(104L,
289 data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
290
291 ASSERT_EQ(1, data.bucket_info(2).atom_size());
292 ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
293 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
294 data.bucket_info(2).start_bucket_elapsed_nanos());
295 EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
296 data.bucket_info(2).end_bucket_elapsed_nanos());
297 EXPECT_EQ(AppStartOccurred::COLD,
298 data.bucket_info(2).atom(0).app_start_occurred().type());
299 EXPECT_EQ("activity_name5",
300 data.bucket_info(2).atom(0).app_start_occurred().activity_name());
301 EXPECT_EQ(105L,
302 data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis());
303 }
304
305 data = gaugeMetrics.data(1);
306
307 EXPECT_EQ(data.dimensions_in_what().field(), util::APP_START_OCCURRED);
308 ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
309 EXPECT_EQ(1 /* uid field */,
310 data.dimensions_in_what().value_tuple().dimensions_value(0).field());
311 EXPECT_EQ(appUid2, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
312 ASSERT_EQ(1, data.bucket_info_size());
313 ASSERT_EQ(1, data.bucket_info(0).atom_size());
314 ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
315 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
316 data.bucket_info(0).start_bucket_elapsed_nanos());
317 EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
318 data.bucket_info(0).end_bucket_elapsed_nanos());
319 EXPECT_EQ(AppStartOccurred::COLD, data.bucket_info(0).atom(0).app_start_occurred().type());
320 EXPECT_EQ("activity_name7",
321 data.bucket_info(0).atom(0).app_start_occurred().activity_name());
322 EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
323 }
324 }
325
TEST_F_GUARDED(GaugeMetricE2ePushedTest,TestRepeatedFieldsForPushedEvent,__ANDROID_API_T__)326 TEST_F_GUARDED(GaugeMetricE2ePushedTest, TestRepeatedFieldsForPushedEvent, __ANDROID_API_T__) {
327 for (const auto& sampling_type :
328 {GaugeMetric::FIRST_N_SAMPLES, GaugeMetric::RANDOM_ONE_SAMPLE}) {
329 StatsdConfig config = CreateStatsdConfigForRepeatedFieldsPushedEvent(sampling_type);
330 int64_t bucketStartTimeNs = 10000000000;
331 int64_t bucketSizeNs =
332 TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
333
334 ConfigKey cfgKey;
335 sp<StatsLogProcessor> processor =
336 CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
337
338 std::vector<std::unique_ptr<LogEvent>> events;
339
340 vector<int> intArray = {3, 6};
341 vector<int64_t> longArray = {1000L, 10002L};
342 vector<float> floatArray = {0.3f, 0.09f};
343 vector<string> stringArray = {"str1", "str2"};
344 int boolArrayLength = 2;
345 bool boolArray[boolArrayLength];
346 boolArray[0] = 1;
347 boolArray[1] = 0;
348 vector<uint8_t> boolArrayVector = {1, 0};
349 vector<int> enumArray = {TestAtomReported::ON, TestAtomReported::OFF};
350
351 events.push_back(CreateTestAtomReportedEventVariableRepeatedFields(
352 bucketStartTimeNs + 10 * NS_PER_SEC, intArray, longArray, floatArray, stringArray,
353 boolArray, boolArrayLength, enumArray));
354 events.push_back(CreateTestAtomReportedEventVariableRepeatedFields(
355 bucketStartTimeNs + 20 * NS_PER_SEC, {}, {}, {}, {}, {}, 0, {}));
356
357 for (const auto& event : events) {
358 processor->OnLogEvent(event.get());
359 }
360
361 ConfigMetricsReportList reports;
362 vector<uint8_t> buffer;
363 processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true, ADB_DUMP,
364 FAST, &buffer);
365 EXPECT_TRUE(buffer.size() > 0);
366 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
367 backfillDimensionPath(&reports);
368 backfillStringInReport(&reports);
369 backfillStartEndTimestamp(&reports);
370 backfillAggregatedAtoms(&reports);
371
372 ASSERT_EQ(1, reports.reports_size());
373 ASSERT_EQ(1, reports.reports(0).metrics_size());
374 StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
375 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(),
376 &gaugeMetrics);
377 ASSERT_EQ(1, gaugeMetrics.data_size());
378
379 GaugeMetricData data = gaugeMetrics.data(0);
380 ASSERT_EQ(1, data.bucket_info_size());
381 if (sampling_type == GaugeMetric::FIRST_N_SAMPLES) {
382 EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
383 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
384 data.bucket_info(0).end_bucket_elapsed_nanos());
385 ASSERT_EQ(2, data.bucket_info(0).atom_size());
386
387 TestAtomReported atom = data.bucket_info(0).atom(0).test_atom_reported();
388 EXPECT_THAT(atom.repeated_int_field(), ElementsAreArray({3}));
389 EXPECT_THAT(atom.repeated_long_field(), ElementsAreArray({10002L}));
390 EXPECT_THAT(atom.repeated_float_field(), ElementsAreArray(floatArray));
391 EXPECT_THAT(atom.repeated_string_field(), ElementsAreArray({"str1"}));
392 EXPECT_THAT(atom.repeated_boolean_field(), ElementsAreArray({0}));
393 EXPECT_THAT(atom.repeated_enum_field(), ElementsAreArray(enumArray));
394
395 atom = data.bucket_info(0).atom(1).test_atom_reported();
396 EXPECT_EQ(atom.repeated_int_field_size(), 0);
397 EXPECT_EQ(atom.repeated_long_field_size(), 0);
398 EXPECT_EQ(atom.repeated_float_field_size(), 0);
399 EXPECT_EQ(atom.repeated_string_field_size(), 0);
400 EXPECT_EQ(atom.repeated_boolean_field_size(), 0);
401 EXPECT_EQ(atom.repeated_enum_field_size(), 0);
402 } else {
403 EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
404 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
405 data.bucket_info(0).end_bucket_elapsed_nanos());
406 ASSERT_EQ(1, data.bucket_info(0).atom_size());
407
408 TestAtomReported atom = data.bucket_info(0).atom(0).test_atom_reported();
409 EXPECT_THAT(atom.repeated_int_field(), ElementsAreArray({3}));
410 EXPECT_THAT(atom.repeated_long_field(), ElementsAreArray({10002L}));
411 EXPECT_THAT(atom.repeated_float_field(), ElementsAreArray(floatArray));
412 EXPECT_THAT(atom.repeated_string_field(), ElementsAreArray({"str1"}));
413 EXPECT_THAT(atom.repeated_boolean_field(), ElementsAreArray({0}));
414 EXPECT_THAT(atom.repeated_enum_field(), ElementsAreArray(enumArray));
415 }
416 }
417 }
418
TEST_F(GaugeMetricE2ePushedTest,TestDimensionalSampling)419 TEST_F(GaugeMetricE2ePushedTest, TestDimensionalSampling) {
420 ShardOffsetProvider::getInstance().setShardOffset(5);
421
422 StatsdConfig config;
423
424 AtomMatcher appCrashMatcher =
425 CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
426 *config.add_atom_matcher() = appCrashMatcher;
427
428 GaugeMetric sampledGaugeMetric =
429 createGaugeMetric("GaugeSampledAppCrashesPerUid", appCrashMatcher.id(),
430 GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt);
431 *sampledGaugeMetric.mutable_dimensions_in_what() =
432 CreateDimensions(util::APP_CRASH_OCCURRED, {1 /* uid */});
433 *sampledGaugeMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
434 CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/});
435 sampledGaugeMetric.mutable_dimensional_sampling_info()->set_shard_count(2);
436 *config.add_gauge_metric() = sampledGaugeMetric;
437
438 const int64_t configAddedTimeNs = 10 * NS_PER_SEC; // 0:10
439 const int64_t bucketSizeNs =
440 TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000LL * 1000LL;
441
442 int uid = 12345;
443 int64_t cfgId = 98765;
444 ConfigKey cfgKey(uid, cfgId);
445
446 sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
447 configAddedTimeNs, configAddedTimeNs, config, cfgKey, nullptr, 0, new UidMap());
448
449 int appUid1 = 1001; // odd hash value
450 int appUid2 = 1002; // even hash value
451 int appUid3 = 1003; // odd hash value
452
453 const int64_t gaugeEventTimeNs1 = configAddedTimeNs + 20 * NS_PER_SEC;
454 const int64_t gaugeEventTimeNs2 = configAddedTimeNs + 40 * NS_PER_SEC;
455 const int64_t gaugeEventTimeNs3 = configAddedTimeNs + 60 * NS_PER_SEC;
456 const int64_t gaugeEventTimeNs4 = configAddedTimeNs + 100 * NS_PER_SEC;
457 const int64_t gaugeEventTimeNs5 = configAddedTimeNs + 110 * NS_PER_SEC;
458 const int64_t gaugeEventTimeNs6 = configAddedTimeNs + 150 * NS_PER_SEC;
459
460 std::vector<std::unique_ptr<LogEvent>> events;
461 events.push_back(CreateAppCrashOccurredEvent(gaugeEventTimeNs1, appUid1)); // 0:30
462 events.push_back(CreateAppCrashOccurredEvent(gaugeEventTimeNs2, appUid2)); // 0:50
463 events.push_back(CreateAppCrashOccurredEvent(gaugeEventTimeNs3, appUid3)); // 1:10
464 events.push_back(CreateAppCrashOccurredEvent(gaugeEventTimeNs4, appUid1)); // 1:50
465 events.push_back(CreateAppCrashOccurredEvent(gaugeEventTimeNs5, appUid2)); // 2:00
466 events.push_back(CreateAppCrashOccurredEvent(gaugeEventTimeNs6, appUid3)); // 2:40
467
468 // Send log events to StatsLogProcessor.
469 for (auto& event : events) {
470 processor->OnLogEvent(event.get());
471 }
472
473 ConfigMetricsReportList reports;
474 vector<uint8_t> buffer;
475 processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
476 FAST, &buffer);
477 EXPECT_TRUE(buffer.size() > 0);
478 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
479 backfillDimensionPath(&reports);
480 backfillStringInReport(&reports);
481 backfillStartEndTimestamp(&reports);
482 backfillAggregatedAtoms(&reports);
483
484 ASSERT_EQ(1, reports.reports_size());
485 ASSERT_EQ(1, reports.reports(0).metrics_size());
486 EXPECT_TRUE(reports.reports(0).metrics(0).has_gauge_metrics());
487 StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
488 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
489 ASSERT_EQ(2, gaugeMetrics.data_size());
490
491 // Only Uid 1 and 3 are logged. (odd hash value) + (offset of 5) % (shard count of 2) = 0
492 GaugeMetricData data = gaugeMetrics.data(0);
493 ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, appUid1);
494 ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
495 configAddedTimeNs + bucketSizeNs,
496 {gaugeEventTimeNs1, gaugeEventTimeNs4});
497
498 data = gaugeMetrics.data(1);
499 ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, appUid3);
500 ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
501 configAddedTimeNs + bucketSizeNs,
502 {gaugeEventTimeNs3, gaugeEventTimeNs6});
503 }
504
TEST_F(GaugeMetricE2ePushedTest,TestPushedGaugeMetricSampling)505 TEST_F(GaugeMetricE2ePushedTest, TestPushedGaugeMetricSampling) {
506 // Initiating StatsdStats at the start of this test, so it doesn't call rand() during the test.
507 StatsdStats::getInstance();
508 // Set srand seed to make rand deterministic for testing.
509 srand(0);
510
511 StatsdConfig config;
512 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
513
514 AtomMatcher appCrashMatcher =
515 CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
516 *config.add_atom_matcher() = appCrashMatcher;
517
518 GaugeMetric sampledGaugeMetric =
519 createGaugeMetric("GaugeSampledAppCrashesPerUid", appCrashMatcher.id(),
520 GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt);
521 *sampledGaugeMetric.mutable_dimensions_in_what() =
522 CreateDimensions(util::APP_CRASH_OCCURRED, {1 /* uid */});
523 sampledGaugeMetric.set_sampling_percentage(50);
524 *config.add_gauge_metric() = sampledGaugeMetric;
525
526 const int64_t configAddedTimeNs = 10 * NS_PER_SEC; // 0:10
527 const int64_t bucketSizeNs =
528 TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000LL * 1000LL;
529
530 int uid = 12345;
531 int64_t cfgId = 98765;
532 ConfigKey cfgKey(uid, cfgId);
533
534 sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
535 configAddedTimeNs, configAddedTimeNs, config, cfgKey, nullptr, 0, new UidMap());
536
537 std::vector<std::unique_ptr<LogEvent>> events;
538 for (int i = 0; i < 10; i++) {
539 events.push_back(
540 CreateAppCrashOccurredEvent(configAddedTimeNs + (10 * i * NS_PER_SEC), 1000 + i));
541 }
542
543 // Send log events to StatsLogProcessor.
544 for (auto& event : events) {
545 processor->OnLogEvent(event.get());
546 }
547
548 ConfigMetricsReportList reports;
549 vector<uint8_t> buffer;
550 processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
551 FAST, &buffer);
552 EXPECT_TRUE(buffer.size() > 0);
553 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
554 backfillDimensionPath(&reports);
555 backfillStringInReport(&reports);
556 backfillStartEndTimestamp(&reports);
557 backfillAggregatedAtoms(&reports);
558
559 ASSERT_EQ(1, reports.reports_size());
560 ASSERT_EQ(1, reports.reports(0).metrics_size());
561 EXPECT_TRUE(reports.reports(0).metrics(0).has_gauge_metrics());
562 StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
563 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
564 ASSERT_EQ(5, gaugeMetrics.data_size());
565
566 GaugeMetricData data = gaugeMetrics.data(0);
567 ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1000);
568 ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
569 configAddedTimeNs + bucketSizeNs, {configAddedTimeNs});
570
571 data = gaugeMetrics.data(1);
572 ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1002);
573 ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
574 configAddedTimeNs + bucketSizeNs,
575 {configAddedTimeNs + (10 * 2 * NS_PER_SEC)});
576
577 data = gaugeMetrics.data(2);
578 ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1003);
579 ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
580 configAddedTimeNs + bucketSizeNs,
581 {configAddedTimeNs + (10 * 3 * NS_PER_SEC)});
582
583 data = gaugeMetrics.data(3);
584 ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1007);
585 ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
586 configAddedTimeNs + bucketSizeNs,
587 {configAddedTimeNs + (10 * 7 * NS_PER_SEC)});
588
589 data = gaugeMetrics.data(4);
590 ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1009);
591 ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
592 configAddedTimeNs + bucketSizeNs,
593 {configAddedTimeNs + (10 * 9 * NS_PER_SEC)});
594 }
595
TEST_F(GaugeMetricE2ePushedTest,TestPushedGaugeMetricSamplingWithDimensionalSampling)596 TEST_F(GaugeMetricE2ePushedTest, TestPushedGaugeMetricSamplingWithDimensionalSampling) {
597 ShardOffsetProvider::getInstance().setShardOffset(5);
598 // Initiating StatsdStats at the start of this test, so it doesn't call rand() during the test.
599 StatsdStats::getInstance();
600 // Set srand seed to make rand deterministic for testing.
601 srand(0);
602
603 StatsdConfig config;
604 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
605
606 AtomMatcher appCrashMatcher =
607 CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
608 *config.add_atom_matcher() = appCrashMatcher;
609
610 GaugeMetric sampledGaugeMetric =
611 createGaugeMetric("GaugeSampledAppCrashesPerUid", appCrashMatcher.id(),
612 GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt);
613 *sampledGaugeMetric.mutable_dimensions_in_what() =
614 CreateDimensions(util::APP_CRASH_OCCURRED, {1 /* uid */});
615 *sampledGaugeMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
616 CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/});
617 sampledGaugeMetric.mutable_dimensional_sampling_info()->set_shard_count(2);
618 sampledGaugeMetric.set_sampling_percentage(50);
619 *config.add_gauge_metric() = sampledGaugeMetric;
620
621 const int64_t configAddedTimeNs = 10 * NS_PER_SEC; // 0:10
622 const int64_t bucketSizeNs =
623 TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000LL * 1000LL;
624
625 int uid = 12345;
626 int64_t cfgId = 98765;
627 ConfigKey cfgKey(uid, cfgId);
628
629 sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
630 configAddedTimeNs, configAddedTimeNs, config, cfgKey, nullptr, 0, new UidMap());
631
632 std::vector<std::unique_ptr<LogEvent>> events;
633 for (int i = 0; i < 30; i++) {
634 // Generate events with three different app uids: 1001, 1002, 1003.
635 events.push_back(CreateAppCrashOccurredEvent(configAddedTimeNs + (10 * i * NS_PER_SEC),
636 1001 + (i % 3)));
637 }
638
639 // Send log events to StatsLogProcessor.
640 for (auto& event : events) {
641 processor->OnLogEvent(event.get());
642 }
643
644 ConfigMetricsReportList reports;
645 vector<uint8_t> buffer;
646 processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
647 FAST, &buffer);
648 EXPECT_TRUE(buffer.size() > 0);
649 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
650 backfillDimensionPath(&reports);
651 backfillStringInReport(&reports);
652 backfillStartEndTimestamp(&reports);
653 backfillAggregatedAtoms(&reports);
654
655 ASSERT_EQ(1, reports.reports_size());
656 ASSERT_EQ(1, reports.reports(0).metrics_size());
657 EXPECT_TRUE(reports.reports(0).metrics(0).has_gauge_metrics());
658 StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
659 sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
660 ASSERT_EQ(2, gaugeMetrics.data_size());
661
662 // Only Uid 1 and 3 are logged. (odd hash value) + (offset of 5) % (shard count of 2) = 0
663 GaugeMetricData data = gaugeMetrics.data(0);
664 ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1001);
665 ValidateGaugeBucketTimes(
666 data.bucket_info(0), configAddedTimeNs, configAddedTimeNs + bucketSizeNs,
667 {10 * NS_PER_SEC, 40 * NS_PER_SEC, 220 * NS_PER_SEC, 280 * NS_PER_SEC});
668
669 data = gaugeMetrics.data(1);
670 ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1003);
671 ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
672 configAddedTimeNs + bucketSizeNs,
673 {60 * NS_PER_SEC, 120 * NS_PER_SEC, 150 * NS_PER_SEC, 180 * NS_PER_SEC,
674 210 * NS_PER_SEC, 300 * NS_PER_SEC});
675 }
676
677 #else
678 GTEST_LOG_(INFO) << "This test does nothing.\n";
679 #endif
680
681 } // namespace statsd
682 } // namespace os
683 } // namespace android
684