• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "IoPerfCollection.h"
18 #include "MockPackageInfoResolver.h"
19 #include "MockProcPidStat.h"
20 #include "MockProcStat.h"
21 #include "MockUidIoStats.h"
22 #include "MockWatchdogServiceHelper.h"
23 #include "PackageInfoResolver.h"
24 
25 #include <WatchdogProperties.sysprop.h>
26 #include <android-base/file.h>
27 #include <gmock/gmock.h>
28 
29 #include <sys/types.h>
30 #include <unistd.h>
31 
32 #include <string>
33 #include <vector>
34 
35 namespace android {
36 namespace automotive {
37 namespace watchdog {
38 
39 using ::android::sp;
40 using ::android::base::Error;
41 using ::android::base::ReadFdToString;
42 using ::android::base::Result;
43 using ::testing::_;
44 using ::testing::Return;
45 
46 namespace {
47 
isEqual(const UidIoPerfData & lhs,const UidIoPerfData & rhs)48 bool isEqual(const UidIoPerfData& lhs, const UidIoPerfData& rhs) {
49     if (lhs.topNReads.size() != rhs.topNReads.size() ||
50         lhs.topNWrites.size() != rhs.topNWrites.size()) {
51         return false;
52     }
53     for (int i = 0; i < METRIC_TYPES; ++i) {
54         for (int j = 0; j < UID_STATES; ++j) {
55             if (lhs.total[i][j] != rhs.total[i][j]) {
56                 return false;
57             }
58         }
59     }
60     auto comp = [&](const UidIoPerfData::Stats& l, const UidIoPerfData::Stats& r) -> bool {
61         bool isEqual = l.userId == r.userId && l.packageName == r.packageName;
62         for (int i = 0; i < UID_STATES; ++i) {
63             isEqual &= l.bytes[i] == r.bytes[i] && l.fsync[i] == r.fsync[i];
64         }
65         return isEqual;
66     };
67     return lhs.topNReads.size() == rhs.topNReads.size() &&
68             std::equal(lhs.topNReads.begin(), lhs.topNReads.end(), rhs.topNReads.begin(), comp) &&
69             lhs.topNWrites.size() == rhs.topNWrites.size() &&
70             std::equal(lhs.topNWrites.begin(), lhs.topNWrites.end(), rhs.topNWrites.begin(), comp);
71 }
72 
isEqual(const SystemIoPerfData & lhs,const SystemIoPerfData & rhs)73 bool isEqual(const SystemIoPerfData& lhs, const SystemIoPerfData& rhs) {
74     return lhs.cpuIoWaitTime == rhs.cpuIoWaitTime && lhs.totalCpuTime == rhs.totalCpuTime &&
75             lhs.ioBlockedProcessesCnt == rhs.ioBlockedProcessesCnt &&
76             lhs.totalProcessesCnt == rhs.totalProcessesCnt;
77 }
78 
isEqual(const ProcessIoPerfData & lhs,const ProcessIoPerfData & rhs)79 bool isEqual(const ProcessIoPerfData& lhs, const ProcessIoPerfData& rhs) {
80     if (lhs.topNIoBlockedUids.size() != rhs.topNIoBlockedUids.size() ||
81         lhs.topNMajorFaultUids.size() != rhs.topNMajorFaultUids.size() ||
82         lhs.totalMajorFaults != rhs.totalMajorFaults ||
83         lhs.majorFaultsPercentChange != rhs.majorFaultsPercentChange) {
84         return false;
85     }
86     auto comp = [&](const ProcessIoPerfData::UidStats& l,
87                     const ProcessIoPerfData::UidStats& r) -> bool {
88         auto comp = [&](const ProcessIoPerfData::UidStats::ProcessStats& l,
89                         const ProcessIoPerfData::UidStats::ProcessStats& r) -> bool {
90             return l.comm == r.comm && l.count == r.count;
91         };
92         return l.userId == r.userId && l.packageName == r.packageName && l.count == r.count &&
93                 l.topNProcesses.size() == r.topNProcesses.size() &&
94                 std::equal(l.topNProcesses.begin(), l.topNProcesses.end(), r.topNProcesses.begin(),
95                            comp);
96     };
97     return lhs.topNIoBlockedUids.size() == lhs.topNIoBlockedUids.size() &&
98             std::equal(lhs.topNIoBlockedUids.begin(), lhs.topNIoBlockedUids.end(),
99                        rhs.topNIoBlockedUids.begin(), comp) &&
100             lhs.topNIoBlockedUidsTotalTaskCnt.size() == rhs.topNIoBlockedUidsTotalTaskCnt.size() &&
101             std::equal(lhs.topNIoBlockedUidsTotalTaskCnt.begin(),
102                        lhs.topNIoBlockedUidsTotalTaskCnt.end(),
103                        rhs.topNIoBlockedUidsTotalTaskCnt.begin()) &&
104             lhs.topNMajorFaultUids.size() == rhs.topNMajorFaultUids.size() &&
105             std::equal(lhs.topNMajorFaultUids.begin(), lhs.topNMajorFaultUids.end(),
106                        rhs.topNMajorFaultUids.begin(), comp);
107 }
108 
isEqual(const IoPerfRecord & lhs,const IoPerfRecord & rhs)109 bool isEqual(const IoPerfRecord& lhs, const IoPerfRecord& rhs) {
110     return isEqual(lhs.uidIoPerfData, rhs.uidIoPerfData) &&
111             isEqual(lhs.systemIoPerfData, rhs.systemIoPerfData) &&
112             isEqual(lhs.processIoPerfData, rhs.processIoPerfData);
113 }
114 
countOccurrences(std::string str,std::string subStr)115 int countOccurrences(std::string str, std::string subStr) {
116     size_t pos = 0;
117     int occurrences = 0;
118     while ((pos = str.find(subStr, pos)) != std::string::npos) {
119         ++occurrences;
120         pos += subStr.length();
121     }
122     return occurrences;
123 }
124 
125 }  // namespace
126 
127 namespace internal {
128 
129 class IoPerfCollectionPeer {
130 public:
IoPerfCollectionPeer(sp<IoPerfCollection> collector)131     explicit IoPerfCollectionPeer(sp<IoPerfCollection> collector) :
132           mCollector(collector),
133           mMockPackageInfoResolver(new MockPackageInfoResolver()) {
134         mCollector->mPackageInfoResolver = mMockPackageInfoResolver;
135     }
136 
137     IoPerfCollectionPeer() = delete;
~IoPerfCollectionPeer()138     ~IoPerfCollectionPeer() {
139         mCollector->terminate();
140         mCollector.clear();
141         mMockPackageInfoResolver.clear();
142     }
143 
init()144     Result<void> init() { return mCollector->init(); }
145 
setTopNStatsPerCategory(int value)146     void setTopNStatsPerCategory(int value) { mCollector->mTopNStatsPerCategory = value; }
147 
setTopNStatsPerSubcategory(int value)148     void setTopNStatsPerSubcategory(int value) { mCollector->mTopNStatsPerSubcategory = value; }
149 
injectUidToPackageNameMapping(std::unordered_map<uid_t,std::string> mapping)150     void injectUidToPackageNameMapping(std::unordered_map<uid_t, std::string> mapping) {
151         EXPECT_CALL(*mMockPackageInfoResolver, getPackageNamesForUids(_))
152                 .WillRepeatedly(Return(mapping));
153     }
154 
getBoottimeCollectionInfo()155     const CollectionInfo& getBoottimeCollectionInfo() {
156         Mutex::Autolock lock(mCollector->mMutex);
157         return mCollector->mBoottimeCollection;
158     }
159 
getPeriodicCollectionInfo()160     const CollectionInfo& getPeriodicCollectionInfo() {
161         Mutex::Autolock lock(mCollector->mMutex);
162         return mCollector->mPeriodicCollection;
163     }
164 
getCustomCollectionInfo()165     const CollectionInfo& getCustomCollectionInfo() {
166         Mutex::Autolock lock(mCollector->mMutex);
167         return mCollector->mCustomCollection;
168     }
169 
170 private:
171     sp<IoPerfCollection> mCollector;
172     sp<MockPackageInfoResolver> mMockPackageInfoResolver;
173 };
174 
175 }  // namespace internal
176 
TEST(IoPerfCollectionTest,TestBoottimeCollection)177 TEST(IoPerfCollectionTest, TestBoottimeCollection) {
178     sp<MockUidIoStats> mockUidIoStats = new MockUidIoStats();
179     sp<MockProcStat> mockProcStat = new MockProcStat();
180     sp<MockProcPidStat> mockProcPidStat = new MockProcPidStat();
181 
182     sp<IoPerfCollection> collector = new IoPerfCollection();
183     internal::IoPerfCollectionPeer collectorPeer(collector);
184 
185     ASSERT_RESULT_OK(collectorPeer.init());
186 
187     const std::unordered_map<uid_t, UidIoUsage> uidIoUsages({
188             {1009, {.uid = 1009, .ios = {0, 14000, 0, 16000, 0, 100}}},
189     });
190     const ProcStatInfo procStatInfo{
191             /*stats=*/{2900, 7900, 4900, 8900, /*ioWaitTime=*/5900, 6966, 7980, 0, 0, 2930},
192             /*runnableCnt=*/100,
193             /*ioBlockedCnt=*/57,
194     };
195     const std::vector<ProcessStats> processStats({
196             {.tgid = 100,
197              .uid = 1009,
198              .process = {100, "disk I/O", "D", 1, 11000, 1, 234},
199              .threads = {{100, {100, "mount", "D", 1, 11000, 1, 234}}}},
200     });
201 
202     EXPECT_CALL(*mockUidIoStats, deltaStats()).WillOnce(Return(uidIoUsages));
203     EXPECT_CALL(*mockProcStat, deltaStats()).WillOnce(Return(procStatInfo));
204     EXPECT_CALL(*mockProcPidStat, deltaStats()).WillOnce(Return(processStats));
205 
206     const IoPerfRecord expected = {
207             .uidIoPerfData = {.topNReads = {{0, "mount", {0, 14000}, {0, 100}}},
208                               .topNWrites = {{0, "mount", {0, 16000}, {0, 100}}},
209                               .total = {{0, 14000}, {0, 16000}, {0, 100}}},
210             .systemIoPerfData = {5900, 48376, 57, 157},
211             .processIoPerfData =
212                     {.topNIoBlockedUids = {{0, "mount", 1, {{"disk I/O", 1}}}},
213                      .topNIoBlockedUidsTotalTaskCnt = {1},
214                      .topNMajorFaultUids = {{0, "mount", 11000, {{"disk I/O", 11000}}}},
215                      .totalMajorFaults = 11000,
216                      .majorFaultsPercentChange = 0},
217     };
218     collectorPeer.injectUidToPackageNameMapping({{1009, "mount"}});
219 
220     time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
221     ASSERT_RESULT_OK(
222             collector->onBoottimeCollection(now, mockUidIoStats, mockProcStat, mockProcPidStat));
223 
224     const CollectionInfo& collectionInfo = collectorPeer.getBoottimeCollectionInfo();
225 
226     ASSERT_EQ(collectionInfo.maxCacheSize, std::numeric_limits<std::size_t>::max());
227     ASSERT_EQ(collectionInfo.records.size(), 1);
228     ASSERT_TRUE(isEqual(collectionInfo.records[0], expected))
229             << "Boottime collection record doesn't match.\nExpected:\n"
230             << toString(expected) << "\nActual:\n"
231             << toString(collectionInfo.records[0]);
232 
233     TemporaryFile dump;
234     ASSERT_RESULT_OK(collector->onDump(dump.fd));
235 
236     lseek(dump.fd, 0, SEEK_SET);
237     std::string dumpContents;
238     ASSERT_TRUE(ReadFdToString(dump.fd, &dumpContents));
239     ASSERT_FALSE(dumpContents.empty());
240 
241     ASSERT_EQ(countOccurrences(dumpContents, kEmptyCollectionMessage), 1)
242             << "Only periodic collection should be not collected. Dump contents: " << dumpContents;
243 }
244 
TEST(IoPerfCollectionTest,TestPeriodicCollection)245 TEST(IoPerfCollectionTest, TestPeriodicCollection) {
246     sp<MockUidIoStats> mockUidIoStats = new MockUidIoStats();
247     sp<MockProcStat> mockProcStat = new MockProcStat();
248     sp<MockProcPidStat> mockProcPidStat = new MockProcPidStat();
249 
250     sp<IoPerfCollection> collector = new IoPerfCollection();
251     internal::IoPerfCollectionPeer collectorPeer(collector);
252 
253     ASSERT_RESULT_OK(collectorPeer.init());
254 
255     const std::unordered_map<uid_t, UidIoUsage> uidIoUsages({
256             {1009, {.uid = 1009, .ios = {0, 14000, 0, 16000, 0, 100}}},
257     });
258     const ProcStatInfo procStatInfo{
259             /*stats=*/{2900, 7900, 4900, 8900, /*ioWaitTime=*/5900, 6966, 7980, 0, 0, 2930},
260             /*runnableCnt=*/100,
261             /*ioBlockedCnt=*/57,
262     };
263     const std::vector<ProcessStats> processStats({
264             {.tgid = 100,
265              .uid = 1009,
266              .process = {100, "disk I/O", "D", 1, 11000, 1, 234},
267              .threads = {{100, {100, "mount", "D", 1, 11000, 1, 234}}}},
268     });
269 
270     EXPECT_CALL(*mockUidIoStats, deltaStats()).WillOnce(Return(uidIoUsages));
271     EXPECT_CALL(*mockProcStat, deltaStats()).WillOnce(Return(procStatInfo));
272     EXPECT_CALL(*mockProcPidStat, deltaStats()).WillOnce(Return(processStats));
273 
274     const IoPerfRecord expected = {
275             .uidIoPerfData = {.topNReads = {{0, "mount", {0, 14000}, {0, 100}}},
276                               .topNWrites = {{0, "mount", {0, 16000}, {0, 100}}},
277                               .total = {{0, 14000}, {0, 16000}, {0, 100}}},
278             .systemIoPerfData = {5900, 48376, 57, 157},
279             .processIoPerfData =
280                     {.topNIoBlockedUids = {{0, "mount", 1, {{"disk I/O", 1}}}},
281                      .topNIoBlockedUidsTotalTaskCnt = {1},
282                      .topNMajorFaultUids = {{0, "mount", 11000, {{"disk I/O", 11000}}}},
283                      .totalMajorFaults = 11000,
284                      .majorFaultsPercentChange = 0},
285     };
286 
287     collectorPeer.injectUidToPackageNameMapping({{1009, "mount"}});
288 
289     time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
290     ASSERT_RESULT_OK(collector->onPeriodicCollection(now, SystemState::NORMAL_MODE, mockUidIoStats,
291                                                      mockProcStat, mockProcPidStat));
292 
293     const CollectionInfo& collectionInfo = collectorPeer.getPeriodicCollectionInfo();
294 
295     ASSERT_EQ(collectionInfo.maxCacheSize,
296               static_cast<size_t>(sysprop::periodicCollectionBufferSize().value_or(
297                       kDefaultPeriodicCollectionBufferSize)));
298     ASSERT_EQ(collectionInfo.records.size(), 1);
299     ASSERT_TRUE(isEqual(collectionInfo.records[0], expected))
300             << "Periodic collection record doesn't match.\nExpected:\n"
301             << toString(expected) << "\nActual:\n"
302             << toString(collectionInfo.records[0]);
303 
304     TemporaryFile dump;
305     ASSERT_RESULT_OK(collector->onDump(dump.fd));
306 
307     lseek(dump.fd, 0, SEEK_SET);
308     std::string dumpContents;
309     ASSERT_TRUE(ReadFdToString(dump.fd, &dumpContents));
310     ASSERT_FALSE(dumpContents.empty());
311 
312     ASSERT_EQ(countOccurrences(dumpContents, kEmptyCollectionMessage), 1)
313             << "Only boot-time collection should be not collected. Dump contents: " << dumpContents;
314 }
315 
TEST(IoPerfCollectionTest,TestCustomCollection)316 TEST(IoPerfCollectionTest, TestCustomCollection) {
317     sp<MockUidIoStats> mockUidIoStats = new MockUidIoStats();
318     sp<MockProcStat> mockProcStat = new MockProcStat();
319     sp<MockProcPidStat> mockProcPidStat = new MockProcPidStat();
320 
321     sp<IoPerfCollection> collector = new IoPerfCollection();
322     internal::IoPerfCollectionPeer collectorPeer(collector);
323 
324     ASSERT_RESULT_OK(collectorPeer.init());
325 
326     // Filter by package name should ignore this limit.
327     collectorPeer.setTopNStatsPerCategory(1);
328 
329     const std::unordered_map<uid_t, UidIoUsage> uidIoUsages({
330             {1009, {.uid = 1009, .ios = {0, 14000, 0, 16000, 0, 100}}},
331             {2001, {.uid = 2001, .ios = {0, 3400, 0, 6700, 0, 200}}},
332             {3456, {.uid = 3456, .ios = {0, 4200, 0, 5600, 0, 300}}},
333     });
334     const ProcStatInfo procStatInfo{
335             /*stats=*/{2900, 7900, 4900, 8900, /*ioWaitTime=*/5900, 6966, 7980, 0, 0, 2930},
336             /*runnableCnt=*/100,
337             /*ioBlockedCnt=*/57,
338     };
339     const std::vector<ProcessStats> processStats({
340             {.tgid = 100,
341              .uid = 1009,
342              .process = {100, "cts_test", "D", 1, 50900, 2, 234},
343              .threads = {{100, {100, "cts_test", "D", 1, 50900, 1, 234}},
344                          {200, {200, "cts_test_2", "D", 1, 0, 1, 290}}}},
345             {.tgid = 1000,
346              .uid = 2001,
347              .process = {1000, "system_server", "D", 1, 1234, 1, 345},
348              .threads = {{1000, {1000, "system_server", "D", 1, 1234, 1, 345}}}},
349             {.tgid = 4000,
350              .uid = 3456,
351              .process = {4000, "random_process", "D", 1, 3456, 1, 890},
352              .threads = {{4000, {4000, "random_process", "D", 1, 50900, 1, 890}}}},
353     });
354 
355     EXPECT_CALL(*mockUidIoStats, deltaStats()).WillOnce(Return(uidIoUsages));
356     EXPECT_CALL(*mockProcStat, deltaStats()).WillOnce(Return(procStatInfo));
357     EXPECT_CALL(*mockProcPidStat, deltaStats()).WillOnce(Return(processStats));
358     const IoPerfRecord expected = {
359             .uidIoPerfData = {.topNReads = {{.userId = 0,
360                                              .packageName = "android.car.cts",
361                                              .bytes = {0, 14000},
362                                              .fsync = {0, 100}},
363                                             {.userId = 0,
364                                              .packageName = "system_server",
365                                              .bytes = {0, 3400},
366                                              .fsync = {0, 200}}},
367                               .topNWrites = {{.userId = 0,
368                                               .packageName = "android.car.cts",
369                                               .bytes = {0, 16000},
370                                               .fsync = {0, 100}},
371                                              {.userId = 0,
372                                               .packageName = "system_server",
373                                               .bytes = {0, 6700},
374                                               .fsync = {0, 200}}},
375                               .total = {{0, 21600}, {0, 28300}, {0, 600}}},
376             .systemIoPerfData = {.cpuIoWaitTime = 5900,
377                                  .totalCpuTime = 48376,
378                                  .ioBlockedProcessesCnt = 57,
379                                  .totalProcessesCnt = 157},
380             .processIoPerfData =
381                     {.topNIoBlockedUids = {{0, "android.car.cts", 2, {{"cts_test", 2}}},
382                                            {0, "system_server", 1, {{"system_server", 1}}}},
383                      .topNIoBlockedUidsTotalTaskCnt = {2, 1},
384                      .topNMajorFaultUids = {{0, "android.car.cts", 50900, {{"cts_test", 50900}}},
385                                             {0, "system_server", 1234, {{"system_server", 1234}}}},
386                      .totalMajorFaults = 55590,
387                      .majorFaultsPercentChange = 0},
388     };
389     collectorPeer.injectUidToPackageNameMapping({
390             {1009, "android.car.cts"},
391             {2001, "system_server"},
392             {3456, "random_process"},
393     });
394 
395     time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
396     ASSERT_RESULT_OK(collector->onCustomCollection(now, SystemState::NORMAL_MODE,
397                                                    {"android.car.cts", "system_server"},
398                                                    mockUidIoStats, mockProcStat, mockProcPidStat));
399 
400     const CollectionInfo& collectionInfo = collectorPeer.getCustomCollectionInfo();
401 
402     EXPECT_EQ(collectionInfo.maxCacheSize, std::numeric_limits<std::size_t>::max());
403     ASSERT_EQ(collectionInfo.records.size(), 1);
404     ASSERT_TRUE(isEqual(collectionInfo.records[0], expected))
405             << "Custom collection record doesn't match.\nExpected:\n"
406             << toString(expected) << "\nActual:\n"
407             << toString(collectionInfo.records[0]);
408 
409     TemporaryFile customDump;
410     ASSERT_RESULT_OK(collector->onCustomCollectionDump(customDump.fd));
411 
412     lseek(customDump.fd, 0, SEEK_SET);
413     std::string customDumpContents;
414     ASSERT_TRUE(ReadFdToString(customDump.fd, &customDumpContents));
415     ASSERT_FALSE(customDumpContents.empty());
416     ASSERT_EQ(countOccurrences(customDumpContents, kEmptyCollectionMessage), 0)
417             << "Custom collection should be reported. Dump contents: " << customDumpContents;
418 
419     // Should clear the cache.
420     ASSERT_RESULT_OK(collector->onCustomCollectionDump(-1));
421 
422     const CollectionInfo& emptyCollectionInfo = collectorPeer.getCustomCollectionInfo();
423     EXPECT_TRUE(emptyCollectionInfo.records.empty());
424     EXPECT_EQ(emptyCollectionInfo.maxCacheSize, std::numeric_limits<std::size_t>::max());
425 }
426 
TEST(IoPerfCollectionTest,TestUidIoStatsGreaterThanTopNStatsLimit)427 TEST(IoPerfCollectionTest, TestUidIoStatsGreaterThanTopNStatsLimit) {
428     std::unordered_map<uid_t, UidIoUsage> uidIoUsages({
429             {1001234, {.uid = 1001234, .ios = {3000, 0, 500, 0, 20, 0}}},
430             {1005678, {.uid = 1005678, .ios = {30, 100, 50, 200, 45, 60}}},
431             {1009, {.uid = 1009, .ios = {0, 20000, 0, 30000, 0, 300}}},
432             {1001000, {.uid = 1001000, .ios = {2000, 200, 1000, 100, 50, 10}}},
433     });
434     sp<MockUidIoStats> mockUidIoStats = new MockUidIoStats();
435     EXPECT_CALL(*mockUidIoStats, deltaStats()).WillOnce(Return(uidIoUsages));
436 
437     struct UidIoPerfData expectedUidIoPerfData = {
438             .topNReads = {{.userId = 0,  // uid: 1009
439                            .packageName = "mount",
440                            .bytes = {0, 20000},
441                            .fsync = {0, 300}},
442                           {.userId = 10,  // uid: 1001234
443                            .packageName = "1001234",
444                            .bytes = {3000, 0},
445                            .fsync = {20, 0}}},
446             .topNWrites = {{.userId = 0,  // uid: 1009
447                             .packageName = "mount",
448                             .bytes = {0, 30000},
449                             .fsync = {0, 300}},
450                            {.userId = 10,  // uid: 1001000
451                             .packageName = "shared:android.uid.system",
452                             .bytes = {1000, 100},
453                             .fsync = {50, 10}}},
454             .total = {{5030, 20300}, {1550, 30300}, {115, 370}},
455     };
456 
457     IoPerfCollection collector;
458     collector.mTopNStatsPerCategory = 2;
459 
460     sp<MockPackageInfoResolver> mockPackageInfoResolver = new MockPackageInfoResolver();
461     collector.mPackageInfoResolver = mockPackageInfoResolver;
462     EXPECT_CALL(*mockPackageInfoResolver, getPackageNamesForUids(_))
463             .WillRepeatedly(Return<std::unordered_map<uid_t, std::string>>(
464                     {{1009, "mount"}, {1001000, "shared:android.uid.system"}}));
465 
466     struct UidIoPerfData actualUidIoPerfData = {};
467     collector.processUidIoPerfData({}, mockUidIoStats, &actualUidIoPerfData);
468 
469     EXPECT_TRUE(isEqual(expectedUidIoPerfData, actualUidIoPerfData))
470         << "First snapshot doesn't match.\nExpected:\n"
471         << toString(expectedUidIoPerfData) << "\nActual:\n"
472         << toString(actualUidIoPerfData);
473 
474     uidIoUsages = {
475             {1001234, {.uid = 1001234, .ios = {4000, 0, 450, 0, 25, 0}}},
476             {1005678, {.uid = 1005678, .ios = {10, 900, 0, 400, 5, 10}}},
477             {1003456, {.uid = 1003456, .ios = {200, 0, 300, 0, 50, 0}}},
478             {1001000, {.uid = 1001000, .ios = {0, 0, 0, 0, 0, 0}}},
479     };
480     EXPECT_CALL(*mockUidIoStats, deltaStats()).WillOnce(Return(uidIoUsages));
481 
482     expectedUidIoPerfData = {
483             .topNReads = {{.userId = 10,  // uid: 1001234
484                            .packageName = "1001234",
485                            .bytes = {4000, 0},
486                            .fsync = {25, 0}},
487                           {.userId = 10,  // uid: 1005678
488                            .packageName = "1005678",
489                            .bytes = {10, 900},
490                            .fsync = {5, 10}}},
491             .topNWrites = {{.userId = 10,  // uid: 1001234
492                             .packageName = "1001234",
493                             .bytes = {450, 0},
494                             .fsync = {25, 0}},
495                            {.userId = 10,  // uid: 1005678
496                             .packageName = "1005678",
497                             .bytes = {0, 400},
498                             .fsync = {5, 10}}},
499             .total = {{4210, 900}, {750, 400}, {80, 10}},
500     };
501     actualUidIoPerfData = {};
502     collector.processUidIoPerfData({}, mockUidIoStats, &actualUidIoPerfData);
503 
504     EXPECT_TRUE(isEqual(expectedUidIoPerfData, actualUidIoPerfData))
505         << "Second snapshot doesn't match.\nExpected:\n"
506         << toString(expectedUidIoPerfData) << "\nActual:\n"
507         << toString(actualUidIoPerfData);
508 }
509 
TEST(IoPerfCollectionTest,TestUidIOStatsLessThanTopNStatsLimit)510 TEST(IoPerfCollectionTest, TestUidIOStatsLessThanTopNStatsLimit) {
511     const std::unordered_map<uid_t, UidIoUsage> uidIoUsages(
512             {{1001234, {.uid = 1001234, .ios = {3000, 0, 500, 0, 20, 0}}}});
513 
514     const struct UidIoPerfData expectedUidIoPerfData = {
515             .topNReads = {{.userId = 10,
516                            .packageName = "1001234",
517                            .bytes = {3000, 0},
518                            .fsync = {20, 0}}},
519             .topNWrites =
520                     {{.userId = 10, .packageName = "1001234", .bytes = {500, 0}, .fsync = {20, 0}}},
521             .total = {{3000, 0}, {500, 0}, {20, 0}},
522     };
523 
524     sp<MockUidIoStats> mockUidIoStats = new MockUidIoStats();
525     EXPECT_CALL(*mockUidIoStats, deltaStats()).WillOnce(Return(uidIoUsages));
526 
527     IoPerfCollection collector;
528     collector.mTopNStatsPerCategory = 10;
529 
530     struct UidIoPerfData actualUidIoPerfData = {};
531     collector.processUidIoPerfData({}, mockUidIoStats, &actualUidIoPerfData);
532 
533     EXPECT_TRUE(isEqual(expectedUidIoPerfData, actualUidIoPerfData))
534         << "Collected data doesn't match.\nExpected:\n"
535         << toString(expectedUidIoPerfData) << "\nActual:\n"
536         << toString(actualUidIoPerfData);
537 }
538 
TEST(IoPerfCollectionTest,TestProcessSystemIoPerfData)539 TEST(IoPerfCollectionTest, TestProcessSystemIoPerfData) {
540     const ProcStatInfo procStatInfo(
541             /*stats=*/{6200, 5700, 1700, 3100, 1100, 5200, 3900, 0, 0, 0},
542             /*runnableCnt=*/17,
543             /*ioBlockedCnt=*/5);
544     struct SystemIoPerfData expectedSystemIoPerfData = {
545             .cpuIoWaitTime = 1100,
546             .totalCpuTime = 26900,
547             .ioBlockedProcessesCnt = 5,
548             .totalProcessesCnt = 22,
549     };
550 
551     sp<MockProcStat> mockProcStat = new MockProcStat();
552     EXPECT_CALL(*mockProcStat, deltaStats()).WillOnce(Return(procStatInfo));
553 
554     IoPerfCollection collector;
555     struct SystemIoPerfData actualSystemIoPerfData = {};
556     collector.processSystemIoPerfData(mockProcStat, &actualSystemIoPerfData);
557 
558     EXPECT_TRUE(isEqual(expectedSystemIoPerfData, actualSystemIoPerfData))
559             << "Expected:\n"
560             << toString(expectedSystemIoPerfData) << "\nActual:\n"
561             << toString(actualSystemIoPerfData);
562 }
563 
TEST(IoPerfCollectionTest,TestProcPidContentsGreaterThanTopNStatsLimit)564 TEST(IoPerfCollectionTest, TestProcPidContentsGreaterThanTopNStatsLimit) {
565     const std::vector<ProcessStats> firstProcessStats({
566             {.tgid = 1,
567              .uid = 0,
568              .process = {1, "init", "S", 0, 220, 2, 0},
569              .threads = {{1, {1, "init", "S", 0, 200, 2, 0}},
570                          {453, {453, "init", "S", 0, 20, 2, 275}}}},
571             {.tgid = 2456,
572              .uid = 1001000,
573              .process = {2456, "system_server", "R", 1, 6000, 3, 1000},
574              .threads = {{2456, {2456, "system_server", "R", 1, 1000, 3, 1000}},
575                          {3456, {3456, "system_server", "S", 1, 3000, 3, 2300}},
576                          {4789, {4789, "system_server", "D", 1, 2000, 3, 4500}}}},
577             {.tgid = 7890,
578              .uid = 1001000,
579              .process = {7890, "logd", "D", 1, 15000, 3, 2345},
580              .threads = {{7890, {7890, "logd", "D", 1, 10000, 3, 2345}},
581                          {8978, {8978, "logd", "D", 1, 1000, 3, 2500}},
582                          {12890, {12890, "logd", "D", 1, 500, 3, 2900}}}},
583             {.tgid = 18902,
584              .uid = 1009,
585              .process = {18902, "disk I/O", "D", 1, 45678, 3, 897654},
586              .threads = {{18902, {18902, "disk I/O", "D", 1, 30000, 3, 897654}},
587                          {21345, {21345, "disk I/O", "D", 1, 15000, 3, 904000}},
588                          {32452, {32452, "disk I/O", "D", 1, 678, 3, 1007000}}}},
589             {.tgid = 28900,
590              .uid = 1001234,
591              .process = {28900, "tombstoned", "D", 1, 89765, 1, 2345671},
592              .threads = {{28900, {28900, "tombstoned", "D", 1, 89765, 1, 2345671}}}},
593     });
594     sp<MockProcPidStat> mockProcPidStat = new MockProcPidStat();
595     EXPECT_CALL(*mockProcPidStat, deltaStats()).WillOnce(Return(firstProcessStats));
596 
597     struct ProcessIoPerfData expectedProcessIoPerfData = {
598             .topNIoBlockedUids = {{.userId = 10,  // uid: 1001000
599                                    .packageName = "shared:android.uid.system",
600                                    .count = 4,
601                                    .topNProcesses = {{"logd", 3}, {"system_server", 1}}},
602                                   {.userId = 0,
603                                    .packageName = "mount",
604                                    .count = 3,
605                                    .topNProcesses = {{"disk I/O", 3}}}},
606             .topNIoBlockedUidsTotalTaskCnt = {6, 3},
607             .topNMajorFaultUids = {{.userId = 10,  // uid: 1001234
608                                     .packageName = "1001234",
609                                     .count = 89765,
610                                     .topNProcesses = {{"tombstoned", 89765}}},
611                                    {.userId = 0,  // uid: 1009
612                                     .packageName = "mount",
613                                     .count = 45678,
614                                     .topNProcesses = {{"disk I/O", 45678}}}},
615             .totalMajorFaults = 156663,
616             .majorFaultsPercentChange = 0.0,
617     };
618 
619     IoPerfCollection collector;
620     collector.mTopNStatsPerCategory = 2;
621     collector.mTopNStatsPerSubcategory = 2;
622 
623     sp<MockPackageInfoResolver> mockPackageInfoResolver = new MockPackageInfoResolver();
624     collector.mPackageInfoResolver = mockPackageInfoResolver;
625     EXPECT_CALL(*mockPackageInfoResolver, getPackageNamesForUids(_))
626             .WillRepeatedly(Return<std::unordered_map<uid_t, std::string>>(
627                     {{0, "root"}, {1009, "mount"}, {1001000, "shared:android.uid.system"}}));
628 
629     struct ProcessIoPerfData actualProcessIoPerfData = {};
630     collector.processProcessIoPerfDataLocked({}, mockProcPidStat, &actualProcessIoPerfData);
631 
632     EXPECT_TRUE(isEqual(expectedProcessIoPerfData, actualProcessIoPerfData))
633             << "First snapshot doesn't match.\nExpected:\n"
634             << toString(expectedProcessIoPerfData) << "\nActual:\n"
635             << toString(actualProcessIoPerfData);
636 
637     const std::vector<ProcessStats> secondProcessStats({
638             {.tgid = 1,
639              .uid = 0,
640              .process = {1, "init", "S", 0, 660, 2, 0},
641              .threads = {{1, {1, "init", "S", 0, 600, 2, 0}},
642                          {453, {453, "init", "S", 0, 60, 2, 275}}}},
643             {.tgid = 2546,
644              .uid = 1001000,
645              .process = {2546, "system_server", "R", 1, 12000, 3, 1000},
646              .threads = {{2456, {2456, "system_server", "R", 1, 2000, 3, 1000}},
647                          {3456, {3456, "system_server", "S", 1, 6000, 3, 2300}},
648                          {4789, {4789, "system_server", "D", 1, 4000, 3, 4500}}}},
649     });
650     EXPECT_CALL(*mockProcPidStat, deltaStats()).WillOnce(Return(secondProcessStats));
651     expectedProcessIoPerfData = {
652             .topNIoBlockedUids = {{.userId = 10,  // uid: 1001000
653                                    .packageName = "shared:android.uid.system",
654                                    .count = 1,
655                                    .topNProcesses = {{"system_server", 1}}}},
656             .topNIoBlockedUidsTotalTaskCnt = {3},
657             .topNMajorFaultUids = {{.userId = 10,  // uid: 1001000
658                                     .packageName = "shared:android.uid.system",
659                                     .count = 12000,
660                                     .topNProcesses = {{"system_server", 12000}}},
661                                    {.userId = 0,  // uid: 0
662                                     .packageName = "root",
663                                     .count = 660,
664                                     .topNProcesses = {{"init", 660}}}},
665             .totalMajorFaults = 12660,
666             .majorFaultsPercentChange = ((12660.0 - 156663.0) / 156663.0) * 100,
667     };
668 
669     actualProcessIoPerfData = {};
670     collector.processProcessIoPerfDataLocked({}, mockProcPidStat, &actualProcessIoPerfData);
671 
672     EXPECT_TRUE(isEqual(expectedProcessIoPerfData, actualProcessIoPerfData))
673             << "Second snapshot doesn't match.\nExpected:\n"
674             << toString(expectedProcessIoPerfData) << "\nActual:\n"
675             << toString(actualProcessIoPerfData);
676 }
677 
TEST(IoPerfCollectionTest,TestProcPidContentsLessThanTopNStatsLimit)678 TEST(IoPerfCollectionTest, TestProcPidContentsLessThanTopNStatsLimit) {
679     const std::vector<ProcessStats> processStats({
680             {.tgid = 1,
681              .uid = 0,
682              .process = {1, "init", "S", 0, 880, 2, 0},
683              .threads = {{1, {1, "init", "S", 0, 800, 2, 0}},
684                          {453, {453, "init", "S", 0, 80, 2, 275}}}},
685     });
686     sp<MockProcPidStat> mockProcPidStat = new MockProcPidStat();
687     EXPECT_CALL(*mockProcPidStat, deltaStats()).WillOnce(Return(processStats));
688 
689     struct ProcessIoPerfData expectedProcessIoPerfData = {
690             .topNMajorFaultUids = {{.userId = 0,  // uid: 0
691                                     .packageName = "root",
692                                     .count = 880,
693                                     .topNProcesses = {{"init", 880}}}},
694             .totalMajorFaults = 880,
695             .majorFaultsPercentChange = 0.0,
696     };
697 
698     IoPerfCollection collector;
699     collector.mTopNStatsPerCategory = 5;
700     collector.mTopNStatsPerSubcategory = 3;
701 
702     sp<MockPackageInfoResolver> mockPackageInfoResolver = new MockPackageInfoResolver();
703     collector.mPackageInfoResolver = mockPackageInfoResolver;
704     EXPECT_CALL(*mockPackageInfoResolver, getPackageNamesForUids(_))
705             .WillRepeatedly(Return<std::unordered_map<uid_t, std::string>>({{0, "root"}}));
706 
707     struct ProcessIoPerfData actualProcessIoPerfData = {};
708     collector.processProcessIoPerfDataLocked({}, mockProcPidStat, &actualProcessIoPerfData);
709 
710     EXPECT_TRUE(isEqual(expectedProcessIoPerfData, actualProcessIoPerfData))
711             << "proc pid contents don't match.\nExpected:\n"
712             << toString(expectedProcessIoPerfData) << "\nActual:\n"
713             << toString(actualProcessIoPerfData);
714 }
715 
716 }  // namespace watchdog
717 }  // namespace automotive
718 }  // namespace android
719