1 // Copyright (C) 2021 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 "src/metrics/KllMetricProducer.h"
16
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 #include <math.h>
20 #include <stdio.h>
21
22 #include <vector>
23
24 #include "metrics_test_helper.h"
25 #include "src/FieldValue.h"
26 #include "src/matchers/SimpleAtomMatchingTracker.h"
27 #include "src/metrics/MetricProducer.h"
28 #include "src/stats_log_util.h"
29 #include "tests/statsd_test_util.h"
30
31 using namespace testing;
32 using android::sp;
33 using dist_proc::aggregation::KllQuantile;
34 using std::make_shared;
35 using std::optional;
36 using std::set;
37 using std::shared_ptr;
38 using std::unique_ptr;
39 using std::unordered_map;
40 using std::vector;
41
42 #ifdef __ANDROID__
43
44 namespace android {
45 namespace os {
46 namespace statsd {
47
48 namespace {
49
50 const ConfigKey kConfigKey(0, 12345);
51 const int atomId = 1;
52 const int64_t metricId = 123;
53 const uint64_t protoHash = 0x1234567890;
54 const int logEventMatcherIndex = 0;
55 const int64_t bucketStartTimeNs = 10000000000;
56 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
57 const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
58 const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
59 const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
60 const int64_t bucket5StartTimeNs = bucketStartTimeNs + 4 * bucketSizeNs;
61 const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs;
62
assertPastBucketsSingleKey(const std::unordered_map<MetricDimensionKey,std::vector<PastBucket<unique_ptr<KllQuantile>>>> & mPastBuckets,const std::initializer_list<int> & expectedKllCountsList,const std::initializer_list<int64_t> & expectedDurationNsList,const std::initializer_list<int64_t> & expectedStartTimeNsList,const std::initializer_list<int64_t> & expectedEndTimeNsList)63 static void assertPastBucketsSingleKey(
64 const std::unordered_map<MetricDimensionKey,
65 std::vector<PastBucket<unique_ptr<KllQuantile>>>>& mPastBuckets,
66 const std::initializer_list<int>& expectedKllCountsList,
67 const std::initializer_list<int64_t>& expectedDurationNsList,
68 const std::initializer_list<int64_t>& expectedStartTimeNsList,
69 const std::initializer_list<int64_t>& expectedEndTimeNsList) {
70 vector<int> expectedKllCounts(expectedKllCountsList);
71 vector<int64_t> expectedDurationNs(expectedDurationNsList);
72 vector<int64_t> expectedStartTimeNs(expectedStartTimeNsList);
73 vector<int64_t> expectedEndTimeNs(expectedEndTimeNsList);
74
75 ASSERT_EQ(expectedKllCounts.size(), expectedDurationNs.size());
76 ASSERT_EQ(expectedKllCounts.size(), expectedStartTimeNs.size());
77 ASSERT_EQ(expectedKllCounts.size(), expectedEndTimeNs.size());
78
79 if (expectedKllCounts.size() == 0) {
80 ASSERT_EQ(0, mPastBuckets.size());
81 return;
82 }
83
84 ASSERT_EQ(1, mPastBuckets.size());
85 const vector<PastBucket<unique_ptr<KllQuantile>>>& buckets = mPastBuckets.begin()->second;
86 ASSERT_EQ(expectedKllCounts.size(), buckets.size());
87
88 for (int i = 0; i < expectedKllCounts.size(); i++) {
89 EXPECT_EQ(expectedKllCounts[i], buckets[i].aggregates[0]->num_values())
90 << "Number of entries in KLL sketch differ at index " << i;
91 EXPECT_EQ(expectedDurationNs[i], buckets[i].mConditionTrueNs)
92 << "Condition duration value differ at index " << i;
93 EXPECT_EQ(expectedStartTimeNs[i], buckets[i].mBucketStartNs)
94 << "Start time differs at index " << i;
95 EXPECT_EQ(expectedEndTimeNs[i], buckets[i].mBucketEndNs)
96 << "End time differs at index " << i;
97 }
98 }
99
onDumpReport(sp<KllMetricProducer> & producer,int64_t dumpTimeNs,bool includeCurrentBucket)100 StatsLogReport onDumpReport(sp<KllMetricProducer>& producer, int64_t dumpTimeNs,
101 bool includeCurrentBucket) {
102 ProtoOutputStream output;
103 set<int32_t> usedUids;
104 producer->onDumpReport(dumpTimeNs, includeCurrentBucket, true /*erase data*/, FAST, nullptr,
105 usedUids, &output);
106 return outputStreamToProto(&output);
107 }
108
109 } // anonymous namespace
110
111 class KllMetricProducerTestHelper {
112 public:
createKllProducerNoConditions(const KllMetric & metric)113 static sp<KllMetricProducer> createKllProducerNoConditions(const KllMetric& metric) {
114 return createKllProducer(metric);
115 }
116
createKllProducerWithCondition(const KllMetric & metric,const ConditionState & initialCondition)117 static sp<KllMetricProducer> createKllProducerWithCondition(
118 const KllMetric& metric, const ConditionState& initialCondition) {
119 return createKllProducer(metric, initialCondition);
120 }
121
createKllProducer(const KllMetric & metric,optional<ConditionState> initialCondition=nullopt,vector<int32_t> slicedStateAtoms={},unordered_map<int,unordered_map<int,int64_t>> stateGroupMap={},const int64_t timeBaseNs=bucketStartTimeNs,const int64_t startTimeNs=bucketStartTimeNs)122 static sp<KllMetricProducer> createKllProducer(
123 const KllMetric& metric, optional<ConditionState> initialCondition = nullopt,
124 vector<int32_t> slicedStateAtoms = {},
125 unordered_map<int, unordered_map<int, int64_t>> stateGroupMap = {},
126 const int64_t timeBaseNs = bucketStartTimeNs,
127 const int64_t startTimeNs = bucketStartTimeNs) {
128 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
129 const int64_t bucketSizeNs = MillisToNano(
130 TimeUnitToBucketSizeInMillisGuardrailed(kConfigKey.GetUid(), metric.bucket()));
131 const bool containsAnyPositionInDimensionsInWhat =
132 HasPositionANY(metric.dimensions_in_what());
133 const bool shouldUseNestedDimensions =
134 ShouldUseNestedDimensions(metric.dimensions_in_what());
135
136 vector<Matcher> fieldMatchers;
137 translateFieldMatcher(metric.kll_field(), &fieldMatchers);
138
139 const auto [dimensionSoftLimit, dimensionHardLimit] =
140 StatsdStats::getAtomDimensionKeySizeLimits(
141 atomId, StatsdStats::kDimensionKeySizeHardLimitMin);
142
143 int conditionIndex = initialCondition ? 0 : -1;
144 vector<ConditionState> initialConditionCache;
145 if (initialCondition) {
146 initialConditionCache.push_back(initialCondition.value());
147 }
148
149 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
150 return new KllMetricProducer(
151 kConfigKey, metric, protoHash, {/*pullAtomId=*/-1, /*pullerManager=*/nullptr},
152 {timeBaseNs, startTimeNs, bucketSizeNs, metric.min_bucket_size_nanos(),
153 /*conditionCorrectionThresholdNs=*/nullopt, metric.split_bucket_for_app_upgrade()},
154 {containsAnyPositionInDimensionsInWhat, shouldUseNestedDimensions,
155 logEventMatcherIndex,
156 /*eventMatcherWizard=*/nullptr, metric.dimensions_in_what(), fieldMatchers},
157 {conditionIndex, metric.links(), initialConditionCache, wizard},
158 {metric.state_link(), slicedStateAtoms, stateGroupMap},
159 {/*eventActivationMap=*/{}, /*eventDeactivationMap=*/{}},
160 {dimensionSoftLimit, dimensionHardLimit}, provider);
161 }
162
createMetric()163 static KllMetric createMetric() {
164 KllMetric metric;
165 metric.set_id(metricId);
166 metric.set_bucket(ONE_MINUTE);
167 metric.mutable_kll_field()->set_field(atomId);
168 metric.mutable_kll_field()->add_child()->set_field(2);
169 metric.set_split_bucket_for_app_upgrade(true);
170 return metric;
171 }
172
createMetricWithCondition()173 static KllMetric createMetricWithCondition() {
174 KllMetric metric = KllMetricProducerTestHelper::createMetric();
175 metric.set_condition(StringToId("SCREEN_ON"));
176 return metric;
177 }
178 };
179
180 // Setup for parameterized tests.
181 class KllMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
182
183 INSTANTIATE_TEST_SUITE_P(KllMetricProducerTest_PartialBucket, KllMetricProducerTest_PartialBucket,
184 testing::Values(APP_UPGRADE, BOOT_COMPLETE));
185
TEST_P(KllMetricProducerTest_PartialBucket,TestPushedEventsMultipleBuckets)186 TEST_P(KllMetricProducerTest_PartialBucket, TestPushedEventsMultipleBuckets) {
187 const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
188 sp<KllMetricProducer> kllProducer =
189 KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
190
191 LogEvent event1(/*uid=*/0, /*pid=*/0);
192 CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
193 kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
194 ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
195
196 const int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 150;
197 switch (GetParam()) {
198 case APP_UPGRADE:
199 kllProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
200 break;
201 case BOOT_COMPLETE:
202 kllProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
203 break;
204 }
205 TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {1},
206 {partialBucketSplitTimeNs - bucketStartTimeNs}, {bucketStartTimeNs},
207 {partialBucketSplitTimeNs});
208 EXPECT_EQ(partialBucketSplitTimeNs, kllProducer->mCurrentBucketStartTimeNs);
209 EXPECT_EQ(0, kllProducer->getCurrentBucketNum());
210
211 // Event arrives after the bucket split.
212 LogEvent event2(/*uid=*/0, /*pid=*/0);
213 CreateRepeatedValueLogEvent(&event2, atomId, bucketStartTimeNs + 59 * NS_PER_SEC, 20);
214 kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
215
216 TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {1},
217 {partialBucketSplitTimeNs - bucketStartTimeNs}, {bucketStartTimeNs},
218 {partialBucketSplitTimeNs});
219 EXPECT_EQ(partialBucketSplitTimeNs, kllProducer->mCurrentBucketStartTimeNs);
220 EXPECT_EQ(0, kllProducer->getCurrentBucketNum());
221
222 // Next value should create a new bucket.
223 LogEvent event3(/*uid=*/0, /*pid=*/0);
224 CreateRepeatedValueLogEvent(&event3, atomId, bucket2StartTimeNs + 5 * NS_PER_SEC, 10);
225 kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
226 TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {1, 1},
227 {partialBucketSplitTimeNs - bucketStartTimeNs,
228 bucket2StartTimeNs - partialBucketSplitTimeNs},
229 {bucketStartTimeNs, partialBucketSplitTimeNs},
230 {partialBucketSplitTimeNs, bucket2StartTimeNs});
231 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, kllProducer->mCurrentBucketStartTimeNs);
232 EXPECT_EQ(1, kllProducer->getCurrentBucketNum());
233 }
234
TEST(KllMetricProducerTest,TestPushedEventsWithoutCondition)235 TEST(KllMetricProducerTest, TestPushedEventsWithoutCondition) {
236 const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
237 sp<KllMetricProducer> kllProducer =
238 KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
239
240 LogEvent event1(/*uid=*/0, /*pid=*/0);
241 CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
242
243 LogEvent event2(/*uid=*/0, /*pid=*/0);
244 CreateRepeatedValueLogEvent(&event2, atomId, bucketStartTimeNs + 20, 20);
245
246 kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
247 // has one slice
248 ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
249 const KllMetricProducer::Interval& curInterval0 =
250 kllProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
251 EXPECT_EQ(1, curInterval0.aggregate->num_values());
252 EXPECT_GT(curInterval0.sampleSize, 0);
253
254 kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
255
256 // has one slice
257 ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
258 EXPECT_EQ(2, curInterval0.aggregate->num_values());
259
260 kllProducer->flushIfNeededLocked(bucket2StartTimeNs);
261 TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {2}, {bucketSizeNs},
262 {bucketStartTimeNs}, {bucket2StartTimeNs});
263 }
264
TEST(KllMetricProducerTest,TestPushedEventsWithCondition)265 TEST(KllMetricProducerTest, TestPushedEventsWithCondition) {
266 const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
267 sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
268 metric, ConditionState::kFalse);
269
270 LogEvent event1(/*uid=*/0, /*pid=*/0);
271 CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
272 kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
273 // Has 0 slices.
274 ASSERT_EQ(0UL, kllProducer->mCurrentSlicedBucket.size());
275
276 kllProducer->onConditionChangedLocked(true, bucketStartTimeNs + 15);
277
278 LogEvent event2(/*uid=*/0, /*pid=*/0);
279 CreateRepeatedValueLogEvent(&event2, atomId, bucketStartTimeNs + 20, 20);
280 kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
281
282 // has one slice
283 ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
284 const KllMetricProducer::Interval& curInterval0 =
285 kllProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
286 EXPECT_EQ(1, curInterval0.aggregate->num_values());
287
288 LogEvent event3(/*uid=*/0, /*pid=*/0);
289 CreateRepeatedValueLogEvent(&event3, atomId, bucketStartTimeNs + 30, 30);
290 kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
291
292 // has one slice
293 ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
294 EXPECT_EQ(2, curInterval0.aggregate->num_values());
295
296 kllProducer->onConditionChangedLocked(false, bucketStartTimeNs + 35);
297
298 LogEvent event4(/*uid=*/0, /*pid=*/0);
299 CreateRepeatedValueLogEvent(&event4, atomId, bucketStartTimeNs + 40, 40);
300 kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event4);
301
302 // has one slice
303 ASSERT_EQ(1UL, kllProducer->mCurrentSlicedBucket.size());
304 EXPECT_EQ(2, curInterval0.aggregate->num_values());
305
306 kllProducer->flushIfNeededLocked(bucket2StartTimeNs);
307 TRACE_CALL(assertPastBucketsSingleKey, kllProducer->mPastBuckets, {2}, {20},
308 {bucketStartTimeNs}, {bucket2StartTimeNs});
309 }
310
311 /*
312 * Test that CONDITION_UNKNOWN dump reason is logged due to an unknown condition
313 * when a metric is initialized.
314 */
TEST(KllMetricProducerTest_BucketDrop,TestInvalidBucketWhenConditionUnknown)315 TEST(KllMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) {
316 const KllMetric& metric = KllMetricProducerTestHelper::createMetricWithCondition();
317 sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
318 metric, ConditionState::kUnknown);
319
320 // Condition change event.
321 kllProducer->onConditionChanged(true, bucketStartTimeNs + 50);
322
323 // Check dump report.
324 int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
325 StatsLogReport report =
326 onDumpReport(kllProducer, dumpReportTimeNs, true /* include current bucket */);
327 EXPECT_TRUE(report.has_kll_metrics());
328 ASSERT_EQ(0, report.kll_metrics().data_size());
329 ASSERT_EQ(1, report.kll_metrics().skipped_size());
330
331 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
332 report.kll_metrics().skipped(0).start_bucket_elapsed_millis());
333 EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
334 report.kll_metrics().skipped(0).end_bucket_elapsed_millis());
335 ASSERT_EQ(1, report.kll_metrics().skipped(0).drop_event_size());
336
337 auto dropEvent = report.kll_metrics().skipped(0).drop_event(0);
338 EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
339 EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
340 }
341
342 /*
343 * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
344 * is smaller than the "min_bucket_size_nanos" specified in the metric config.
345 */
TEST(KllMetricProducerTest_BucketDrop,TestBucketDropWhenBucketTooSmall)346 TEST(KllMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
347 KllMetric metric = KllMetricProducerTestHelper::createMetric();
348 metric.set_min_bucket_size_nanos(10 * NS_PER_SEC); // 10 seconds
349
350 sp<KllMetricProducer> kllProducer =
351 KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
352
353 LogEvent event1(/*uid=*/0, /*pid=*/0);
354 CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
355 kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
356
357 // Check dump report.
358 int64_t dumpReportTimeNs = bucketStartTimeNs + 9000000;
359 StatsLogReport report =
360 onDumpReport(kllProducer, dumpReportTimeNs, true /* include current bucket */);
361 EXPECT_TRUE(report.has_kll_metrics());
362 ASSERT_EQ(0, report.kll_metrics().data_size());
363 ASSERT_EQ(1, report.kll_metrics().skipped_size());
364
365 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
366 report.kll_metrics().skipped(0).start_bucket_elapsed_millis());
367 EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
368 report.kll_metrics().skipped(0).end_bucket_elapsed_millis());
369 ASSERT_EQ(1, report.kll_metrics().skipped(0).drop_event_size());
370
371 auto dropEvent = report.kll_metrics().skipped(0).drop_event(0);
372 EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
373 EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
374 }
375
376 /*
377 * Test that NO_DATA dump reason is logged when a flushed bucket contains no data.
378 */
TEST(KllMetricProducerTest_BucketDrop,TestBucketDropWhenDataUnavailable)379 TEST(KllMetricProducerTest_BucketDrop, TestBucketDropWhenDataUnavailable) {
380 const KllMetric& metric = KllMetricProducerTestHelper::createMetricWithCondition();
381
382 sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
383 metric, ConditionState::kFalse);
384
385 // Check dump report.
386 int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds
387 StatsLogReport report =
388 onDumpReport(kllProducer, dumpReportTimeNs, true /* include current bucket */);
389 EXPECT_TRUE(report.has_kll_metrics());
390 ASSERT_EQ(0, report.kll_metrics().data_size());
391 ASSERT_EQ(1, report.kll_metrics().skipped_size());
392
393 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
394 report.kll_metrics().skipped(0).start_bucket_elapsed_millis());
395 EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
396 report.kll_metrics().skipped(0).end_bucket_elapsed_millis());
397 ASSERT_EQ(1, report.kll_metrics().skipped(0).drop_event_size());
398
399 auto dropEvent = report.kll_metrics().skipped(0).drop_event(0);
400 EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
401 EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
402 }
403
404 /*
405 * Test bucket splits when condition is unknown.
406 */
TEST(KllMetricProducerTest,TestForcedBucketSplitWhenConditionUnknownSkipsBucket)407 TEST(KllMetricProducerTest, TestForcedBucketSplitWhenConditionUnknownSkipsBucket) {
408 const KllMetric& metric = KllMetricProducerTestHelper::createMetricWithCondition();
409
410 sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
411 metric, ConditionState::kUnknown);
412
413 // App update event.
414 int64_t appUpdateTimeNs = bucketStartTimeNs + 1000;
415 kllProducer->notifyAppUpgrade(appUpdateTimeNs);
416
417 // Check dump report.
418 int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds
419 StatsLogReport report =
420 onDumpReport(kllProducer, dumpReportTimeNs, false /* include current bucket */);
421 EXPECT_TRUE(report.has_kll_metrics());
422 ASSERT_EQ(0, report.kll_metrics().data_size());
423 ASSERT_EQ(1, report.kll_metrics().skipped_size());
424
425 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
426 report.kll_metrics().skipped(0).start_bucket_elapsed_millis());
427 EXPECT_EQ(NanoToMillis(appUpdateTimeNs),
428 report.kll_metrics().skipped(0).end_bucket_elapsed_millis());
429 ASSERT_EQ(1, report.kll_metrics().skipped(0).drop_event_size());
430
431 auto dropEvent = report.kll_metrics().skipped(0).drop_event(0);
432 EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
433 EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis());
434 }
435
TEST(KllMetricProducerTest,TestByteSize)436 TEST(KllMetricProducerTest, TestByteSize) {
437 const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
438 sp<KllMetricProducer> kllProducer =
439 KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
440
441 LogEvent event1(/*uid=*/0, /*pid=*/0);
442 CreateRepeatedValueLogEvent(&event1, atomId, bucketStartTimeNs + 10, 10);
443
444 LogEvent event2(/*uid=*/0, /*pid=*/0);
445 CreateRepeatedValueLogEvent(&event2, atomId, bucketStartTimeNs + 20, 20);
446
447 kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
448 kllProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
449 kllProducer->flushIfNeededLocked(bucket2StartTimeNs);
450
451 const size_t expectedSize = kllProducer->kBucketSize + 4 /* one int aggIndex entry */ +
452 16 /* two int64_t entries in KllQuantile object */;
453
454 EXPECT_EQ(expectedSize, kllProducer->byteSize());
455 }
456
TEST(KllMetricProducerTest,TestCorruptedDataReason_WhatLoss)457 TEST(KllMetricProducerTest, TestCorruptedDataReason_WhatLoss) {
458 const KllMetric& metric = KllMetricProducerTestHelper::createMetric();
459 sp<KllMetricProducer> kllProducer =
460 KllMetricProducerTestHelper::createKllProducerNoConditions(metric);
461
462 kllProducer->onMatchedLogEventLost(atomId, DATA_CORRUPTED_SOCKET_LOSS,
463 MetricProducer::LostAtomType::kWhat);
464 {
465 // Check dump report content.
466 StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 50,
467 true /* include current bucket */);
468 EXPECT_THAT(report.data_corrupted_reason(), ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
469 }
470
471 kllProducer->onMatchedLogEventLost(atomId, DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW,
472 MetricProducer::LostAtomType::kWhat);
473 {
474 // Check dump report content.
475 StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 150,
476 true /* include current bucket */);
477 EXPECT_THAT(report.data_corrupted_reason(),
478 ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW));
479 }
480
481 kllProducer->onMatchedLogEventLost(atomId, DATA_CORRUPTED_SOCKET_LOSS,
482 MetricProducer::LostAtomType::kWhat);
483 kllProducer->onMatchedLogEventLost(atomId, DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW,
484 MetricProducer::LostAtomType::kWhat);
485 {
486 // Check dump report content.
487 StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 250,
488 true /* include current bucket */);
489 EXPECT_THAT(report.data_corrupted_reason(),
490 ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW, DATA_CORRUPTED_SOCKET_LOSS));
491 }
492 }
493
TEST(KllMetricProducerTest,TestCorruptedDataReason_ConditionLoss)494 TEST(KllMetricProducerTest, TestCorruptedDataReason_ConditionLoss) {
495 const int conditionId = 10;
496
497 const KllMetric& metric = KllMetricProducerTestHelper::createMetricWithCondition();
498
499 sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
500 metric, ConditionState::kFalse);
501
502 kllProducer->onMatchedLogEventLost(conditionId, DATA_CORRUPTED_SOCKET_LOSS,
503 MetricProducer::LostAtomType::kCondition);
504 {
505 // Check dump report content.
506 StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 50,
507 true /* include current bucket */);
508 EXPECT_THAT(report.data_corrupted_reason(), ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
509 }
510
511 kllProducer->onMatchedLogEventLost(conditionId, DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW,
512 MetricProducer::LostAtomType::kCondition);
513 {
514 // Check dump report content.
515 StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 150,
516 true /* include current bucket */);
517 EXPECT_THAT(report.data_corrupted_reason(),
518 ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW, DATA_CORRUPTED_SOCKET_LOSS));
519 }
520 }
521
TEST(KllMetricProducerTest,TestCorruptedDataReason_StateLoss)522 TEST(KllMetricProducerTest, TestCorruptedDataReason_StateLoss) {
523 const int stateAtomId = 10;
524
525 const KllMetric& metric = KllMetricProducerTestHelper::createMetricWithCondition();
526
527 sp<KllMetricProducer> kllProducer = KllMetricProducerTestHelper::createKllProducerWithCondition(
528 metric, ConditionState::kFalse);
529
530 kllProducer->onStateEventLost(stateAtomId, DATA_CORRUPTED_SOCKET_LOSS);
531 {
532 // Check dump report content.
533 StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 50,
534 true /* include current bucket */);
535 EXPECT_THAT(report.data_corrupted_reason(), ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
536 }
537
538 // validation that data corruption signal remains accurate after another dump
539 {
540 // Check dump report content.
541 StatsLogReport report = onDumpReport(kllProducer, bucketStartTimeNs + 150,
542 true /* include current bucket */);
543 EXPECT_THAT(report.data_corrupted_reason(), ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
544 }
545 }
546
547 } // namespace statsd
548 } // namespace os
549 } // namespace android
550 #else
551 GTEST_LOG_(INFO) << "This test does nothing.\n";
552 #endif
553