• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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