Loading libs/cputimeinstate/cputimeinstate.cpp +30 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ static uint32_t gNCpus = 0; static std::vector<std::vector<uint32_t>> gPolicyFreqs; static std::vector<std::vector<uint32_t>> gPolicyCpus; static std::set<uint32_t> gAllFreqs; static unique_fd gTisTotalMapFd; static unique_fd gTisMapFd; static unique_fd gConcurrentMapFd; static unique_fd gUidLastUpdateMapFd; Loading Loading @@ -129,6 +130,10 @@ static bool initGlobals() { gPolicyCpus.emplace_back(*cpus); } gTisTotalMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_total_time_in_state_map")}; if (gTisTotalMapFd < 0) return false; gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; if (gTisMapFd < 0) return false; Loading Loading @@ -239,6 +244,31 @@ std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs() { return gPolicyFreqs; } std::optional<std::vector<std::vector<uint64_t>>> getTotalCpuFreqTimes() { if (!gInitialized && !initGlobals()) return {}; std::vector<std::vector<uint64_t>> out; uint32_t maxFreqCount = 0; for (const auto &freqList : gPolicyFreqs) { if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size(); out.emplace_back(freqList.size(), 0); } std::vector<uint64_t> vals(gNCpus); const uint32_t freqCount = maxFreqCount <= MAX_FREQS_FOR_TOTAL ? maxFreqCount : MAX_FREQS_FOR_TOTAL; for (uint32_t freqIdx = 0; freqIdx < freqCount; ++freqIdx) { if (findMapEntry(gTisTotalMapFd, &freqIdx, vals.data())) return {}; for (uint32_t policyIdx = 0; policyIdx < gNPolicies; ++policyIdx) { if (freqIdx >= gPolicyFreqs[policyIdx].size()) continue; for (const auto &cpu : gPolicyCpus[policyIdx]) { out[policyIdx][freqIdx] += vals[cpu]; } } } return out; } // Retrieve the times in ns that uid spent running at each CPU frequency. // Return contains no value on error, otherwise it contains a vector of vectors using the format: // [[t0_0, t0_1, ...], Loading libs/cputimeinstate/cputimeinstate.h +1 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ namespace android { namespace bpf { bool startTrackingUidTimes(); std::optional<std::vector<std::vector<uint64_t>>> getTotalCpuFreqTimes(); std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid); std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> getUidsCpuFreqTimes(); Loading libs/cputimeinstate/testtimeinstate.cpp +47 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,12 @@ static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365; using std::vector; TEST(TimeInStateTest, TotalTimeInState) { auto times = getTotalCpuFreqTimes(); ASSERT_TRUE(times.has_value()); EXPECT_FALSE(times->empty()); } TEST(TimeInStateTest, SingleUidTimeInState) { auto times = getUidCpuFreqTimes(0); ASSERT_TRUE(times.has_value()); Loading Loading @@ -186,6 +192,31 @@ TEST(TimeInStateTest, AllUidUpdatedTimeInState) { } } TEST(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) { auto allUid = getUidsCpuFreqTimes(); auto total = getTotalCpuFreqTimes(); ASSERT_TRUE(allUid.has_value() && total.has_value()); // Check the number of policies. ASSERT_EQ(allUid->at(0).size(), total->size()); for (uint32_t policyIdx = 0; policyIdx < total->size(); ++policyIdx) { std::vector<uint64_t> totalTimes = total->at(policyIdx); uint32_t totalFreqsCount = totalTimes.size(); std::vector<uint64_t> allUidTimes(totalFreqsCount, 0); for (auto const &[uid, uidTimes]: *allUid) { for (uint32_t freqIdx = 0; freqIdx < uidTimes[policyIdx].size(); ++freqIdx) { allUidTimes[std::min(freqIdx, totalFreqsCount - 1)] += uidTimes[policyIdx][freqIdx]; } } for (uint32_t freqIdx = 0; freqIdx < totalFreqsCount; ++freqIdx) { ASSERT_LE(allUidTimes[freqIdx], totalTimes[freqIdx]); } } } TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { uint64_t zero = 0; auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; Loading Loading @@ -292,6 +323,22 @@ void TestCheckDelta(uint64_t before, uint64_t after) { ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf()); } TEST(TimeInStateTest, TotalTimeInStateMonotonic) { auto before = getTotalCpuFreqTimes(); ASSERT_TRUE(before.has_value()); sleep(1); auto after = getTotalCpuFreqTimes(); ASSERT_TRUE(after.has_value()); for (uint32_t policyIdx = 0; policyIdx < after->size(); ++policyIdx) { auto timesBefore = before->at(policyIdx); auto timesAfter = after->at(policyIdx); for (uint32_t freqIdx = 0; freqIdx < timesAfter.size(); ++freqIdx) { ASSERT_NO_FATAL_FAILURE(TestCheckDelta(timesBefore[freqIdx], timesAfter[freqIdx])); } } } TEST(TimeInStateTest, AllUidTimeInStateMonotonic) { auto map1 = getUidsCpuFreqTimes(); ASSERT_TRUE(map1.has_value()); Loading Loading
libs/cputimeinstate/cputimeinstate.cpp +30 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ static uint32_t gNCpus = 0; static std::vector<std::vector<uint32_t>> gPolicyFreqs; static std::vector<std::vector<uint32_t>> gPolicyCpus; static std::set<uint32_t> gAllFreqs; static unique_fd gTisTotalMapFd; static unique_fd gTisMapFd; static unique_fd gConcurrentMapFd; static unique_fd gUidLastUpdateMapFd; Loading Loading @@ -129,6 +130,10 @@ static bool initGlobals() { gPolicyCpus.emplace_back(*cpus); } gTisTotalMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_total_time_in_state_map")}; if (gTisTotalMapFd < 0) return false; gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; if (gTisMapFd < 0) return false; Loading Loading @@ -239,6 +244,31 @@ std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs() { return gPolicyFreqs; } std::optional<std::vector<std::vector<uint64_t>>> getTotalCpuFreqTimes() { if (!gInitialized && !initGlobals()) return {}; std::vector<std::vector<uint64_t>> out; uint32_t maxFreqCount = 0; for (const auto &freqList : gPolicyFreqs) { if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size(); out.emplace_back(freqList.size(), 0); } std::vector<uint64_t> vals(gNCpus); const uint32_t freqCount = maxFreqCount <= MAX_FREQS_FOR_TOTAL ? maxFreqCount : MAX_FREQS_FOR_TOTAL; for (uint32_t freqIdx = 0; freqIdx < freqCount; ++freqIdx) { if (findMapEntry(gTisTotalMapFd, &freqIdx, vals.data())) return {}; for (uint32_t policyIdx = 0; policyIdx < gNPolicies; ++policyIdx) { if (freqIdx >= gPolicyFreqs[policyIdx].size()) continue; for (const auto &cpu : gPolicyCpus[policyIdx]) { out[policyIdx][freqIdx] += vals[cpu]; } } } return out; } // Retrieve the times in ns that uid spent running at each CPU frequency. // Return contains no value on error, otherwise it contains a vector of vectors using the format: // [[t0_0, t0_1, ...], Loading
libs/cputimeinstate/cputimeinstate.h +1 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ namespace android { namespace bpf { bool startTrackingUidTimes(); std::optional<std::vector<std::vector<uint64_t>>> getTotalCpuFreqTimes(); std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid); std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> getUidsCpuFreqTimes(); Loading
libs/cputimeinstate/testtimeinstate.cpp +47 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,12 @@ static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365; using std::vector; TEST(TimeInStateTest, TotalTimeInState) { auto times = getTotalCpuFreqTimes(); ASSERT_TRUE(times.has_value()); EXPECT_FALSE(times->empty()); } TEST(TimeInStateTest, SingleUidTimeInState) { auto times = getUidCpuFreqTimes(0); ASSERT_TRUE(times.has_value()); Loading Loading @@ -186,6 +192,31 @@ TEST(TimeInStateTest, AllUidUpdatedTimeInState) { } } TEST(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) { auto allUid = getUidsCpuFreqTimes(); auto total = getTotalCpuFreqTimes(); ASSERT_TRUE(allUid.has_value() && total.has_value()); // Check the number of policies. ASSERT_EQ(allUid->at(0).size(), total->size()); for (uint32_t policyIdx = 0; policyIdx < total->size(); ++policyIdx) { std::vector<uint64_t> totalTimes = total->at(policyIdx); uint32_t totalFreqsCount = totalTimes.size(); std::vector<uint64_t> allUidTimes(totalFreqsCount, 0); for (auto const &[uid, uidTimes]: *allUid) { for (uint32_t freqIdx = 0; freqIdx < uidTimes[policyIdx].size(); ++freqIdx) { allUidTimes[std::min(freqIdx, totalFreqsCount - 1)] += uidTimes[policyIdx][freqIdx]; } } for (uint32_t freqIdx = 0; freqIdx < totalFreqsCount; ++freqIdx) { ASSERT_LE(allUidTimes[freqIdx], totalTimes[freqIdx]); } } } TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { uint64_t zero = 0; auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; Loading Loading @@ -292,6 +323,22 @@ void TestCheckDelta(uint64_t before, uint64_t after) { ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf()); } TEST(TimeInStateTest, TotalTimeInStateMonotonic) { auto before = getTotalCpuFreqTimes(); ASSERT_TRUE(before.has_value()); sleep(1); auto after = getTotalCpuFreqTimes(); ASSERT_TRUE(after.has_value()); for (uint32_t policyIdx = 0; policyIdx < after->size(); ++policyIdx) { auto timesBefore = before->at(policyIdx); auto timesAfter = after->at(policyIdx); for (uint32_t freqIdx = 0; freqIdx < timesAfter.size(); ++freqIdx) { ASSERT_NO_FATAL_FAILURE(TestCheckDelta(timesBefore[freqIdx], timesAfter[freqIdx])); } } } TEST(TimeInStateTest, AllUidTimeInStateMonotonic) { auto map1 = getUidsCpuFreqTimes(); ASSERT_TRUE(map1.has_value()); Loading