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