1 // Copyright (C) 2020 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/external/StatsPullerManager.h"
16
17 #include <aidl/android/os/IPullAtomResultReceiver.h>
18 #include <aidl/android/util/StatsEventParcel.h>
19 #include <gmock/gmock.h>
20 #include <gtest/gtest.h>
21
22 #include <atomic>
23 #include <thread>
24
25 #include "stats_event.h"
26 #include "tests/statsd_test_util.h"
27
28 using aidl::android::util::StatsEventParcel;
29 using ::ndk::SharedRefBase;
30 using std::make_shared;
31 using std::shared_ptr;
32 using std::vector;
33
34 namespace android {
35 namespace os {
36 namespace statsd {
37
38 namespace {
39
40 int pullTagId1 = 10101;
41 int pullTagId2 = 10102;
42 int pullTagIdWithoutUid = 99999;
43 int uid1 = 9999;
44 int uid2 = 8888;
45 int unRegisteredUid1 = 7777;
46 int unRegisteredUid2 = 7778;
47 ConfigKey configKey(50, 12345);
48 ConfigKey badConfigKey(60, 54321);
49 int unregisteredUid = 98765;
50 int64_t coolDownNs = NS_PER_SEC;
51 int64_t timeoutNs = NS_PER_SEC / 2;
52 std::atomic_int pullReceiverCounter;
53
createSimpleEvent(int32_t atomId,int32_t value)54 AStatsEvent* createSimpleEvent(int32_t atomId, int32_t value) {
55 AStatsEvent* event = AStatsEvent_obtain();
56 AStatsEvent_setAtomId(event, atomId);
57 AStatsEvent_writeInt32(event, value);
58 AStatsEvent_build(event);
59 return event;
60 }
61
62 class FakePullAtomCallback : public BnPullAtomCallback {
63 public:
FakePullAtomCallback(int32_t uid,int32_t pullDurationNs=0)64 FakePullAtomCallback(int32_t uid, int32_t pullDurationNs = 0)
65 : mUid(uid), mDurationNs(pullDurationNs) {};
onPullAtom(int atomTag,const shared_ptr<IPullAtomResultReceiver> & resultReceiver)66 Status onPullAtom(int atomTag,
67 const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
68 onPullAtomCalled(atomTag);
69
70 vector<StatsEventParcel> parcels;
71 AStatsEvent* event = createSimpleEvent(atomTag, mUid);
72 size_t size;
73 uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
74
75 StatsEventParcel p;
76 // vector.assign() creates a copy, but this is inevitable unless
77 // stats_event.h/c uses a vector as opposed to a buffer.
78 p.buffer.assign(buffer, buffer + size);
79 parcels.push_back(std::move(p));
80 AStatsEvent_release(event);
81
82 if (mDurationNs > 0) {
83 std::this_thread::sleep_for(std::chrono::nanoseconds(mDurationNs));
84 }
85
86 resultReceiver->pullFinished(atomTag, /*success*/ true, parcels);
87 return Status::ok();
88 }
89 int32_t mUid;
90 int32_t mDurationNs;
91
onPullAtomCalled(int atomTag) const92 virtual void onPullAtomCalled(int atomTag) const {};
93 };
94
95 class FakePullUidProvider : public PullUidProvider {
96 public:
getPullAtomUids(int atomId)97 vector<int32_t> getPullAtomUids(int atomId) override {
98 if (atomId == pullTagId1) {
99 return {uid2, uid1};
100 } else if (atomId == pullTagId2) {
101 return {uid2};
102 }
103 return {};
104 }
105 };
106
107 class FakeAllowAllAtomsUidProvider : public PullUidProvider {
108 public:
getPullAtomUids(int atomId)109 vector<int32_t> getPullAtomUids(int atomId) override {
110 return {uid1, uid2};
111 }
112 };
113 class MockPullAtomCallback : public FakePullAtomCallback {
114 public:
MockPullAtomCallback(int32_t uid,int32_t pullDurationNs=0)115 MockPullAtomCallback(int32_t uid, int32_t pullDurationNs = 0)
116 : FakePullAtomCallback(uid, pullDurationNs) {
117 }
118
119 MOCK_METHOD(void, onPullAtomCalled, (int), (const override));
120 };
121
122 class MockPullDataReceiver : public PullDataReceiver {
123 public:
124 virtual ~MockPullDataReceiver() = default;
125
126 MOCK_METHOD(void, onDataPulled,
127 (const std::vector<std::shared_ptr<LogEvent>>&, PullResult, int64_t), (override));
128
isPullNeeded() const129 bool isPullNeeded() const override {
130 return true;
131 };
132 };
133
createPullerManagerAndRegister(int32_t pullDurationMs=0)134 sp<StatsPullerManager> createPullerManagerAndRegister(int32_t pullDurationMs = 0) {
135 sp<StatsPullerManager> pullerManager = new StatsPullerManager();
136 shared_ptr<FakePullAtomCallback> cb1 =
137 SharedRefBase::make<FakePullAtomCallback>(uid1, pullDurationMs);
138 pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1);
139 shared_ptr<FakePullAtomCallback> cb2 =
140 SharedRefBase::make<FakePullAtomCallback>(uid2, pullDurationMs);
141 shared_ptr<FakePullAtomCallback> cb3 =
142 SharedRefBase::make<FakePullAtomCallback>(uid2, pullDurationMs);
143 pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2);
144 pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1);
145 pullerManager->RegisterPullAtomCallback(unRegisteredUid1, pullTagIdWithoutUid, coolDownNs,
146 timeoutNs, {}, cb3);
147 pullerManager->RegisterPullAtomCallback(unRegisteredUid2, pullTagIdWithoutUid, coolDownNs,
148 timeoutNs, {}, cb3);
149 return pullerManager;
150 }
151 } // anonymous namespace
152
TEST(StatsPullerManagerTest,TestPullInvalidUid)153 TEST(StatsPullerManagerTest, TestPullInvalidUid) {
154 sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
155
156 vector<shared_ptr<LogEvent>> data;
157 EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data));
158 }
159
TEST(StatsPullerManagerTest,TestPullChoosesCorrectUid)160 TEST(StatsPullerManagerTest, TestPullChoosesCorrectUid) {
161 sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
162
163 vector<shared_ptr<LogEvent>> data;
164 EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data));
165 ASSERT_EQ(data.size(), 1);
166 EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
167 ASSERT_EQ(data[0]->getValues().size(), 1);
168 EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid1);
169 }
170
TEST(StatsPullerManagerTest,TestPullInvalidConfigKey)171 TEST(StatsPullerManagerTest, TestPullInvalidConfigKey) {
172 sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
173 sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
174 pullerManager->RegisterPullUidProvider(configKey, uidProvider);
175
176 vector<shared_ptr<LogEvent>> data;
177 EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data));
178 }
179
TEST(StatsPullerManagerTest,TestPullConfigKeyGood)180 TEST(StatsPullerManagerTest, TestPullConfigKeyGood) {
181 sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
182 sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
183 pullerManager->RegisterPullUidProvider(configKey, uidProvider);
184
185 vector<shared_ptr<LogEvent>> data;
186 EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data));
187 EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
188 ASSERT_EQ(data[0]->getValues().size(), 1);
189 EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid2);
190 }
191
TEST(StatsPullerManagerTest,TestPullConfigKeyNoPullerWithUid)192 TEST(StatsPullerManagerTest, TestPullConfigKeyNoPullerWithUid) {
193 sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
194 sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
195 pullerManager->RegisterPullUidProvider(configKey, uidProvider);
196
197 vector<shared_ptr<LogEvent>> data;
198 EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data));
199 }
200
TEST(StatsPullerManagerTest,TestSameAtomIsPulledInABatch)201 TEST(StatsPullerManagerTest, TestSameAtomIsPulledInABatch) {
202 // define 2 puller callbacks with small duration each to guaranty that
203 // call sequence callback A + callback B will invalidate pull cache
204 // for callback A if PullerManager does not group receivers by tagId
205
206 const int64_t pullDurationNs = (int)(timeoutNs * 0.9);
207
208 sp<StatsPullerManager> pullerManager = new StatsPullerManager();
209 auto cb1 = SharedRefBase::make<StrictMock<MockPullAtomCallback>>(uid1, pullDurationNs);
210 pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1);
211 auto cb2 = SharedRefBase::make<StrictMock<MockPullAtomCallback>>(uid2, pullDurationNs);
212 pullerManager->RegisterPullAtomCallback(uid2, pullTagId2, coolDownNs, timeoutNs, {}, cb2);
213
214 sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
215 pullerManager->RegisterPullUidProvider(configKey, uidProvider);
216
217 const int64_t bucketBoundary = NS_PER_SEC * 60 * 60 * 1; // 1 hour
218
219 // create 10 receivers to simulate 10 distinct metrics for pulled atoms
220 // add 10 metric where 5 depends on atom A and 5 on atom B
221 vector<sp<MockPullDataReceiver>> receivers;
222 receivers.reserve(10);
223 for (int i = 0; i < 10; i++) {
224 auto receiver = new StrictMock<MockPullDataReceiver>();
225 EXPECT_CALL(*receiver, onDataPulled(_, _, _)).Times(1);
226 receivers.push_back(receiver);
227
228 const int32_t atomTag = i % 2 == 0 ? pullTagId1 : pullTagId2;
229 pullerManager->RegisterReceiver(atomTag, configKey, receiver, bucketBoundary,
230 bucketBoundary);
231 }
232
233 // check that only 2 pulls will be done and remaining 8 pulls from cache
234 EXPECT_CALL(*cb1, onPullAtomCalled(pullTagId1)).Times(1);
235 EXPECT_CALL(*cb2, onPullAtomCalled(pullTagId2)).Times(1);
236
237 // validate that created 2 receivers groups just for 2 atoms with 5 receivers in each
238 ASSERT_EQ(pullerManager->mReceivers.size(), 2);
239 ASSERT_EQ(pullerManager->mReceivers.begin()->second.size(), 5);
240 ASSERT_EQ(pullerManager->mReceivers.rbegin()->second.size(), 5);
241
242 // simulate pulls
243 pullerManager->OnAlarmFired(bucketBoundary + 1);
244
245 // to allow async pullers to complete + some extra time
246 std::this_thread::sleep_for(std::chrono::nanoseconds(pullDurationNs * 3));
247 }
248
TEST(StatsPullerManagerTest,TestOnAlarmFiredMultipleReceivers)249 TEST(StatsPullerManagerTest, TestOnAlarmFiredMultipleReceivers) {
250 pullReceiverCounter.store(0);
251 sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
252 sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
253 sp<MockPullDataReceiver> receiver = new StrictMock<MockPullDataReceiver>();
254 EXPECT_CALL(*receiver, onDataPulled(_, PullResult::PULL_RESULT_SUCCESS, _))
255 .WillRepeatedly(Invoke([]() { pullReceiverCounter++; }));
256 for (int i = 0; i < 250; i++) {
257 ConfigKey newConfigKey(uid1, i);
258 pullerManager->RegisterReceiver(pullTagId1, newConfigKey, receiver,
259 /*nextPullTimeNs =*/0,
260 /*intervalNs =*/(250 - i) * 60 * NS_PER_SEC);
261 pullerManager->RegisterPullUidProvider(newConfigKey, uidProvider);
262 }
263
264 pullerManager->OnAlarmFired(100);
265
266 EXPECT_EQ(pullReceiverCounter.load(), 250);
267
268 pullerManager->OnAlarmFired(60 * NS_PER_SEC + 100);
269 EXPECT_EQ(pullReceiverCounter.load(), 251);
270 }
271
TEST(StatsPullerManagerTest,TestOnAlarmFiredMultiplePulls)272 TEST(StatsPullerManagerTest, TestOnAlarmFiredMultiplePulls) {
273 pullReceiverCounter.store(0);
274 sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
275 sp<FakeAllowAllAtomsUidProvider> uidProvider = new FakeAllowAllAtomsUidProvider();
276 sp<MockPullDataReceiver> receiver = new StrictMock<MockPullDataReceiver>();
277 EXPECT_CALL(*receiver, onDataPulled(_, PullResult::PULL_RESULT_SUCCESS, _))
278 .WillRepeatedly(Invoke([]() { pullReceiverCounter++; }));
279 pullerManager->RegisterPullUidProvider(configKey, uidProvider);
280 for (int i = 0; i < 250; i++) {
281 pullerManager->RegisterReceiver(pullTagId1 + i, configKey, receiver,
282 /*nextPullTimeNs =*/0,
283 /*intervalNs =*/1000);
284 shared_ptr<FakePullAtomCallback> fakeCallback =
285 SharedRefBase::make<FakePullAtomCallback>(uid1, /*pullDurationMs= */ 0);
286 pullerManager->RegisterPullAtomCallback(uid1, pullTagId1 + i, coolDownNs, timeoutNs, {},
287 fakeCallback);
288 }
289
290 pullerManager->OnAlarmFired(100);
291
292 EXPECT_EQ(pullReceiverCounter.load(), 250);
293 }
294
TEST(StatsPullerManagerTest,TestOnAlarmFiredNoPullerForUidNotesPullerNotFound)295 TEST(StatsPullerManagerTest, TestOnAlarmFiredNoPullerForUidNotesPullerNotFound) {
296 StatsdStats::getInstance().reset();
297
298 sp<MockPullDataReceiver> receiver = new StrictMock<MockPullDataReceiver>();
299 EXPECT_CALL(*receiver, onDataPulled(_, PullResult::PULL_RESULT_FAIL, _)).Times(1);
300 sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
301 sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
302 pullerManager->RegisterPullUidProvider(configKey, uidProvider);
303 pullerManager->RegisterReceiver(pullTagIdWithoutUid, configKey, receiver, /*nextPullTimeNs =*/0,
304 /*intervalNs =*/60 * NS_PER_SEC);
305
306 pullerManager->OnAlarmFired(100);
307 // Assert that mNextPullTime is correctly set. The #onDataPulled mock is only invoked once.
308 pullerManager->OnAlarmFired(100);
309
310 EXPECT_EQ(StatsdStats::getInstance().mPulledAtomStats[pullTagIdWithoutUid].pullerNotFound, 1);
311 EXPECT_EQ(StatsdStats::getInstance().mPulledAtomStats[pullTagIdWithoutUid].pullFailed, 0);
312 }
313
TEST(StatsPullerManagerTest,TestOnAlarmFiredNoUidProviderUpdatesNextPullTime)314 TEST(StatsPullerManagerTest, TestOnAlarmFiredNoUidProviderUpdatesNextPullTime) {
315 StatsdStats::getInstance().reset();
316
317 sp<MockPullDataReceiver> receiver = new StrictMock<MockPullDataReceiver>();
318 EXPECT_CALL(*receiver, onDataPulled(_, PullResult::PULL_RESULT_FAIL, _)).Times(1);
319 sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
320 pullerManager->RegisterReceiver(pullTagId1, configKey, receiver, /*nextPullTimeNs =*/0,
321 /*intervalNs =*/60 * NS_PER_SEC);
322
323 pullerManager->OnAlarmFired(100);
324 // Assert that mNextPullTime is correctly set. The #onDataPulled mock is only invoked once.
325 pullerManager->OnAlarmFired(100);
326
327 EXPECT_EQ(StatsdStats::getInstance().mPulledAtomStats[pullTagId1].pullUidProviderNotFound, 1);
328 EXPECT_EQ(StatsdStats::getInstance().mPulledAtomStats[pullTagId1].pullerNotFound, 0);
329 }
330
TEST(StatsPullerManagerTest,TestOnAlarmFiredMultipleUidsSelectsFirstUid)331 TEST(StatsPullerManagerTest, TestOnAlarmFiredMultipleUidsSelectsFirstUid) {
332 pullReceiverCounter.store(0);
333 sp<MockPullDataReceiver> receiver = new StrictMock<MockPullDataReceiver>();
334 EXPECT_CALL(*receiver, onDataPulled(_, PullResult::PULL_RESULT_SUCCESS, _))
335 .WillRepeatedly(Invoke([]() { pullReceiverCounter++; }));
336 sp<StatsPullerManager> pullerManager = new StatsPullerManager();
337 shared_ptr<MockPullAtomCallback> cb1 =
338 SharedRefBase::make<MockPullAtomCallback>(uid1, /*=pullDurationMs=*/0);
339 shared_ptr<MockPullAtomCallback> cb2 =
340 SharedRefBase::make<MockPullAtomCallback>(uid2, /*=pullDurationMs=*/0);
341 EXPECT_CALL(*cb1, onPullAtomCalled(pullTagId1)).Times(0);
342 // We expect cb2 to be invoked because uid2 is provided before uid1 in the PullUidProvider.
343 EXPECT_CALL(*cb2, onPullAtomCalled(pullTagId1)).Times(1);
344 pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1);
345 pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2);
346 sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
347 pullerManager->RegisterPullUidProvider(configKey, uidProvider);
348 pullerManager->RegisterReceiver(pullTagId1, configKey, receiver, /*nextPullTimeNs =*/0,
349 /*intervalNs =*/1000);
350
351 pullerManager->OnAlarmFired(100);
352
353 EXPECT_EQ(pullReceiverCounter.load(), 1);
354 }
355
TEST(StatsPullerManagerTest,TestOnAlarmFiredUidsNotRegisteredInPullAtomCallback)356 TEST(StatsPullerManagerTest, TestOnAlarmFiredUidsNotRegisteredInPullAtomCallback) {
357 sp<MockPullDataReceiver> receiver = new StrictMock<MockPullDataReceiver>();
358 EXPECT_CALL(*receiver, onDataPulled(_, PullResult::PULL_RESULT_FAIL, _)).Times(1);
359 sp<StatsPullerManager> pullerManager = new StatsPullerManager();
360 shared_ptr<MockPullAtomCallback> cb1 =
361 SharedRefBase::make<MockPullAtomCallback>(unRegisteredUid1, /*=pullDurationMs=*/0);
362 EXPECT_CALL(*cb1, onPullAtomCalled(pullTagId1)).Times(0);
363 pullerManager->RegisterPullAtomCallback(unRegisteredUid1, pullTagId1, coolDownNs, timeoutNs, {},
364 cb1);
365 sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
366 pullerManager->RegisterPullUidProvider(configKey, uidProvider);
367 pullerManager->RegisterReceiver(pullTagId1, configKey, receiver, /*nextPullTimeNs =*/0,
368 /*intervalNs =*/1000);
369
370 pullerManager->OnAlarmFired(100);
371
372 EXPECT_EQ(StatsdStats::getInstance().mPulledAtomStats[pullTagId1].pullerNotFound, 1);
373 EXPECT_EQ(StatsdStats::getInstance().mPulledAtomStats[pullTagId1].pullFailed, 0);
374 }
375
376 } // namespace statsd
377 } // namespace os
378 } // namespace android
379