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