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