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 "MockProcStat.h"
19 #include "MockUidStatsCollector.h"
20 #include "MockWatchdogServiceHelper.h"
21 #include "PackageInfoTestUtils.h"
22
23 #include <WatchdogProperties.sysprop.h>
24 #include <android-base/file.h>
25 #include <gmock/gmock.h>
26 #include <utils/RefBase.h>
27
28 #include <sys/types.h>
29 #include <unistd.h>
30
31 #include <string>
32 #include <type_traits>
33 #include <vector>
34
35 namespace android {
36 namespace automotive {
37 namespace watchdog {
38
39 using ::android::RefBase;
40 using ::android::sp;
41 using ::android::automotive::watchdog::internal::PackageInfo;
42 using ::android::base::ReadFdToString;
43 using ::android::base::Result;
44 using ::testing::_;
45 using ::testing::AllOf;
46 using ::testing::ElementsAreArray;
47 using ::testing::Eq;
48 using ::testing::ExplainMatchResult;
49 using ::testing::Field;
50 using ::testing::IsSubsetOf;
51 using ::testing::Matcher;
52 using ::testing::Return;
53 using ::testing::Test;
54 using ::testing::UnorderedElementsAreArray;
55 using ::testing::VariantWith;
56
57 namespace {
58
59 MATCHER_P(IoStatsEq, expected, "") {
60 return ExplainMatchResult(AllOf(Field("bytes", &UserPackageStats::IoStats::bytes,
61 ElementsAreArray(expected.bytes)),
62 Field("fsync", &UserPackageStats::IoStats::fsync,
63 ElementsAreArray(expected.fsync))),
64 arg, result_listener);
65 }
66
67 MATCHER_P(ProcessCountEq, expected, "") {
68 return ExplainMatchResult(AllOf(Field("comm", &UserPackageStats::ProcStats::ProcessCount::comm,
69 Eq(expected.comm)),
70 Field("count",
71 &UserPackageStats::ProcStats::ProcessCount::count,
72 Eq(expected.count))),
73 arg, result_listener);
74 }
75
76 MATCHER_P(ProcStatsEq, expected, "") {
77 std::vector<Matcher<const UserPackageStats::ProcStats::ProcessCount&>> processCountMatchers;
78 for (const auto& processCount : expected.topNProcesses) {
79 processCountMatchers.push_back(ProcessCountEq(processCount));
80 }
81 return ExplainMatchResult(AllOf(Field("count", &UserPackageStats::ProcStats::count,
82 Eq(expected.count)),
83 Field("topNProcesses",
84 &UserPackageStats::ProcStats::topNProcesses,
85 ElementsAreArray(processCountMatchers))),
86 arg, result_listener);
87 }
88
89 MATCHER_P(UserPackageStatsEq, expected, "") {
90 const auto uidMatcher = Field("uid", &UserPackageStats::uid, Eq(expected.uid));
91 const auto packageNameMatcher =
92 Field("genericPackageName", &UserPackageStats::genericPackageName,
93 Eq(expected.genericPackageName));
94 return std::visit(
__anoncab421ad0202(const auto& stats) 95 [&](const auto& stats) -> bool {
96 using T = std::decay_t<decltype(stats)>;
97 if constexpr (std::is_same_v<T, UserPackageStats::IoStats>) {
98 return ExplainMatchResult(AllOf(uidMatcher, packageNameMatcher,
99 Field("stats:IoStats", &UserPackageStats::stats,
100 VariantWith<UserPackageStats::IoStats>(
101 IoStatsEq(stats)))),
102 arg, result_listener);
103 } else if constexpr (std::is_same_v<T, UserPackageStats::ProcStats>) {
104 return ExplainMatchResult(AllOf(uidMatcher, packageNameMatcher,
105 Field("stats:ProcStats",
106 &UserPackageStats::stats,
107 VariantWith<UserPackageStats::ProcStats>(
108 ProcStatsEq(stats)))),
109 arg, result_listener);
110 }
111 *result_listener << "Unexpected variant in UserPackageStats::stats";
112 return false;
113 },
114 expected.stats);
115 }
116
117 MATCHER_P(UserPackageSummaryStatsEq, expected, "") {
__anoncab421ad0302(const std::vector<UserPackageStats>& stats) 118 const auto& userPackageStatsMatchers = [&](const std::vector<UserPackageStats>& stats) {
119 std::vector<Matcher<const UserPackageStats&>> matchers;
120 for (const auto& curStats : stats) {
121 matchers.push_back(UserPackageStatsEq(curStats));
122 }
123 return ElementsAreArray(matchers);
124 };
__anoncab421ad0402(const int64_t expected[][UID_STATES]) 125 const auto& totalIoStatsArrayMatcher = [&](const int64_t expected[][UID_STATES]) {
126 std::vector<Matcher<const int64_t[UID_STATES]>> matchers;
127 for (int i = 0; i < METRIC_TYPES; ++i) {
128 matchers.push_back(ElementsAreArray(expected[i], UID_STATES));
129 }
130 return ElementsAreArray(matchers);
131 };
132 return ExplainMatchResult(AllOf(Field("topNIoReads", &UserPackageSummaryStats::topNIoReads,
133 userPackageStatsMatchers(expected.topNIoReads)),
134 Field("topNIoWrites", &UserPackageSummaryStats::topNIoWrites,
135 userPackageStatsMatchers(expected.topNIoWrites)),
136 Field("topNIoBlocked", &UserPackageSummaryStats::topNIoBlocked,
137 userPackageStatsMatchers(expected.topNIoBlocked)),
138 Field("topNMajorFaults",
139 &UserPackageSummaryStats::topNMajorFaults,
140 userPackageStatsMatchers(expected.topNMajorFaults)),
141 Field("totalIoStats", &UserPackageSummaryStats::totalIoStats,
142 totalIoStatsArrayMatcher(expected.totalIoStats)),
143 Field("taskCountByUid",
144 &UserPackageSummaryStats::taskCountByUid,
145 IsSubsetOf(expected.taskCountByUid)),
146 Field("totalMajorFaults",
147 &UserPackageSummaryStats::totalMajorFaults,
148 Eq(expected.totalMajorFaults)),
149 Field("majorFaultsPercentChange",
150 &UserPackageSummaryStats::majorFaultsPercentChange,
151 Eq(expected.majorFaultsPercentChange))),
152 arg, result_listener);
153 }
154
155 MATCHER_P(SystemSummaryStatsEq, expected, "") {
156 return ExplainMatchResult(AllOf(Field("cpuIoWaitTime", &SystemSummaryStats::cpuIoWaitTime,
157 Eq(expected.cpuIoWaitTime)),
158 Field("totalCpuTime", &SystemSummaryStats::totalCpuTime,
159 Eq(expected.totalCpuTime)),
160 Field("ioBlockedProcessCount",
161 &SystemSummaryStats::ioBlockedProcessCount,
162 Eq(expected.ioBlockedProcessCount)),
163 Field("totalProcessCount",
164 &SystemSummaryStats::totalProcessCount,
165 Eq(expected.totalProcessCount))),
166 arg, result_listener);
167 }
168
169 MATCHER_P(PerfStatsRecordEq, expected, "") {
170 return ExplainMatchResult(AllOf(Field(&PerfStatsRecord::systemSummaryStats,
171 SystemSummaryStatsEq(expected.systemSummaryStats)),
172 Field(&PerfStatsRecord::userPackageSummaryStats,
173 UserPackageSummaryStatsEq(
174 expected.userPackageSummaryStats))),
175 arg, result_listener);
176 }
177
constructPerfStatsRecordMatchers(const std::vector<PerfStatsRecord> & records)178 const std::vector<Matcher<const PerfStatsRecord&>> constructPerfStatsRecordMatchers(
179 const std::vector<PerfStatsRecord>& records) {
180 std::vector<Matcher<const PerfStatsRecord&>> matchers;
181 for (const auto& record : records) {
182 matchers.push_back(PerfStatsRecordEq(record));
183 }
184 return matchers;
185 }
186
187 MATCHER_P(CollectionInfoEq, expected, "") {
188 return ExplainMatchResult(AllOf(Field("maxCacheSize", &CollectionInfo::maxCacheSize,
189 Eq(expected.maxCacheSize)),
190 Field("records", &CollectionInfo::records,
191 ElementsAreArray(constructPerfStatsRecordMatchers(
192 expected.records)))),
193 arg, result_listener);
194 }
195
countOccurrences(std::string str,std::string subStr)196 int countOccurrences(std::string str, std::string subStr) {
197 size_t pos = 0;
198 int occurrences = 0;
199 while ((pos = str.find(subStr, pos)) != std::string::npos) {
200 ++occurrences;
201 pos += subStr.length();
202 }
203 return occurrences;
204 }
205
sampleUidStats(int multiplier=1)206 std::tuple<std::vector<UidStats>, UserPackageSummaryStats> sampleUidStats(int multiplier = 1) {
207 /* The number of returned sample stats are less that the top N stats per category/sub-category.
208 * The top N stats per category/sub-category is set to % during test setup. Thus, the default
209 * testing behavior is # reported stats < top N stats.
210 */
211 const auto int64Multiplier = [&](int64_t bytes) -> int64_t {
212 return static_cast<int64_t>(bytes * multiplier);
213 };
214 const auto uint64Multiplier = [&](uint64_t count) -> uint64_t {
215 return static_cast<uint64_t>(count * multiplier);
216 };
217 std::vector<UidStats>
218 uidStats{{.packageInfo = constructPackageInfo("mount", 1009),
219 .ioStats = {/*fgRdBytes=*/0,
220 /*bgRdBytes=*/int64Multiplier(14'000),
221 /*fgWrBytes=*/0,
222 /*bgWrBytes=*/int64Multiplier(16'000),
223 /*fgFsync=*/0, /*bgFsync=*/int64Multiplier(100)},
224 .procStats = {.totalMajorFaults = uint64Multiplier(11'000),
225 .totalTasksCount = 1,
226 .ioBlockedTasksCount = 1,
227 .processStatsByPid =
228 {{/*pid=*/100,
229 {/*comm=*/"disk I/O", /*startTime=*/234,
230 /*totalMajorFaults=*/uint64Multiplier(11'000),
231 /*totalTasksCount=*/1,
232 /*ioBlockedTasksCount=*/1}}}}},
233 {.packageInfo =
234 constructPackageInfo("com.google.android.car.kitchensink", 1002001),
235 .ioStats = {/*fgRdBytes=*/0,
236 /*bgRdBytes=*/int64Multiplier(3'400),
237 /*fgWrBytes=*/0,
238 /*bgWrBytes=*/int64Multiplier(6'700),
239 /*fgFsync=*/0,
240 /*bgFsync=*/int64Multiplier(200)},
241 .procStats = {.totalMajorFaults = uint64Multiplier(22'445),
242 .totalTasksCount = 5,
243 .ioBlockedTasksCount = 3,
244 .processStatsByPid =
245 {{/*pid=*/1000,
246 {/*comm=*/"KitchenSinkApp", /*startTime=*/467,
247 /*totalMajorFaults=*/uint64Multiplier(12'345),
248 /*totalTasksCount=*/2,
249 /*ioBlockedTasksCount=*/1}},
250 {/*pid=*/1001,
251 {/*comm=*/"CTS", /*startTime=*/789,
252 /*totalMajorFaults=*/uint64Multiplier(10'100),
253 /*totalTasksCount=*/3,
254 /*ioBlockedTasksCount=*/2}}}}},
255 {.packageInfo = constructPackageInfo("", 1012345),
256 .ioStats = {/*fgRdBytes=*/int64Multiplier(1'000),
257 /*bgRdBytes=*/int64Multiplier(4'200),
258 /*fgWrBytes=*/int64Multiplier(300),
259 /*bgWrBytes=*/int64Multiplier(5'600),
260 /*fgFsync=*/int64Multiplier(600),
261 /*bgFsync=*/int64Multiplier(300)},
262 .procStats = {.totalMajorFaults = uint64Multiplier(50'900),
263 .totalTasksCount = 4,
264 .ioBlockedTasksCount = 2,
265 .processStatsByPid =
266 {{/*pid=*/2345,
267 {/*comm=*/"MapsApp", /*startTime=*/6789,
268 /*totalMajorFaults=*/uint64Multiplier(50'900),
269 /*totalTasksCount=*/4,
270 /*ioBlockedTasksCount=*/2}}}}},
271 {.packageInfo = constructPackageInfo("com.google.radio", 1015678),
272 .ioStats = {/*fgRdBytes=*/0,
273 /*bgRdBytes=*/0,
274 /*fgWrBytes=*/0,
275 /*bgWrBytes=*/0,
276 /*fgFsync=*/0, /*bgFsync=*/0},
277 .procStats = {.totalMajorFaults = 0,
278 .totalTasksCount = 4,
279 .ioBlockedTasksCount = 0,
280 .processStatsByPid = {
281 {/*pid=*/2345,
282 {/*comm=*/"RadioApp", /*startTime=*/19789,
283 /*totalMajorFaults=*/0,
284 /*totalTasksCount=*/4,
285 /*ioBlockedTasksCount=*/0}}}}}};
286
287 UserPackageSummaryStats userPackageSummaryStats{
288 .topNIoReads =
289 {{1009, "mount",
290 UserPackageStats::IoStats{{0, int64Multiplier(14'000)},
291 {0, int64Multiplier(100)}}},
292 {1012345, "1012345",
293 UserPackageStats::IoStats{{int64Multiplier(1'000), int64Multiplier(4'200)},
294 {int64Multiplier(600), int64Multiplier(300)}}},
295 {1002001, "com.google.android.car.kitchensink",
296 UserPackageStats::IoStats{{0, int64Multiplier(3'400)},
297 {0, int64Multiplier(200)}}}},
298 .topNIoWrites =
299 {{1009, "mount",
300 UserPackageStats::IoStats{{0, int64Multiplier(16'000)},
301 {0, int64Multiplier(100)}}},
302 {1002001, "com.google.android.car.kitchensink",
303 UserPackageStats::IoStats{{0, int64Multiplier(6'700)},
304 {0, int64Multiplier(200)}}},
305 {1012345, "1012345",
306 UserPackageStats::IoStats{{int64Multiplier(300), int64Multiplier(5'600)},
307 {int64Multiplier(600), int64Multiplier(300)}}}},
308 .topNIoBlocked = {{1002001, "com.google.android.car.kitchensink",
309 UserPackageStats::ProcStats{3, {{"CTS", 2}, {"KitchenSinkApp", 1}}}},
310 {1012345, "1012345",
311 UserPackageStats::ProcStats{2, {{"MapsApp", 2}}}},
312 {1009, "mount", UserPackageStats::ProcStats{1, {{"disk I/O", 1}}}}},
313 .topNMajorFaults =
314 {{1012345, "1012345",
315 UserPackageStats::ProcStats{uint64Multiplier(50'900),
316 {{"MapsApp", uint64Multiplier(50'900)}}}},
317 {1002001, "com.google.android.car.kitchensink",
318 UserPackageStats::ProcStats{uint64Multiplier(22'445),
319 {{"KitchenSinkApp", uint64Multiplier(12'345)},
320 {"CTS", uint64Multiplier(10'100)}}}},
321 {1009, "mount",
322 UserPackageStats::ProcStats{uint64Multiplier(11'000),
323 {{"disk I/O", uint64Multiplier(11'000)}}}}},
324 .totalIoStats = {{int64Multiplier(1'000), int64Multiplier(21'600)},
325 {int64Multiplier(300), int64Multiplier(28'300)},
326 {int64Multiplier(600), int64Multiplier(600)}},
327 .taskCountByUid = {{1009, 1}, {1002001, 5}, {1012345, 4}},
328 .totalMajorFaults = uint64Multiplier(84'345),
329 .majorFaultsPercentChange = 0.0,
330 };
331 return std::make_tuple(uidStats, userPackageSummaryStats);
332 }
333
sampleProcStat(int multiplier=1)334 std::tuple<ProcStatInfo, SystemSummaryStats> sampleProcStat(int multiplier = 1) {
335 const auto uint64Multiplier = [&](uint64_t bytes) -> uint64_t {
336 return static_cast<uint64_t>(bytes * multiplier);
337 };
338 const auto uint32Multiplier = [&](uint32_t bytes) -> uint32_t {
339 return static_cast<uint32_t>(bytes * multiplier);
340 };
341 ProcStatInfo procStatInfo{/*cpuStats=*/{uint64Multiplier(2'900), uint64Multiplier(7'900),
342 uint64Multiplier(4'900), uint64Multiplier(8'900),
343 /*ioWaitTime=*/uint64Multiplier(5'900),
344 uint64Multiplier(6'966), uint64Multiplier(7'980), 0, 0,
345 uint64Multiplier(2'930)},
346 /*runnableProcessCount=*/uint32Multiplier(100),
347 /*ioBlockedProcessCount=*/uint32Multiplier(57)};
348 SystemSummaryStats systemSummaryStats{/*cpuIoWaitTime=*/uint64Multiplier(5'900),
349 /*totalCpuTime=*/uint64Multiplier(48'376),
350 /*ioBlockedProcessCount=*/uint32Multiplier(57),
351 /*totalProcessCount=*/uint32Multiplier(157)};
352 return std::make_tuple(procStatInfo, systemSummaryStats);
353 }
354
355 } // namespace
356
357 namespace internal {
358
359 class IoPerfCollectionPeer : public RefBase {
360 public:
IoPerfCollectionPeer(sp<IoPerfCollection> collector)361 explicit IoPerfCollectionPeer(sp<IoPerfCollection> collector) : mCollector(collector) {}
362
363 IoPerfCollectionPeer() = delete;
~IoPerfCollectionPeer()364 ~IoPerfCollectionPeer() {
365 mCollector->terminate();
366 mCollector.clear();
367 }
368
init()369 Result<void> init() { return mCollector->init(); }
370
setTopNStatsPerCategory(int value)371 void setTopNStatsPerCategory(int value) { mCollector->mTopNStatsPerCategory = value; }
372
setTopNStatsPerSubcategory(int value)373 void setTopNStatsPerSubcategory(int value) { mCollector->mTopNStatsPerSubcategory = value; }
374
getBoottimeCollectionInfo()375 const CollectionInfo& getBoottimeCollectionInfo() {
376 Mutex::Autolock lock(mCollector->mMutex);
377 return mCollector->mBoottimeCollection;
378 }
379
getPeriodicCollectionInfo()380 const CollectionInfo& getPeriodicCollectionInfo() {
381 Mutex::Autolock lock(mCollector->mMutex);
382 return mCollector->mPeriodicCollection;
383 }
384
getCustomCollectionInfo()385 const CollectionInfo& getCustomCollectionInfo() {
386 Mutex::Autolock lock(mCollector->mMutex);
387 return mCollector->mCustomCollection;
388 }
389
390 private:
391 sp<IoPerfCollection> mCollector;
392 };
393
394 } // namespace internal
395
396 class IoPerfCollectionTest : public Test {
397 protected:
SetUp()398 void SetUp() override {
399 mMockUidStatsCollector = sp<MockUidStatsCollector>::make();
400 mMockProcStat = sp<MockProcStat>::make();
401 mCollector = sp<IoPerfCollection>::make();
402 mCollectorPeer = sp<internal::IoPerfCollectionPeer>::make(mCollector);
403 ASSERT_RESULT_OK(mCollectorPeer->init());
404 mCollectorPeer->setTopNStatsPerCategory(5);
405 mCollectorPeer->setTopNStatsPerSubcategory(5);
406 }
407
TearDown()408 void TearDown() override {
409 mMockUidStatsCollector.clear();
410 mMockProcStat.clear();
411 mCollector.clear();
412 mCollectorPeer.clear();
413 }
414
checkDumpContents(int wantedEmptyCollectionInstances)415 void checkDumpContents(int wantedEmptyCollectionInstances) {
416 TemporaryFile dump;
417 ASSERT_RESULT_OK(mCollector->onDump(dump.fd));
418
419 checkDumpFd(wantedEmptyCollectionInstances, dump.fd);
420 }
421
checkCustomDumpContents()422 void checkCustomDumpContents() {
423 TemporaryFile dump;
424 ASSERT_RESULT_OK(mCollector->onCustomCollectionDump(dump.fd));
425
426 checkDumpFd(/*wantedEmptyCollectionInstances=*/0, dump.fd);
427 }
428
429 private:
checkDumpFd(int wantedEmptyCollectionInstances,int fd)430 void checkDumpFd(int wantedEmptyCollectionInstances, int fd) {
431 lseek(fd, 0, SEEK_SET);
432 std::string dumpContents;
433 ASSERT_TRUE(ReadFdToString(fd, &dumpContents));
434 ASSERT_FALSE(dumpContents.empty());
435
436 ASSERT_EQ(countOccurrences(dumpContents, kEmptyCollectionMessage),
437 wantedEmptyCollectionInstances)
438 << "Dump contents: " << dumpContents;
439 }
440
441 protected:
442 sp<MockUidStatsCollector> mMockUidStatsCollector;
443 sp<MockProcStat> mMockProcStat;
444 sp<IoPerfCollection> mCollector;
445 sp<internal::IoPerfCollectionPeer> mCollectorPeer;
446 };
447
TEST_F(IoPerfCollectionTest,TestOnBoottimeCollection)448 TEST_F(IoPerfCollectionTest, TestOnBoottimeCollection) {
449 const auto [uidStats, userPackageSummaryStats] = sampleUidStats();
450 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
451
452 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
453 EXPECT_CALL(*mMockProcStat, deltaStats()).WillOnce(Return(procStatInfo));
454
455 time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
456 ASSERT_RESULT_OK(mCollector->onBoottimeCollection(now, mMockUidStatsCollector, mMockProcStat));
457
458 const auto actual = mCollectorPeer->getBoottimeCollectionInfo();
459
460 const CollectionInfo expected{
461 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
462 .records = {{
463 .systemSummaryStats = systemSummaryStats,
464 .userPackageSummaryStats = userPackageSummaryStats,
465 }},
466 };
467
468 EXPECT_THAT(actual, CollectionInfoEq(expected))
469 << "Boottime collection info doesn't match.\nExpected:\n"
470 << expected.toString() << "\nActual:\n"
471 << actual.toString();
472
473 ASSERT_NO_FATAL_FAILURE(checkDumpContents(/*wantedEmptyCollectionInstances=*/1))
474 << "Periodic collection shouldn't be reported";
475 }
476
TEST_F(IoPerfCollectionTest,TestOnPeriodicCollection)477 TEST_F(IoPerfCollectionTest, TestOnPeriodicCollection) {
478 const auto [uidStats, userPackageSummaryStats] = sampleUidStats();
479 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
480
481 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
482 EXPECT_CALL(*mMockProcStat, deltaStats()).WillOnce(Return(procStatInfo));
483
484 time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
485 ASSERT_RESULT_OK(mCollector->onPeriodicCollection(now, SystemState::NORMAL_MODE,
486 mMockUidStatsCollector, mMockProcStat));
487
488 const auto actual = mCollectorPeer->getPeriodicCollectionInfo();
489
490 const CollectionInfo expected{
491 .maxCacheSize = static_cast<size_t>(sysprop::periodicCollectionBufferSize().value_or(
492 kDefaultPeriodicCollectionBufferSize)),
493 .records = {{
494 .systemSummaryStats = systemSummaryStats,
495 .userPackageSummaryStats = userPackageSummaryStats,
496 }},
497 };
498
499 EXPECT_THAT(actual, CollectionInfoEq(expected))
500 << "Periodic collection info doesn't match.\nExpected:\n"
501 << expected.toString() << "\nActual:\n"
502 << actual.toString();
503
504 ASSERT_NO_FATAL_FAILURE(checkDumpContents(/*wantedEmptyCollectionInstances=*/1))
505 << "Boot-time collection shouldn't be reported";
506 }
507
TEST_F(IoPerfCollectionTest,TestOnCustomCollectionWithoutPackageFilter)508 TEST_F(IoPerfCollectionTest, TestOnCustomCollectionWithoutPackageFilter) {
509 const auto [uidStats, userPackageSummaryStats] = sampleUidStats();
510 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
511
512 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
513 EXPECT_CALL(*mMockProcStat, deltaStats()).WillOnce(Return(procStatInfo));
514
515 time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
516 ASSERT_RESULT_OK(mCollector->onCustomCollection(now, SystemState::NORMAL_MODE, {},
517 mMockUidStatsCollector, mMockProcStat));
518
519 const auto actual = mCollectorPeer->getCustomCollectionInfo();
520
521 CollectionInfo expected{
522 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
523 .records = {{
524 .systemSummaryStats = systemSummaryStats,
525 .userPackageSummaryStats = userPackageSummaryStats,
526 }},
527 };
528
529 EXPECT_THAT(actual, CollectionInfoEq(expected))
530 << "Custom collection info doesn't match.\nExpected:\n"
531 << expected.toString() << "\nActual:\n"
532 << actual.toString();
533
534 ASSERT_NO_FATAL_FAILURE(checkCustomDumpContents()) << "Custom collection should be reported";
535
536 TemporaryFile customDump;
537 ASSERT_RESULT_OK(mCollector->onCustomCollectionDump(customDump.fd));
538
539 // Should clear the cache.
540 ASSERT_RESULT_OK(mCollector->onCustomCollectionDump(-1));
541
542 expected.records.clear();
543 const CollectionInfo& emptyCollectionInfo = mCollectorPeer->getCustomCollectionInfo();
544 EXPECT_THAT(emptyCollectionInfo, CollectionInfoEq(expected))
545 << "Custom collection should be cleared.";
546 }
547
TEST_F(IoPerfCollectionTest,TestOnCustomCollectionWithPackageFilter)548 TEST_F(IoPerfCollectionTest, TestOnCustomCollectionWithPackageFilter) {
549 // Filter by package name should ignore this limit with package filter.
550 mCollectorPeer->setTopNStatsPerCategory(1);
551
552 const auto [uidStats, _] = sampleUidStats();
553 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
554
555 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
556 EXPECT_CALL(*mMockProcStat, deltaStats()).WillOnce(Return(procStatInfo));
557
558 time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
559 ASSERT_RESULT_OK(mCollector->onCustomCollection(now, SystemState::NORMAL_MODE,
560 {"mount", "com.google.android.car.kitchensink"},
561 mMockUidStatsCollector, mMockProcStat));
562
563 const auto actual = mCollectorPeer->getCustomCollectionInfo();
564
565 UserPackageSummaryStats userPackageSummaryStats{
566 .topNIoReads = {{1009, "mount", UserPackageStats::IoStats{{0, 14'000}, {0, 100}}},
567 {1002001, "com.google.android.car.kitchensink",
568 UserPackageStats::IoStats{{0, 3'400}, {0, 200}}}},
569 .topNIoWrites = {{1009, "mount", UserPackageStats::IoStats{{0, 16'000}, {0, 100}}},
570 {1002001, "com.google.android.car.kitchensink",
571 UserPackageStats::IoStats{{0, 6'700}, {0, 200}}}},
572 .topNIoBlocked = {{1009, "mount", UserPackageStats::ProcStats{1, {{"disk I/O", 1}}}},
573 {1002001, "com.google.android.car.kitchensink",
574 UserPackageStats::ProcStats{3,
575 {{"CTS", 2}, {"KitchenSinkApp", 1}}}}},
576 .topNMajorFaults =
577 {{1009, "mount", UserPackageStats::ProcStats{11'000, {{"disk I/O", 11'000}}}},
578 {1002001, "com.google.android.car.kitchensink",
579 UserPackageStats::ProcStats{22'445,
580 {{"KitchenSinkApp", 12'345}, {"CTS", 10'100}}}}},
581 .totalIoStats = {{1000, 21'600}, {300, 28'300}, {600, 600}},
582 .taskCountByUid = {{1009, 1}, {1002001, 5}},
583 .totalMajorFaults = 84'345,
584 .majorFaultsPercentChange = 0.0,
585 };
586
587 CollectionInfo expected{
588 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
589 .records = {{
590 .systemSummaryStats = systemSummaryStats,
591 .userPackageSummaryStats = userPackageSummaryStats,
592 }},
593 };
594
595 EXPECT_THAT(actual, CollectionInfoEq(expected))
596 << "Custom collection info doesn't match.\nExpected:\n"
597 << expected.toString() << "\nActual:\n"
598 << actual.toString();
599
600 ASSERT_NO_FATAL_FAILURE(checkCustomDumpContents()) << "Custom collection should be reported";
601
602 TemporaryFile customDump;
603 ASSERT_RESULT_OK(mCollector->onCustomCollectionDump(customDump.fd));
604
605 // Should clear the cache.
606 ASSERT_RESULT_OK(mCollector->onCustomCollectionDump(-1));
607
608 expected.records.clear();
609 const CollectionInfo& emptyCollectionInfo = mCollectorPeer->getCustomCollectionInfo();
610 EXPECT_THAT(emptyCollectionInfo, CollectionInfoEq(expected))
611 << "Custom collection should be cleared.";
612 }
613
TEST_F(IoPerfCollectionTest,TestOnPeriodicCollectionWithTrimmingStatsAfterTopN)614 TEST_F(IoPerfCollectionTest, TestOnPeriodicCollectionWithTrimmingStatsAfterTopN) {
615 mCollectorPeer->setTopNStatsPerCategory(1);
616 mCollectorPeer->setTopNStatsPerSubcategory(1);
617
618 const auto [uidStats, _] = sampleUidStats();
619 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
620
621 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
622 EXPECT_CALL(*mMockProcStat, deltaStats()).WillOnce(Return(procStatInfo));
623
624 time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
625 ASSERT_RESULT_OK(mCollector->onPeriodicCollection(now, SystemState::NORMAL_MODE,
626 mMockUidStatsCollector, mMockProcStat));
627
628 const auto actual = mCollectorPeer->getPeriodicCollectionInfo();
629
630 UserPackageSummaryStats userPackageSummaryStats{
631 .topNIoReads = {{1009, "mount", UserPackageStats::IoStats{{0, 14'000}, {0, 100}}}},
632 .topNIoWrites = {{1009, "mount", UserPackageStats::IoStats{{0, 16'000}, {0, 100}}}},
633 .topNIoBlocked = {{1002001, "com.google.android.car.kitchensink",
634 UserPackageStats::ProcStats{3, {{"CTS", 2}}}}},
635 .topNMajorFaults = {{1012345, "1012345",
636 UserPackageStats::ProcStats{50'900, {{"MapsApp", 50'900}}}}},
637 .totalIoStats = {{1000, 21'600}, {300, 28'300}, {600, 600}},
638 .taskCountByUid = {{1009, 1}, {1002001, 5}, {1012345, 4}},
639 .totalMajorFaults = 84'345,
640 .majorFaultsPercentChange = 0.0,
641 };
642
643 const CollectionInfo expected{
644 .maxCacheSize = static_cast<size_t>(sysprop::periodicCollectionBufferSize().value_or(
645 kDefaultPeriodicCollectionBufferSize)),
646 .records = {{
647 .systemSummaryStats = systemSummaryStats,
648 .userPackageSummaryStats = userPackageSummaryStats,
649 }},
650 };
651
652 EXPECT_THAT(actual, CollectionInfoEq(expected))
653 << "Periodic collection info doesn't match.\nExpected:\n"
654 << expected.toString() << "\nActual:\n"
655 << actual.toString();
656
657 ASSERT_NO_FATAL_FAILURE(checkDumpContents(/*wantedEmptyCollectionInstances=*/1))
658 << "Boot-time collection shouldn't be reported";
659 }
660
TEST_F(IoPerfCollectionTest,TestConsecutiveOnPeriodicCollection)661 TEST_F(IoPerfCollectionTest, TestConsecutiveOnPeriodicCollection) {
662 const auto [firstUidStats, firstUserPackageSummaryStats] = sampleUidStats();
663 const auto [firstProcStatInfo, firstSystemSummaryStats] = sampleProcStat();
664
665 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(firstUidStats));
666 EXPECT_CALL(*mMockProcStat, deltaStats()).WillOnce(Return(firstProcStatInfo));
667
668 time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
669 ASSERT_RESULT_OK(mCollector->onPeriodicCollection(now, SystemState::NORMAL_MODE,
670 mMockUidStatsCollector, mMockProcStat));
671
672 auto [secondUidStats, secondUserPackageSummaryStats] = sampleUidStats(/*multiplier=*/2);
673 const auto [secondProcStatInfo, secondSystemSummaryStats] = sampleProcStat(/*multiplier=*/2);
674
675 secondUserPackageSummaryStats.majorFaultsPercentChange =
676 (static_cast<double>(secondUserPackageSummaryStats.totalMajorFaults -
677 firstUserPackageSummaryStats.totalMajorFaults) /
678 static_cast<double>(firstUserPackageSummaryStats.totalMajorFaults)) *
679 100.0;
680
681 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(secondUidStats));
682 EXPECT_CALL(*mMockProcStat, deltaStats()).WillOnce(Return(secondProcStatInfo));
683
684 ASSERT_RESULT_OK(mCollector->onPeriodicCollection(now, SystemState::NORMAL_MODE,
685 mMockUidStatsCollector, mMockProcStat));
686
687 const auto actual = mCollectorPeer->getPeriodicCollectionInfo();
688
689 const CollectionInfo expected{
690 .maxCacheSize = static_cast<size_t>(sysprop::periodicCollectionBufferSize().value_or(
691 kDefaultPeriodicCollectionBufferSize)),
692 .records = {{.systemSummaryStats = firstSystemSummaryStats,
693 .userPackageSummaryStats = firstUserPackageSummaryStats},
694 {.systemSummaryStats = secondSystemSummaryStats,
695 .userPackageSummaryStats = secondUserPackageSummaryStats}},
696 };
697
698 EXPECT_THAT(actual, CollectionInfoEq(expected))
699 << "Periodic collection info doesn't match.\nExpected:\n"
700 << expected.toString() << "\nActual:\n"
701 << actual.toString();
702
703 ASSERT_NO_FATAL_FAILURE(checkDumpContents(/*wantedEmptyCollectionInstances=*/1))
704 << "Boot-time collection shouldn't be reported";
705 }
706
707 } // namespace watchdog
708 } // namespace automotive
709 } // namespace android
710