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 <thread>
23 
24 #include "stats_event.h"
25 #include "tests/statsd_test_util.h"
26 
27 using aidl::android::util::StatsEventParcel;
28 using ::ndk::SharedRefBase;
29 using std::make_shared;
30 using std::shared_ptr;
31 using std::vector;
32 
33 namespace android {
34 namespace os {
35 namespace statsd {
36 
37 namespace {
38 
39 int pullTagId1 = 10101;
40 int pullTagId2 = 10102;
41 int uid1 = 9999;
42 int uid2 = 8888;
43 ConfigKey configKey(50, 12345);
44 ConfigKey badConfigKey(60, 54321);
45 int unregisteredUid = 98765;
46 int64_t coolDownNs = NS_PER_SEC;
47 int64_t timeoutNs = NS_PER_SEC / 2;
48 
createSimpleEvent(int32_t atomId,int32_t value)49 AStatsEvent* createSimpleEvent(int32_t atomId, int32_t value) {
50     AStatsEvent* event = AStatsEvent_obtain();
51     AStatsEvent_setAtomId(event, atomId);
52     AStatsEvent_writeInt32(event, value);
53     AStatsEvent_build(event);
54     return event;
55 }
56 
57 class FakePullAtomCallback : public BnPullAtomCallback {
58 public:
FakePullAtomCallback(int32_t uid,int32_t pullDurationNs=0)59     FakePullAtomCallback(int32_t uid, int32_t pullDurationNs = 0)
60         : mUid(uid), mDurationNs(pullDurationNs) {};
onPullAtom(int atomTag,const shared_ptr<IPullAtomResultReceiver> & resultReceiver)61     Status onPullAtom(int atomTag,
62                       const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
63         onPullAtomCalled(atomTag);
64 
65         vector<StatsEventParcel> parcels;
66         AStatsEvent* event = createSimpleEvent(atomTag, mUid);
67         size_t size;
68         uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
69 
70         StatsEventParcel p;
71         // vector.assign() creates a copy, but this is inevitable unless
72         // stats_event.h/c uses a vector as opposed to a buffer.
73         p.buffer.assign(buffer, buffer + size);
74         parcels.push_back(std::move(p));
75         AStatsEvent_release(event);
76 
77         if (mDurationNs > 0) {
78             std::this_thread::sleep_for(std::chrono::nanoseconds(mDurationNs));
79         }
80 
81         resultReceiver->pullFinished(atomTag, /*success*/ true, parcels);
82         return Status::ok();
83     }
84     int32_t mUid;
85     int32_t mDurationNs;
86 
onPullAtomCalled(int atomTag) const87     virtual void onPullAtomCalled(int atomTag) const {};
88 };
89 
90 class FakePullUidProvider : public PullUidProvider {
91 public:
getPullAtomUids(int atomId)92     vector<int32_t> getPullAtomUids(int atomId) override {
93         if (atomId == pullTagId1) {
94             return {uid2, uid1};
95         } else if (atomId == pullTagId2) {
96             return {uid2};
97         }
98         return {};
99     }
100 };
101 
102 class MockPullAtomCallback : public FakePullAtomCallback {
103 public:
MockPullAtomCallback(int32_t uid,int32_t pullDurationNs=0)104     MockPullAtomCallback(int32_t uid, int32_t pullDurationNs = 0)
105         : FakePullAtomCallback(uid, pullDurationNs) {
106     }
107 
108     MOCK_METHOD(void, onPullAtomCalled, (int), (const override));
109 };
110 
111 class MockPullDataReceiver : public PullDataReceiver {
112 public:
113     virtual ~MockPullDataReceiver() = default;
114 
115     MOCK_METHOD(void, onDataPulled,
116                 (const std::vector<std::shared_ptr<LogEvent>>&, PullResult, int64_t), (override));
117 
isPullNeeded() const118     bool isPullNeeded() const override {
119         return true;
120     };
121 };
122 
createPullerManagerAndRegister(int32_t pullDurationMs=0)123 sp<StatsPullerManager> createPullerManagerAndRegister(int32_t pullDurationMs = 0) {
124     sp<StatsPullerManager> pullerManager = new StatsPullerManager();
125     shared_ptr<FakePullAtomCallback> cb1 =
126             SharedRefBase::make<FakePullAtomCallback>(uid1, pullDurationMs);
127     pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1);
128     shared_ptr<FakePullAtomCallback> cb2 =
129             SharedRefBase::make<FakePullAtomCallback>(uid2, pullDurationMs);
130     pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2);
131     pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1);
132     return pullerManager;
133 }
134 }  // anonymous namespace
135 
TEST(StatsPullerManagerTest,TestPullInvalidUid)136 TEST(StatsPullerManagerTest, TestPullInvalidUid) {
137     sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
138 
139     vector<shared_ptr<LogEvent>> data;
140     EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data));
141 }
142 
TEST(StatsPullerManagerTest,TestPullChoosesCorrectUid)143 TEST(StatsPullerManagerTest, TestPullChoosesCorrectUid) {
144     sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
145 
146     vector<shared_ptr<LogEvent>> data;
147     EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data));
148     ASSERT_EQ(data.size(), 1);
149     EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
150     ASSERT_EQ(data[0]->getValues().size(), 1);
151     EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid1);
152 }
153 
TEST(StatsPullerManagerTest,TestPullInvalidConfigKey)154 TEST(StatsPullerManagerTest, TestPullInvalidConfigKey) {
155     sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
156     sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
157     pullerManager->RegisterPullUidProvider(configKey, uidProvider);
158 
159     vector<shared_ptr<LogEvent>> data;
160     EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data));
161 }
162 
TEST(StatsPullerManagerTest,TestPullConfigKeyGood)163 TEST(StatsPullerManagerTest, TestPullConfigKeyGood) {
164     sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
165     sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
166     pullerManager->RegisterPullUidProvider(configKey, uidProvider);
167 
168     vector<shared_ptr<LogEvent>> data;
169     EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data));
170     EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
171     ASSERT_EQ(data[0]->getValues().size(), 1);
172     EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid2);
173 }
174 
TEST(StatsPullerManagerTest,TestPullConfigKeyNoPullerWithUid)175 TEST(StatsPullerManagerTest, TestPullConfigKeyNoPullerWithUid) {
176     sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
177     sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
178     pullerManager->RegisterPullUidProvider(configKey, uidProvider);
179 
180     vector<shared_ptr<LogEvent>> data;
181     EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data));
182 }
183 
TEST(StatsPullerManagerTest,TestSameAtomIsPulledInABatch)184 TEST(StatsPullerManagerTest, TestSameAtomIsPulledInABatch) {
185     // define 2 puller callbacks with small duration each to guaranty that
186     // call sequence callback A + callback B will invalidate pull cache
187     // for callback A if PullerManager does not group receivers by tagId
188 
189     const int64_t pullDurationNs = (int)(timeoutNs * 0.9);
190 
191     sp<StatsPullerManager> pullerManager = new StatsPullerManager();
192     auto cb1 = SharedRefBase::make<StrictMock<MockPullAtomCallback>>(uid1, pullDurationNs);
193     pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1);
194     auto cb2 = SharedRefBase::make<StrictMock<MockPullAtomCallback>>(uid2, pullDurationNs);
195     pullerManager->RegisterPullAtomCallback(uid2, pullTagId2, coolDownNs, timeoutNs, {}, cb2);
196 
197     sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
198     pullerManager->RegisterPullUidProvider(configKey, uidProvider);
199 
200     const int64_t bucketBoundary = NS_PER_SEC * 60 * 60 * 1;  // 1 hour
201 
202     // create 10 receivers to simulate 10 distinct metrics for pulled atoms
203     // add 10 metric where 5 depends on atom A and 5 on atom B
204     vector<sp<MockPullDataReceiver>> receivers;
205     receivers.reserve(10);
206     for (int i = 0; i < 10; i++) {
207         auto receiver = new StrictMock<MockPullDataReceiver>();
208         EXPECT_CALL(*receiver, onDataPulled(_, _, _)).Times(1);
209         receivers.push_back(receiver);
210 
211         const int32_t atomTag = i % 2 == 0 ? pullTagId1 : pullTagId2;
212         pullerManager->RegisterReceiver(atomTag, configKey, receiver, bucketBoundary,
213                                         bucketBoundary);
214     }
215 
216     // check that only 2 pulls will be done and remaining 8 pulls from cache
217     EXPECT_CALL(*cb1, onPullAtomCalled(pullTagId1)).Times(1);
218     EXPECT_CALL(*cb2, onPullAtomCalled(pullTagId2)).Times(1);
219 
220     // validate that created 2 receivers groups just for 2 atoms with 5 receivers in each
221     ASSERT_EQ(pullerManager->mReceivers.size(), 2);
222     ASSERT_EQ(pullerManager->mReceivers.begin()->second.size(), 5);
223     ASSERT_EQ(pullerManager->mReceivers.rbegin()->second.size(), 5);
224 
225     // simulate pulls
226     pullerManager->OnAlarmFired(bucketBoundary + 1);
227 
228     // to allow async pullers to complete + some extra time
229     std::this_thread::sleep_for(std::chrono::nanoseconds(pullDurationNs * 3));
230 }
231 
232 }  // namespace statsd
233 }  // namespace os
234 }  // namespace android
235