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