/* * Copyright 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MockPackageInfoResolver.h" #include "MockUidCpuStatsCollector.h" #include "MockUidIoStatsCollector.h" #include "MockUidProcStatsCollector.h" #include "PackageInfoTestUtils.h" #include "UidIoStatsCollector.h" #include "UidProcStatsCollector.h" #include "UidProcStatsCollectorTestUtils.h" #include "UidStatsCollector.h" #include #include #include #include #include namespace android { namespace automotive { namespace watchdog { using ::android::automotive::watchdog::internal::PackageInfo; using ::android::automotive::watchdog::internal::UidType; using ::android::base::Error; using ::android::base::Result; using ::android::base::StringAppendF; using ::android::base::StringPrintf; using ::testing::AllOf; using ::testing::Eq; using ::testing::ExplainMatchResult; using ::testing::Field; using ::testing::IsEmpty; using ::testing::Matcher; using ::testing::Return; using ::testing::UnorderedElementsAre; using ::testing::UnorderedElementsAreArray; namespace { std::string toString(const UidStats& uidStats) { return StringPrintf("UidStats{packageInfo: %s, cpuTimeMillis: %" PRId64 ", ioStats: %s, procStats: %s}", uidStats.packageInfo.toString().c_str(), uidStats.cpuTimeMillis, uidStats.ioStats.toString().c_str(), uidStats.procStats.toString().c_str()); } std::string toString(const std::vector& uidStats) { std::string buffer; StringAppendF(&buffer, "{"); for (const auto& stats : uidStats) { StringAppendF(&buffer, "%s\n", toString(stats).c_str()); } StringAppendF(&buffer, "}"); return buffer; } MATCHER_P(UidStatsEq, expected, "") { return ExplainMatchResult(AllOf(Field("packageInfo", &UidStats::packageInfo, PackageInfoEq(expected.packageInfo)), Field("cpuTimeMillis", &UidStats::cpuTimeMillis, Eq(expected.cpuTimeMillis)), Field("ioStats", &UidStats::ioStats, Eq(expected.ioStats)), Field("procStats", &UidStats::procStats, UidProcStatsEq(expected.procStats))), arg, result_listener); } std::vector> UidStatsMatchers(const std::vector& uidStats) { std::vector> matchers; for (const auto& stats : uidStats) { matchers.push_back(UidStatsEq(stats)); } return matchers; } std::unordered_map samplePackageInfoByUid() { return {{1001234, constructPackageInfo("system.daemon", 1001234, UidType::NATIVE)}, {1005678, constructPackageInfo("kitchensink.app", 1005678, UidType::APPLICATION)}}; } std::unordered_map sampleUidIoStatsByUid() { return {{1001234, UidIoStats{/*fgRdBytes=*/3'000, /*bgRdBytes=*/0, /*fgWrBytes=*/500, /*bgWrBytes=*/0, /*fgFsync=*/20, /*bgFsync=*/0}}, {1005678, UidIoStats{/*fgRdBytes=*/30, /*bgRdBytes=*/100, /*fgWrBytes=*/50, /*bgWrBytes=*/200, /*fgFsync=*/45, /*bgFsync=*/60}}}; } std::unordered_map sampleUidProcStatsByUid() { return {{1001234, UidProcStats{.cpuTimeMillis = 10, .totalMajorFaults = 220, .totalTasksCount = 2, .ioBlockedTasksCount = 1, .processStatsByPid = {{1, {"init", 0, 10, 220, 2, 1}}}}}, {1005678, UidProcStats{.cpuTimeMillis = 43, .totalMajorFaults = 600, .totalTasksCount = 2, .ioBlockedTasksCount = 2, .processStatsByPid = { {1000, {"system_server", 13'400, 43, 600, 2, 2}}}}}}; } std::unordered_map sampleUidCpuStatsByUid() { return {{1001234, 15}, {1005678, 43}}; } std::vector sampleUidStats() { return {{.packageInfo = constructPackageInfo("system.daemon", 1001234, UidType::NATIVE), .cpuTimeMillis = 15, .ioStats = UidIoStats{/*fgRdBytes=*/3'000, /*bgRdBytes=*/0, /*fgWrBytes=*/500, /*bgWrBytes=*/0, /*fgFsync=*/20, /*bgFsync=*/0}, .procStats = UidProcStats{.cpuTimeMillis = 10, .totalMajorFaults = 220, .totalTasksCount = 2, .ioBlockedTasksCount = 1, .processStatsByPid = {{1, {"init", 0, 10, 220, 2, 1}}}}}, {.packageInfo = constructPackageInfo("kitchensink.app", 1005678, UidType::APPLICATION), .cpuTimeMillis = 43, .ioStats = UidIoStats{/*fgRdBytes=*/30, /*bgRdBytes=*/100, /*fgWrBytes=*/50, /*bgWrBytes=*/200, /*fgFsync=*/45, /*bgFsync=*/60}, .procStats = UidProcStats{.cpuTimeMillis = 43, .totalMajorFaults = 600, .totalTasksCount = 2, .ioBlockedTasksCount = 2, .processStatsByPid = { {1000, {"system_server", 13'400, 43, 600, 2, 2}}}}}}; } } // namespace namespace internal { class UidStatsCollectorPeer final : public RefBase { public: explicit UidStatsCollectorPeer(sp collector) : mCollector(collector) {} void setPackageInfoResolver(const sp& packageInfoResolver) { mCollector->mPackageInfoResolver = packageInfoResolver; } void setUidIoStatsCollector(const sp& uidIoStatsCollector) { mCollector->mUidIoStatsCollector = uidIoStatsCollector; } void setUidProcStatsCollector(const sp& uidProcStatsCollector) { mCollector->mUidProcStatsCollector = uidProcStatsCollector; } void setUidCpuStatsCollector(const sp& uidCpuStatsCollector) { mCollector->mUidCpuStatsCollector = uidCpuStatsCollector; } private: sp mCollector; }; } // namespace internal class UidStatsCollectorTest : public ::testing::Test { protected: virtual void SetUp() { mUidStatsCollector = sp::make(); mUidStatsCollectorPeer = sp::make(mUidStatsCollector); mMockPackageInfoResolver = sp::make(); mMockUidIoStatsCollector = sp::make(); mMockUidProcStatsCollector = sp::make(); mMockUidCpuStatsCollector = sp::make(); mUidStatsCollectorPeer->setPackageInfoResolver(mMockPackageInfoResolver); mUidStatsCollectorPeer->setUidIoStatsCollector(mMockUidIoStatsCollector); mUidStatsCollectorPeer->setUidProcStatsCollector(mMockUidProcStatsCollector); mUidStatsCollectorPeer->setUidCpuStatsCollector(mMockUidCpuStatsCollector); } virtual void TearDown() { mUidStatsCollector.clear(); mUidStatsCollectorPeer.clear(); mMockPackageInfoResolver.clear(); mMockUidIoStatsCollector.clear(); mMockUidProcStatsCollector.clear(); mMockUidCpuStatsCollector.clear(); } sp mUidStatsCollector; sp mUidStatsCollectorPeer; sp mMockPackageInfoResolver; sp mMockUidIoStatsCollector; sp mMockUidProcStatsCollector; sp mMockUidCpuStatsCollector; }; TEST_F(UidStatsCollectorTest, TestInit) { EXPECT_CALL(*mMockUidIoStatsCollector, init()).Times(1); EXPECT_CALL(*mMockUidProcStatsCollector, init()).Times(1); mUidStatsCollector->init(); } TEST_F(UidStatsCollectorTest, TestCollect) { EXPECT_CALL(*mMockUidIoStatsCollector, enabled()).WillOnce(Return(true)); EXPECT_CALL(*mMockUidProcStatsCollector, enabled()).WillOnce(Return(true)); EXPECT_CALL(*mMockUidIoStatsCollector, collect()).WillOnce(Return(Result())); EXPECT_CALL(*mMockUidProcStatsCollector, collect()).WillOnce(Return(Result())); EXPECT_CALL(*mMockUidIoStatsCollector, latestStats()) .WillOnce(Return(std::unordered_map())); EXPECT_CALL(*mMockUidProcStatsCollector, latestStats()) .WillOnce(Return(std::unordered_map())); EXPECT_CALL(*mMockUidIoStatsCollector, deltaStats()) .WillOnce(Return(std::unordered_map())); EXPECT_CALL(*mMockUidProcStatsCollector, deltaStats()) .WillOnce(Return(std::unordered_map())); ASSERT_RESULT_OK(mUidStatsCollector->collect()); } TEST_F(UidStatsCollectorTest, TestFailsCollectOnUidIoStatsCollectorError) { Result errorResult = Error() << "Failed to collect per-UID I/O stats"; EXPECT_CALL(*mMockUidIoStatsCollector, collect()).WillOnce(Return(errorResult)); ASSERT_FALSE(mUidStatsCollector->collect().ok()) << "Must fail to collect when per-UID I/O stats collector fails"; } TEST_F(UidStatsCollectorTest, TestFailsCollectOnUidProcStatsCollectorError) { Result errorResult = Error() << "Failed to collect per-UID proc stats"; EXPECT_CALL(*mMockUidProcStatsCollector, collect()).WillOnce(Return(errorResult)); ASSERT_FALSE(mUidStatsCollector->collect().ok()) << "Must fail to collect when per-UID proc stats collector fails"; } TEST_F(UidStatsCollectorTest, TestCollectLatestStats) { const std::unordered_map packageInfoByUid = samplePackageInfoByUid(); const std::unordered_map uidIoStatsByUid = sampleUidIoStatsByUid(); const std::unordered_map uidProcStatsByUid = sampleUidProcStatsByUid(); const std::unordered_map uidCpuStatsByUid = sampleUidCpuStatsByUid(); EXPECT_CALL(*mMockPackageInfoResolver, getPackageInfosForUids(UnorderedElementsAre(1001234, 1005678))) .WillOnce(Return(packageInfoByUid)); EXPECT_CALL(*mMockUidIoStatsCollector, latestStats()).WillOnce(Return(uidIoStatsByUid)); EXPECT_CALL(*mMockUidProcStatsCollector, latestStats()).WillOnce(Return(uidProcStatsByUid)); EXPECT_CALL(*mMockUidCpuStatsCollector, latestStats()).WillOnce(Return(uidCpuStatsByUid)); ASSERT_RESULT_OK(mUidStatsCollector->collect()); const std::vector expected = sampleUidStats(); auto actual = mUidStatsCollector->latestStats(); EXPECT_THAT(actual, UnorderedElementsAreArray(UidStatsMatchers(expected))) << "Latest UID stats doesn't match.\nExpected: " << toString(expected) << "\nActual: " << toString(actual); actual = mUidStatsCollector->deltaStats(); EXPECT_THAT(actual, IsEmpty()) << "Delta UID stats isn't empty.\nActual: " << toString(actual); } TEST_F(UidStatsCollectorTest, TestCollectDeltaStats) { const std::unordered_map packageInfoByUid = samplePackageInfoByUid(); const std::unordered_map uidIoStatsByUid = sampleUidIoStatsByUid(); const std::unordered_map uidProcStatsByUid = sampleUidProcStatsByUid(); const std::unordered_map uidCpuStatsByUid = sampleUidCpuStatsByUid(); EXPECT_CALL(*mMockPackageInfoResolver, getPackageInfosForUids(UnorderedElementsAre(1001234, 1005678))) .WillOnce(Return(packageInfoByUid)); EXPECT_CALL(*mMockUidIoStatsCollector, deltaStats()).WillOnce(Return(uidIoStatsByUid)); EXPECT_CALL(*mMockUidProcStatsCollector, deltaStats()).WillOnce(Return(uidProcStatsByUid)); EXPECT_CALL(*mMockUidCpuStatsCollector, deltaStats()).WillOnce(Return(uidCpuStatsByUid)); ASSERT_RESULT_OK(mUidStatsCollector->collect()); const std::vector expected = sampleUidStats(); auto actual = mUidStatsCollector->deltaStats(); EXPECT_THAT(actual, UnorderedElementsAreArray(UidStatsMatchers(expected))) << "Delta UID stats doesn't match.\nExpected: " << toString(expected) << "\nActual: " << toString(actual); actual = mUidStatsCollector->latestStats(); EXPECT_THAT(actual, IsEmpty()) << "Latest UID stats isn't empty.\nActual: " << toString(actual); } TEST_F(UidStatsCollectorTest, TestCollectDeltaStatsWithMissingUidIoStats) { const std::unordered_map packageInfoByUid = samplePackageInfoByUid(); std::unordered_map uidIoStatsByUid = sampleUidIoStatsByUid(); uidIoStatsByUid.erase(1001234); const std::unordered_map uidProcStatsByUid = sampleUidProcStatsByUid(); const std::unordered_map uidCpuStatsByUid = sampleUidCpuStatsByUid(); EXPECT_CALL(*mMockPackageInfoResolver, getPackageInfosForUids(UnorderedElementsAre(1001234, 1005678))) .WillOnce(Return(packageInfoByUid)); EXPECT_CALL(*mMockUidIoStatsCollector, deltaStats()).WillOnce(Return(uidIoStatsByUid)); EXPECT_CALL(*mMockUidProcStatsCollector, deltaStats()).WillOnce(Return(uidProcStatsByUid)); EXPECT_CALL(*mMockUidCpuStatsCollector, deltaStats()).WillOnce(Return(uidCpuStatsByUid)); ASSERT_RESULT_OK(mUidStatsCollector->collect()); std::vector expected = sampleUidStats(); expected[0].ioStats = {}; auto actual = mUidStatsCollector->deltaStats(); EXPECT_THAT(actual, UnorderedElementsAreArray(UidStatsMatchers(expected))) << "Delta UID stats doesn't match.\nExpected: " << toString(expected) << "\nActual: " << toString(actual); actual = mUidStatsCollector->latestStats(); EXPECT_THAT(actual, IsEmpty()) << "Latest UID stats isn't empty.\nActual: " << toString(actual); } TEST_F(UidStatsCollectorTest, TestCollectDeltaStatsWithMissingUidProcStats) { const std::unordered_map packageInfoByUid = samplePackageInfoByUid(); const std::unordered_map uidIoStatsByUid = sampleUidIoStatsByUid(); std::unordered_map uidProcStatsByUid = sampleUidProcStatsByUid(); uidProcStatsByUid.erase(1001234); const std::unordered_map uidCpuStatsByUid = sampleUidCpuStatsByUid(); EXPECT_CALL(*mMockPackageInfoResolver, getPackageInfosForUids(UnorderedElementsAre(1001234, 1005678))) .WillOnce(Return(packageInfoByUid)); EXPECT_CALL(*mMockUidIoStatsCollector, deltaStats()).WillOnce(Return(uidIoStatsByUid)); EXPECT_CALL(*mMockUidProcStatsCollector, deltaStats()).WillOnce(Return(uidProcStatsByUid)); EXPECT_CALL(*mMockUidCpuStatsCollector, deltaStats()).WillOnce(Return(uidCpuStatsByUid)); ASSERT_RESULT_OK(mUidStatsCollector->collect()); std::vector expected = sampleUidStats(); expected[0].procStats = {}; auto actual = mUidStatsCollector->deltaStats(); EXPECT_THAT(actual, UnorderedElementsAreArray(UidStatsMatchers(expected))) << "Delta UID stats doesn't match.\nExpected: " << toString(expected) << "\nActual: " << toString(actual); actual = mUidStatsCollector->latestStats(); EXPECT_THAT(actual, IsEmpty()) << "Latest UID stats isn't empty.\nActual: " << toString(actual); } TEST_F(UidStatsCollectorTest, TestCollectDeltaStatsWithMissingUidCpuStats) { const std::unordered_map packageInfoByUid = samplePackageInfoByUid(); const std::unordered_map uidIoStatsByUid = sampleUidIoStatsByUid(); const std::unordered_map uidProcStatsByUid = sampleUidProcStatsByUid(); EXPECT_CALL(*mMockPackageInfoResolver, getPackageInfosForUids(UnorderedElementsAre(1001234, 1005678))) .WillOnce(Return(packageInfoByUid)); EXPECT_CALL(*mMockUidIoStatsCollector, deltaStats()).WillOnce(Return(uidIoStatsByUid)); EXPECT_CALL(*mMockUidProcStatsCollector, deltaStats()).WillOnce(Return(uidProcStatsByUid)); ASSERT_RESULT_OK(mUidStatsCollector->collect()); std::vector expected = sampleUidStats(); // Since no Uid CPU time was recovered from the /proc/uid_cputime/show_uid_stat file, the // collector will default the UID CPU time to the UidProcStats' CPU time. expected[0].cpuTimeMillis = 10; auto actual = mUidStatsCollector->deltaStats(); EXPECT_THAT(actual, UnorderedElementsAreArray(UidStatsMatchers(expected))) << "Delta UID stats doesn't match.\nExpected: " << toString(expected) << "\nActual: " << toString(actual); actual = mUidStatsCollector->latestStats(); EXPECT_THAT(actual, IsEmpty()) << "Latest UID stats isn't empty.\nActual: " << toString(actual); } TEST_F(UidStatsCollectorTest, TestCollectDeltaStatsWithMissingPackageInfo) { std::unordered_map packageInfoByUid = samplePackageInfoByUid(); packageInfoByUid.erase(1001234); const std::unordered_map uidIoStatsByUid = sampleUidIoStatsByUid(); const std::unordered_map uidProcStatsByUid = sampleUidProcStatsByUid(); const std::unordered_map uidCpuStatsByUid = sampleUidCpuStatsByUid(); EXPECT_CALL(*mMockPackageInfoResolver, getPackageInfosForUids(UnorderedElementsAre(1001234, 1005678))) .WillOnce(Return(packageInfoByUid)); EXPECT_CALL(*mMockUidIoStatsCollector, deltaStats()).WillOnce(Return(uidIoStatsByUid)); EXPECT_CALL(*mMockUidProcStatsCollector, deltaStats()).WillOnce(Return(uidProcStatsByUid)); EXPECT_CALL(*mMockUidCpuStatsCollector, deltaStats()).WillOnce(Return(uidCpuStatsByUid)); ASSERT_RESULT_OK(mUidStatsCollector->collect()); std::vector expected = sampleUidStats(); expected[0].packageInfo = constructPackageInfo("", 1001234); auto actual = mUidStatsCollector->deltaStats(); EXPECT_THAT(actual, UnorderedElementsAreArray(UidStatsMatchers(expected))) << "Delta UID stats doesn't match.\nExpected: " << toString(expected) << "\nActual: " << toString(actual); actual = mUidStatsCollector->latestStats(); EXPECT_THAT(actual, IsEmpty()) << "Latest UID stats isn't empty.\nActual: " << toString(actual); } TEST_F(UidStatsCollectorTest, TestUidStatsHasPackageInfo) { std::unordered_map packageInfoByUid = samplePackageInfoByUid(); packageInfoByUid.erase(1001234); const std::unordered_map uidIoStatsByUid = sampleUidIoStatsByUid(); const std::unordered_map uidProcStatsByUid = sampleUidProcStatsByUid(); EXPECT_CALL(*mMockPackageInfoResolver, getPackageInfosForUids(UnorderedElementsAre(1001234, 1005678))) .WillOnce(Return(packageInfoByUid)); EXPECT_CALL(*mMockUidIoStatsCollector, deltaStats()).WillOnce(Return(uidIoStatsByUid)); EXPECT_CALL(*mMockUidProcStatsCollector, deltaStats()).WillOnce(Return(uidProcStatsByUid)); ASSERT_RESULT_OK(mUidStatsCollector->collect()); const auto actual = mUidStatsCollector->deltaStats(); EXPECT_EQ(actual.size(), static_cast(2)); for (const auto stats : actual) { if (stats.packageInfo.packageIdentifier.uid == 1001234) { EXPECT_FALSE(stats.hasPackageInfo()) << "Stats without package info should return false"; } else if (stats.packageInfo.packageIdentifier.uid == 1005678) { EXPECT_TRUE(stats.hasPackageInfo()) << "Stats without package info should return true"; } else { FAIL() << "Unexpected uid " << stats.packageInfo.packageIdentifier.uid; } } } TEST_F(UidStatsCollectorTest, TestUidStatsGenericPackageName) { std::unordered_map packageInfoByUid = samplePackageInfoByUid(); packageInfoByUid.erase(1001234); const std::unordered_map uidIoStatsByUid = sampleUidIoStatsByUid(); const std::unordered_map uidProcStatsByUid = sampleUidProcStatsByUid(); EXPECT_CALL(*mMockPackageInfoResolver, getPackageInfosForUids(UnorderedElementsAre(1001234, 1005678))) .WillOnce(Return(packageInfoByUid)); EXPECT_CALL(*mMockUidIoStatsCollector, deltaStats()).WillOnce(Return(uidIoStatsByUid)); EXPECT_CALL(*mMockUidProcStatsCollector, deltaStats()).WillOnce(Return(uidProcStatsByUid)); ASSERT_RESULT_OK(mUidStatsCollector->collect()); const auto actual = mUidStatsCollector->deltaStats(); EXPECT_EQ(actual.size(), static_cast(2)); for (const auto stats : actual) { if (stats.packageInfo.packageIdentifier.uid == 1001234) { EXPECT_EQ(stats.genericPackageName(), "1001234") << "Stats without package info should return UID as package name"; } else if (stats.packageInfo.packageIdentifier.uid == 1005678) { EXPECT_EQ(stats.genericPackageName(), "kitchensink.app") << "Stats with package info should return corresponding package name"; } else { FAIL() << "Unexpected uid " << stats.packageInfo.packageIdentifier.uid; } } } TEST_F(UidStatsCollectorTest, TestUidStatsUid) { std::unordered_map packageInfoByUid = samplePackageInfoByUid(); packageInfoByUid.erase(1001234); const std::unordered_map uidIoStatsByUid = sampleUidIoStatsByUid(); const std::unordered_map uidProcStatsByUid = sampleUidProcStatsByUid(); EXPECT_CALL(*mMockPackageInfoResolver, getPackageInfosForUids(UnorderedElementsAre(1001234, 1005678))) .WillOnce(Return(packageInfoByUid)); EXPECT_CALL(*mMockUidIoStatsCollector, deltaStats()).WillOnce(Return(uidIoStatsByUid)); EXPECT_CALL(*mMockUidProcStatsCollector, deltaStats()).WillOnce(Return(uidProcStatsByUid)); ASSERT_RESULT_OK(mUidStatsCollector->collect()); const auto actual = mUidStatsCollector->deltaStats(); for (const auto stats : actual) { EXPECT_EQ(stats.uid(), static_cast(stats.packageInfo.packageIdentifier.uid)); } } } // namespace watchdog } // namespace automotive } // namespace android