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 "src/metrics/GaugeMetricProducer.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 "logd/LogEvent.h"
25 #include "metrics_test_helper.h"
26 #include "src/matchers/SimpleAtomMatchingTracker.h"
27 #include "src/metrics/MetricProducer.h"
28 #include "src/stats_log_util.h"
29 #include "stats_event.h"
30 #include "tests/statsd_test_util.h"
31
32 using namespace testing;
33 using android::sp;
34 using std::set;
35 using std::unordered_map;
36 using std::vector;
37 using std::make_shared;
38
39 #ifdef __ANDROID__
40
41 namespace android {
42 namespace os {
43 namespace statsd {
44
45 namespace {
46
47 const ConfigKey kConfigKey(0, 12345);
48 const int tagId = 1;
49 const int64_t metricId = 123;
50 const uint64_t protoHash = 0x123456789;
51 const int logEventMatcherIndex = 0;
52 const int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
53 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
54 const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
55 const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
56 const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
57 const int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
58
makeLogEvent(int32_t atomId,int64_t timestampNs,int32_t value1,string str1,int32_t value2)59 shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t value1, string str1,
60 int32_t value2) {
61 AStatsEvent* statsEvent = AStatsEvent_obtain();
62 AStatsEvent_setAtomId(statsEvent, atomId);
63 AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
64
65 AStatsEvent_writeInt32(statsEvent, value1);
66 AStatsEvent_writeString(statsEvent, str1.c_str());
67 AStatsEvent_writeInt32(statsEvent, value2);
68
69 shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
70 parseStatsEventToLogEvent(statsEvent, logEvent.get());
71 return logEvent;
72 }
73 } // anonymous namespace
74
75 // Setup for parameterized tests.
76 class GaugeMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
77
78 INSTANTIATE_TEST_SUITE_P(GaugeMetricProducerTest_PartialBucket,
79 GaugeMetricProducerTest_PartialBucket,
80 testing::Values(APP_UPGRADE, BOOT_COMPLETE));
81
82 /*
83 * Tests that the first bucket works correctly
84 */
TEST(GaugeMetricProducerTest,TestFirstBucket)85 TEST(GaugeMetricProducerTest, TestFirstBucket) {
86 GaugeMetric metric;
87 metric.set_id(metricId);
88 metric.set_bucket(ONE_MINUTE);
89 metric.mutable_gauge_fields_filter()->set_include_all(false);
90 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
91 gaugeFieldMatcher->set_field(tagId);
92 gaugeFieldMatcher->add_child()->set_field(1);
93 gaugeFieldMatcher->add_child()->set_field(3);
94
95 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
96
97 sp<EventMatcherWizard> eventMatcherWizard =
98 createEventMatcherWizard(tagId, logEventMatcherIndex);
99
100 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
101 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
102
103 // statsd started long ago.
104 // The metric starts in the middle of the bucket
105 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
106 wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
107 -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
108 pullerManager, provider);
109 gaugeProducer.prepareFirstBucket();
110
111 EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
112 EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum);
113 EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs());
114 }
115
TEST(GaugeMetricProducerTest,TestPulledEventsNoCondition)116 TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) {
117 GaugeMetric metric;
118 metric.set_id(metricId);
119 metric.set_bucket(ONE_MINUTE);
120 metric.mutable_gauge_fields_filter()->set_include_all(false);
121 metric.set_max_pull_delay_sec(INT_MAX);
122 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
123 gaugeFieldMatcher->set_field(tagId);
124 gaugeFieldMatcher->add_child()->set_field(1);
125 gaugeFieldMatcher->add_child()->set_field(3);
126
127 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
128
129 sp<EventMatcherWizard> eventMatcherWizard =
130 createEventMatcherWizard(tagId, logEventMatcherIndex);
131
132 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
133 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
134 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
135 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
136 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
137 vector<std::shared_ptr<LogEvent>>* data) {
138 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
139 data->clear();
140 data->push_back(makeLogEvent(tagId, eventTimeNs + 10, 3, "some value", 11));
141 return true;
142 }));
143
144 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
145
146 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
147 wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
148 tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
149 pullerManager, provider);
150 gaugeProducer.prepareFirstBucket();
151
152 vector<shared_ptr<LogEvent>> allData;
153 allData.clear();
154 allData.push_back(makeLogEvent(tagId, bucket2StartTimeNs + 1, 10, "some value", 11));
155
156 gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs);
157 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
158 auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
159 EXPECT_EQ(INT, it->mValue.getType());
160 EXPECT_EQ(10, it->mValue.int_value);
161 it++;
162 EXPECT_EQ(11, it->mValue.int_value);
163 ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
164 EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()
165 ->second.back()
166 .mAggregatedAtoms.begin()
167 ->first.getAtomFieldValues()
168 .getValues()
169 .begin()
170 ->mValue.int_value);
171
172 allData.clear();
173 allData.push_back(makeLogEvent(tagId, bucket3StartTimeNs + 10, 24, "some value", 25));
174 gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs);
175 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
176 it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
177 EXPECT_EQ(INT, it->mValue.getType());
178 EXPECT_EQ(24, it->mValue.int_value);
179 it++;
180 EXPECT_EQ(INT, it->mValue.getType());
181 EXPECT_EQ(25, it->mValue.int_value);
182 // One dimension.
183 ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
184 ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
185 auto it2 = gaugeProducer.mPastBuckets.begin()
186 ->second.back()
187 .mAggregatedAtoms.begin()
188 ->first.getAtomFieldValues()
189 .getValues()
190 .begin();
191 EXPECT_EQ(INT, it2->mValue.getType());
192 EXPECT_EQ(10L, it2->mValue.int_value);
193 it2++;
194 EXPECT_EQ(INT, it2->mValue.getType());
195 EXPECT_EQ(11L, it2->mValue.int_value);
196
197 gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs);
198 ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
199 // One dimension.
200 ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
201 ASSERT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size());
202 it2 = gaugeProducer.mPastBuckets.begin()
203 ->second.back()
204 .mAggregatedAtoms.begin()
205 ->first.getAtomFieldValues()
206 .getValues()
207 .begin();
208 EXPECT_EQ(INT, it2->mValue.getType());
209 EXPECT_EQ(24L, it2->mValue.int_value);
210 it2++;
211 EXPECT_EQ(INT, it2->mValue.getType());
212 EXPECT_EQ(25L, it2->mValue.int_value);
213 }
214
TEST_P(GaugeMetricProducerTest_PartialBucket,TestPushedEvents)215 TEST_P(GaugeMetricProducerTest_PartialBucket, TestPushedEvents) {
216 sp<AlarmMonitor> alarmMonitor;
217 GaugeMetric metric;
218 metric.set_id(metricId);
219 metric.set_bucket(ONE_MINUTE);
220 metric.mutable_gauge_fields_filter()->set_include_all(true);
221 metric.set_split_bucket_for_app_upgrade(true);
222
223 Alert alert;
224 alert.set_id(101);
225 alert.set_metric_id(metricId);
226 alert.set_trigger_if_sum_gt(25);
227 alert.set_num_buckets(100);
228 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
229 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
230
231 sp<EventMatcherWizard> eventMatcherWizard =
232 createEventMatcherWizard(tagId, logEventMatcherIndex);
233
234 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
235
236 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
237 wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
238 -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
239 bucketStartTimeNs, pullerManager, provider);
240 gaugeProducer.prepareFirstBucket();
241
242 sp<AnomalyTracker> anomalyTracker =
243 gaugeProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
244 EXPECT_TRUE(anomalyTracker != nullptr);
245
246 LogEvent event1(/*uid=*/0, /*pid=*/0);
247 CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
248 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
249 EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
250
251 switch (GetParam()) {
252 case APP_UPGRADE:
253 gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
254 break;
255 case BOOT_COMPLETE:
256 gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
257 break;
258 }
259 EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
260 ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
261 EXPECT_EQ(bucketStartTimeNs,
262 gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
263 EXPECT_EQ(partialBucketSplitTimeNs,
264 gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
265 EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
266 EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
267 // Partial buckets are not sent to anomaly tracker.
268 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
269
270 // Create an event in the same partial bucket.
271 LogEvent event2(/*uid=*/0, /*pid=*/0);
272 CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10);
273 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
274 EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
275 ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
276 EXPECT_EQ(bucketStartTimeNs,
277 gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
278 EXPECT_EQ(partialBucketSplitTimeNs,
279 gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
280 EXPECT_EQ((int64_t)partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
281 // Partial buckets are not sent to anomaly tracker.
282 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
283
284 // Next event should trigger creation of new bucket and send previous full bucket to anomaly
285 // tracker.
286 LogEvent event3(/*uid=*/0, /*pid=*/0);
287 CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10);
288 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
289 EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum);
290 ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
291 EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs);
292 EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
293
294 // Next event should trigger creation of new bucket.
295 LogEvent event4(/*uid=*/0, /*pid=*/0);
296 CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 125 * NS_PER_SEC, 1, 10);
297 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
298 EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum);
299 ASSERT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
300 EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
301 }
302
TEST_P(GaugeMetricProducerTest_PartialBucket,TestPulled)303 TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) {
304 GaugeMetric metric;
305 metric.set_id(metricId);
306 metric.set_bucket(ONE_MINUTE);
307 metric.set_max_pull_delay_sec(INT_MAX);
308 metric.set_split_bucket_for_app_upgrade(true);
309 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
310 gaugeFieldMatcher->set_field(tagId);
311 gaugeFieldMatcher->add_child()->set_field(2);
312
313 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
314
315 sp<EventMatcherWizard> eventMatcherWizard =
316 createEventMatcherWizard(tagId, logEventMatcherIndex);
317
318 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
319
320 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
321 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
322 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
323 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
324 .WillOnce(Return(false))
325 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
326 vector<std::shared_ptr<LogEvent>>* data) {
327 EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
328 data->clear();
329 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 2));
330 return true;
331 }));
332
333 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
334 wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
335 tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
336 pullerManager, provider);
337 gaugeProducer.prepareFirstBucket();
338
339 vector<shared_ptr<LogEvent>> allData;
340 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
341 gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucketStartTimeNs);
342 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
343 EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
344 ->second.front()
345 .mFields->begin()
346 ->mValue.int_value);
347
348 switch (GetParam()) {
349 case APP_UPGRADE:
350 gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
351 break;
352 case BOOT_COMPLETE:
353 gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
354 break;
355 }
356 ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
357 EXPECT_EQ(bucketStartTimeNs,
358 gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
359 EXPECT_EQ(partialBucketSplitTimeNs,
360 gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
361 EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
362 EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
363 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
364 EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin()
365 ->second.front()
366 .mFields->begin()
367 ->mValue.int_value);
368
369 allData.clear();
370 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 1, 3));
371 gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS,
372 bucketStartTimeNs + bucketSizeNs);
373 ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
374 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
375 EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin()
376 ->second.front()
377 .mFields->begin()
378 ->mValue.int_value);
379 }
380
TEST(GaugeMetricProducerTest,TestPulledWithAppUpgradeDisabled)381 TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
382 GaugeMetric metric;
383 metric.set_id(metricId);
384 metric.set_bucket(ONE_MINUTE);
385 metric.set_max_pull_delay_sec(INT_MAX);
386 metric.set_split_bucket_for_app_upgrade(false);
387 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
388 gaugeFieldMatcher->set_field(tagId);
389 gaugeFieldMatcher->add_child()->set_field(2);
390
391 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
392
393 sp<EventMatcherWizard> eventMatcherWizard =
394 createEventMatcherWizard(tagId, logEventMatcherIndex);
395
396 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
397 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
398 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
399 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
400 .WillOnce(Return(false));
401
402 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
403
404 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
405 wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
406 tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
407 pullerManager, provider);
408 gaugeProducer.prepareFirstBucket();
409
410 vector<shared_ptr<LogEvent>> allData;
411 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
412 gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucketStartTimeNs);
413 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
414 EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
415 ->second.front()
416 .mFields->begin()
417 ->mValue.int_value);
418
419 gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
420 ASSERT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
421 EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
422 EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
423 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
424 EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
425 ->second.front()
426 .mFields->begin()
427 ->mValue.int_value);
428 }
429
TEST(GaugeMetricProducerTest,TestPulledEventsWithCondition)430 TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) {
431 GaugeMetric metric;
432 metric.set_id(metricId);
433 metric.set_bucket(ONE_MINUTE);
434 metric.set_max_pull_delay_sec(INT_MAX);
435 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
436 gaugeFieldMatcher->set_field(tagId);
437 gaugeFieldMatcher->add_child()->set_field(2);
438 metric.set_condition(StringToId("SCREEN_ON"));
439
440 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
441
442 sp<EventMatcherWizard> eventMatcherWizard =
443 createEventMatcherWizard(tagId, logEventMatcherIndex);
444
445 int64_t conditionChangeNs = bucketStartTimeNs + 8;
446
447 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
448 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
449 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
450 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _))
451 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
452 vector<std::shared_ptr<LogEvent>>* data) {
453 data->clear();
454 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs + 10, 100));
455 return true;
456 }));
457
458 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
459
460 GaugeMetricProducer gaugeProducer(
461 kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard,
462 protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
463 bucketStartTimeNs, bucketStartTimeNs, pullerManager, provider);
464 gaugeProducer.prepareFirstBucket();
465
466 gaugeProducer.onConditionChanged(true, conditionChangeNs);
467 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
468 EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin()
469 ->second.front()
470 .mFields->begin()
471 ->mValue.int_value);
472 ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size());
473
474 vector<shared_ptr<LogEvent>> allData;
475 allData.clear();
476 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
477 gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs);
478
479 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
480 EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin()
481 ->second.front()
482 .mFields->begin()
483 ->mValue.int_value);
484 ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
485
486 EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()
487 ->second.back()
488 .mAggregatedAtoms.begin()
489 ->first.getAtomFieldValues()
490 .getValues()
491 .begin()
492 ->mValue.int_value);
493
494 gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10);
495 gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10);
496 ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
497 ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
498 EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()
499 ->second.back()
500 .mAggregatedAtoms.begin()
501 ->first.getAtomFieldValues()
502 .getValues()
503 .begin()
504 ->mValue.int_value);
505 }
506
TEST(GaugeMetricProducerTest,TestPulledEventsWithSlicedCondition)507 TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) {
508 const int conditionTag = 65;
509 GaugeMetric metric;
510 metric.set_id(1111111);
511 metric.set_bucket(ONE_MINUTE);
512 metric.mutable_gauge_fields_filter()->set_include_all(true);
513 metric.set_condition(StringToId("APP_DIED"));
514 metric.set_max_pull_delay_sec(INT_MAX);
515 auto dim = metric.mutable_dimensions_in_what();
516 dim->set_field(tagId);
517 dim->add_child()->set_field(1);
518
519 sp<EventMatcherWizard> eventMatcherWizard =
520 createEventMatcherWizard(tagId, logEventMatcherIndex);
521
522 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
523 EXPECT_CALL(*wizard, query(_, _, _))
524 .WillRepeatedly(
525 Invoke([](const int conditionIndex, const ConditionKey& conditionParameters,
526 const bool isPartialLink) {
527 int pos[] = {1, 0, 0};
528 Field f(conditionTag, pos, 0);
529 HashableDimensionKey key;
530 key.mutableValues()->emplace_back(f, Value((int32_t)1000000));
531
532 return ConditionState::kTrue;
533 }));
534
535 int64_t sliceConditionChangeNs = bucketStartTimeNs + 8;
536
537 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
538 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
539 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
540 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _))
541 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
542 vector<std::shared_ptr<LogEvent>>* data) {
543 data->clear();
544 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs + 10, 1000, 100));
545 return true;
546 }));
547
548 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
549
550 GaugeMetricProducer gaugeProducer(
551 kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard,
552 protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
553 bucketStartTimeNs, bucketStartTimeNs, pullerManager, provider);
554 gaugeProducer.prepareFirstBucket();
555
556 gaugeProducer.onSlicedConditionMayChange(true, sliceConditionChangeNs);
557
558 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
559 const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first;
560 ASSERT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size());
561 EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
562
563 ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size());
564
565 vector<shared_ptr<LogEvent>> allData;
566 allData.clear();
567 allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1000, 110));
568 gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs);
569
570 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
571 ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
572 }
573
TEST(GaugeMetricProducerTest,TestPulledEventsAnomalyDetection)574 TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) {
575 sp<AlarmMonitor> alarmMonitor;
576 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
577
578 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
579 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
580 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
581 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
582 .WillOnce(Return(false));
583
584 GaugeMetric metric;
585 metric.set_id(metricId);
586 metric.set_bucket(ONE_MINUTE);
587 metric.set_max_pull_delay_sec(INT_MAX);
588 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
589 gaugeFieldMatcher->set_field(tagId);
590 gaugeFieldMatcher->add_child()->set_field(2);
591
592 sp<EventMatcherWizard> eventMatcherWizard =
593 createEventMatcherWizard(tagId, logEventMatcherIndex);
594
595 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
596
597 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
598 wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
599 tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
600 pullerManager, provider);
601 gaugeProducer.prepareFirstBucket();
602
603 Alert alert;
604 alert.set_id(101);
605 alert.set_metric_id(metricId);
606 alert.set_trigger_if_sum_gt(25);
607 alert.set_num_buckets(2);
608 const int32_t refPeriodSec = 60;
609 alert.set_refractory_period_secs(refPeriodSec);
610 sp<AnomalyTracker> anomalyTracker =
611 gaugeProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
612
613 int tagId = 1;
614 vector<shared_ptr<LogEvent>> allData;
615 allData.clear();
616 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 13));
617 gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucketStartTimeNs);
618 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
619 EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()
620 ->second.front()
621 .mFields->begin()
622 ->mValue.int_value);
623 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
624
625 std::shared_ptr<LogEvent> event2 =
626 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 20, 15);
627
628 allData.clear();
629 allData.push_back(event2);
630 gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS,
631 bucketStartTimeNs + bucketSizeNs);
632 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
633 EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()
634 ->second.front()
635 .mFields->begin()
636 ->mValue.int_value);
637 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
638 std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec);
639
640 allData.clear();
641 allData.push_back(
642 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10, 26));
643 gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS,
644 bucket2StartTimeNs + 2 * bucketSizeNs);
645 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
646 EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin()
647 ->second.front()
648 .mFields->begin()
649 ->mValue.int_value);
650 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
651 std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
652
653 // This event does not have the gauge field. Thus the current bucket value is 0.
654 allData.clear();
655 allData.push_back(CreateNoValuesLogEvent(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10));
656 gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS,
657 bucketStartTimeNs + 3 * bucketSizeNs);
658 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
659 EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty());
660 }
661
TEST(GaugeMetricProducerTest,TestPullOnTrigger)662 TEST(GaugeMetricProducerTest, TestPullOnTrigger) {
663 GaugeMetric metric;
664 metric.set_id(metricId);
665 metric.set_bucket(ONE_MINUTE);
666 metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
667 metric.mutable_gauge_fields_filter()->set_include_all(false);
668 metric.set_max_pull_delay_sec(INT_MAX);
669 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
670 gaugeFieldMatcher->set_field(tagId);
671 gaugeFieldMatcher->add_child()->set_field(1);
672
673 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
674
675 sp<EventMatcherWizard> eventMatcherWizard =
676 createEventMatcherWizard(tagId, logEventMatcherIndex);
677
678 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
679 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
680 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
681 vector<std::shared_ptr<LogEvent>>* data) {
682 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
683 data->clear();
684 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4));
685 return true;
686 }))
687 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
688 vector<std::shared_ptr<LogEvent>>* data) {
689 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
690 data->clear();
691 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
692 return true;
693 }))
694 .WillOnce(Return(true));
695
696 int triggerId = 5;
697 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
698
699 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
700 wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
701 tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
702 pullerManager, provider);
703 gaugeProducer.prepareFirstBucket();
704
705 ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
706
707 LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
708 CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 10);
709 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
710 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
711 triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
712 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
713 ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
714 triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1);
715 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
716
717 ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
718 ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mAggregatedAtoms.size());
719 auto it = gaugeProducer.mPastBuckets.begin()->second.back().mAggregatedAtoms.begin();
720 vector<int> atomValues;
721 atomValues.emplace_back(it->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
722 it++;
723 atomValues.emplace_back(it->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
724 EXPECT_THAT(atomValues, UnorderedElementsAre(4, 5));
725 }
726
TEST(GaugeMetricProducerTest,TestPullNWithoutTrigger)727 TEST(GaugeMetricProducerTest, TestPullNWithoutTrigger) {
728 GaugeMetric metric;
729 metric.set_id(metricId);
730 metric.set_bucket(ONE_MINUTE);
731 metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
732 metric.set_max_pull_delay_sec(INT_MAX);
733 metric.set_max_num_gauge_atoms_per_bucket(3);
734 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
735 gaugeFieldMatcher->set_field(tagId);
736
737 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
738
739 sp<EventMatcherWizard> eventMatcherWizard =
740 createEventMatcherWizard(tagId, logEventMatcherIndex);
741
742 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
743 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
744 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
745 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
746 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
747 vector<std::shared_ptr<LogEvent>>* data) {
748 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
749 data->clear();
750 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4));
751 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
752 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 6));
753 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 7));
754 return true;
755 }));
756
757 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
758
759 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
760 wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
761 tagId, /*triggerId=*/-1, tagId, bucketStartTimeNs,
762 bucketStartTimeNs, pullerManager, provider);
763
764 EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
765 gaugeProducer.prepareFirstBucket();
766 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
767 EXPECT_EQ(3UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
768
769 vector<std::shared_ptr<LogEvent>> allData;
770 allData.push_back(CreateNoValuesLogEvent(tagId, bucket2StartTimeNs + 10));
771 gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs + 30);
772 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
773 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
774
775 ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
776 ASSERT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.back().mAggregatedAtoms.size());
777 auto it = gaugeProducer.mPastBuckets.begin()->second.back().mAggregatedAtoms.begin();
778 vector<int> atomValues;
779 atomValues.emplace_back(it->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
780 it++;
781 atomValues.emplace_back(it->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
782 it++;
783 atomValues.emplace_back(it->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
784 EXPECT_THAT(atomValues, UnorderedElementsAre(4, 5, 6));
785 }
786
TEST(GaugeMetricProducerTest,TestRemoveDimensionInOutput)787 TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) {
788 GaugeMetric metric;
789 metric.set_id(metricId);
790 metric.set_bucket(ONE_MINUTE);
791 metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
792 metric.mutable_gauge_fields_filter()->set_include_all(true);
793 metric.set_max_pull_delay_sec(INT_MAX);
794 auto dimensionMatcher = metric.mutable_dimensions_in_what();
795 // use field 1 as dimension.
796 dimensionMatcher->set_field(tagId);
797 dimensionMatcher->add_child()->set_field(1);
798
799 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
800
801 sp<EventMatcherWizard> eventMatcherWizard =
802 createEventMatcherWizard(tagId, logEventMatcherIndex);
803
804 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
805 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
806 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
807 vector<std::shared_ptr<LogEvent>>* data) {
808 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
809 data->clear();
810 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 3, 4));
811 return true;
812 }))
813 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
814 vector<std::shared_ptr<LogEvent>>* data) {
815 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
816 data->clear();
817 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 5));
818 return true;
819 }))
820 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
821 vector<std::shared_ptr<LogEvent>>* data) {
822 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
823 data->clear();
824 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 6));
825 return true;
826 }))
827 .WillOnce(Return(true));
828
829 int triggerId = 5;
830 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
831
832 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
833 wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
834 tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
835 pullerManager, provider);
836 gaugeProducer.prepareFirstBucket();
837
838 LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
839 CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3);
840 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
841 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
842 triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 10);
843 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
844 ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size());
845 ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
846 triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
847 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
848 ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
849 triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1);
850 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
851
852 ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.size());
853 auto bucketIt = gaugeProducer.mPastBuckets.begin();
854 ASSERT_EQ(1UL, bucketIt->second.back().mAggregatedAtoms.size());
855 EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
856 EXPECT_EQ(4, bucketIt->second.back()
857 .mAggregatedAtoms.begin()
858 ->first.getAtomFieldValues()
859 .getValues()
860 .begin()
861 ->mValue.int_value);
862 bucketIt++;
863 ASSERT_EQ(2UL, bucketIt->second.back().mAggregatedAtoms.size());
864 EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
865 auto atomIt = bucketIt->second.back().mAggregatedAtoms.begin();
866 vector<int> atomValues;
867 atomValues.emplace_back(
868 atomIt->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
869 atomIt++;
870 atomValues.emplace_back(
871 atomIt->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
872 EXPECT_THAT(atomValues, UnorderedElementsAre(5, 6));
873 }
874
875 /*
876 * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
877 * is smaller than the "min_bucket_size_nanos" specified in the metric config.
878 */
TEST(GaugeMetricProducerTest_BucketDrop,TestBucketDropWhenBucketTooSmall)879 TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
880 GaugeMetric metric;
881 metric.set_id(metricId);
882 metric.set_bucket(FIVE_MINUTES);
883 metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
884 metric.set_min_bucket_size_nanos(10000000000); // 10 seconds
885
886 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
887
888 sp<EventMatcherWizard> eventMatcherWizard =
889 createEventMatcherWizard(tagId, logEventMatcherIndex);
890
891 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
892 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _))
893 // Bucket start.
894 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
895 vector<std::shared_ptr<LogEvent>>* data) {
896 data->clear();
897 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 10));
898 return true;
899 }));
900
901 int triggerId = 5;
902 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
903
904 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
905 wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
906 tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
907 pullerManager, provider);
908 gaugeProducer.prepareFirstBucket();
909
910 LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
911 CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3);
912 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
913
914 // Check dump report.
915 ProtoOutputStream output;
916 std::set<string> strSet;
917 gaugeProducer.onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */, true,
918 FAST /* dump_latency */, &strSet, &output);
919
920 StatsLogReport report = outputStreamToProto(&output);
921 EXPECT_TRUE(report.has_gauge_metrics());
922 ASSERT_EQ(0, report.gauge_metrics().data_size());
923 ASSERT_EQ(1, report.gauge_metrics().skipped_size());
924
925 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
926 report.gauge_metrics().skipped(0).start_bucket_elapsed_millis());
927 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000),
928 report.gauge_metrics().skipped(0).end_bucket_elapsed_millis());
929 ASSERT_EQ(1, report.gauge_metrics().skipped(0).drop_event_size());
930
931 auto dropEvent = report.gauge_metrics().skipped(0).drop_event(0);
932 EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
933 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis());
934 }
935
TEST(GaugeMetricProducerTest,TestPullDimensionalSampling)936 TEST(GaugeMetricProducerTest, TestPullDimensionalSampling) {
937 ShardOffsetProvider::getInstance().setShardOffset(5);
938
939 StatsdConfig config;
940
941 int triggerId = 5;
942 int shardCount = 2;
943 GaugeMetric sampledGaugeMetric = createGaugeMetric(
944 "GaugePullSampled", metricId, GaugeMetric::FIRST_N_SAMPLES, nullopt, triggerId);
945 sampledGaugeMetric.set_max_pull_delay_sec(INT_MAX);
946 *sampledGaugeMetric.mutable_dimensions_in_what() = CreateDimensions(tagId, {1});
947 *sampledGaugeMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
948 CreateDimensions(tagId, {1});
949 sampledGaugeMetric.mutable_dimensional_sampling_info()->set_shard_count(shardCount);
950 *config.add_gauge_metric() = sampledGaugeMetric;
951
952 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
953
954 sp<EventMatcherWizard> eventMatcherWizard =
955 createEventMatcherWizard(tagId, logEventMatcherIndex);
956
957 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
958
959 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
960 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
961 vector<std::shared_ptr<LogEvent>>* data) {
962 data->clear();
963 data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 10, 1001, 5, 10));
964 data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 10, 1002, 10, 10));
965 data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 10, 1003, 15, 10));
966 return true;
967 }))
968 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
969 vector<std::shared_ptr<LogEvent>>* data) {
970 data->clear();
971 data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 20, 1001, 6, 10));
972 data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 20, 1002, 12, 10));
973 data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 20, 1003, 18, 10));
974 return true;
975 }));
976 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
977 GaugeMetricProducer gaugeProducer(
978 kConfigKey, sampledGaugeMetric, -1 /*-1 meaning no condition*/, {}, wizard, protoHash,
979 logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, tagId, bucketStartTimeNs,
980 bucketStartTimeNs, pullerManager, provider);
981 SamplingInfo samplingInfo;
982 samplingInfo.shardCount = shardCount;
983 translateFieldMatcher(sampledGaugeMetric.dimensional_sampling_info().sampled_what_field(),
984 &samplingInfo.sampledWhatFields);
985 gaugeProducer.setSamplingInfo(samplingInfo);
986 gaugeProducer.prepareFirstBucket();
987
988 LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
989 CreateRepeatedValueLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 10, 5);
990 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
991
992 triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
993 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
994
995 // Check dump report.
996 ProtoOutputStream output;
997 std::set<string> strSet;
998 int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000;
999 gaugeProducer.onDumpReport(dumpReportTimeNs, true /* include current buckets */, true,
1000 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
1001
1002 StatsLogReport report = outputStreamToProto(&output);
1003 backfillDimensionPath(&report);
1004 backfillStartEndTimestamp(&report);
1005 backfillAggregatedAtoms(&report);
1006
1007 EXPECT_TRUE(report.has_gauge_metrics());
1008 StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
1009 sortMetricDataByDimensionsValue(report.gauge_metrics(), &gaugeMetrics);
1010 ASSERT_EQ(2, gaugeMetrics.data_size());
1011 EXPECT_EQ(0, report.gauge_metrics().skipped_size());
1012
1013 // Only Uid 1 and 3 are logged. (odd hash value) + (offset of 5) % (shard count of 2) = 0
1014 GaugeMetricData data = gaugeMetrics.data(0);
1015 ValidateUidDimension(data.dimensions_in_what(), tagId, 1001);
1016 ValidateGaugeBucketTimes(data.bucket_info(0), bucketStartTimeNs, dumpReportTimeNs,
1017 {bucketStartTimeNs + 10, bucketStartTimeNs + 20});
1018
1019 data = gaugeMetrics.data(1);
1020 ValidateUidDimension(data.dimensions_in_what(), tagId, 1003);
1021 ValidateGaugeBucketTimes(data.bucket_info(0), bucketStartTimeNs, dumpReportTimeNs,
1022 {bucketStartTimeNs + 10, bucketStartTimeNs + 20});
1023 }
1024
1025 } // namespace statsd
1026 } // namespace os
1027 } // namespace android
1028 #else
1029 GTEST_LOG_(INFO) << "This test does nothing.\n";
1030 #endif
1031