diff options
Diffstat (limited to 'libs/cputimeinstate')
-rw-r--r-- | libs/cputimeinstate/Android.bp | 11 | ||||
-rw-r--r-- | libs/cputimeinstate/cputimeinstate.cpp | 167 | ||||
-rw-r--r-- | libs/cputimeinstate/cputimeinstate.h | 7 | ||||
-rw-r--r-- | libs/cputimeinstate/testtimeinstate.cpp | 156 |
4 files changed, 7 insertions, 334 deletions
diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp index 570af71d9a..b1943a4afd 100644 --- a/libs/cputimeinstate/Android.bp +++ b/libs/cputimeinstate/Android.bp @@ -1,12 +1,3 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], -} - cc_library { name: "libtimeinstate", srcs: ["cputimeinstate.cpp"], @@ -42,5 +33,5 @@ cc_test { "-Wall", "-Wextra", ], - require_root: true, } + diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 7e9bb7d468..50f6289726 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -56,11 +56,9 @@ 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; -static unique_fd gPidTisMapFd; static std::optional<std::vector<uint32_t>> readNumbersFromFile(const std::string &path) { std::string data; @@ -99,7 +97,7 @@ static bool initGlobals() { struct dirent **dirlist; const char basepath[] = "/sys/devices/system/cpu/cpufreq"; int ret = scandir(basepath, &dirlist, isPolicyFile, comparePolicyFiles); - if (ret == -1 || ret == 0) return false; + if (ret == -1) return false; gNPolicies = ret; std::vector<std::string> policyFileNames; @@ -130,10 +128,6 @@ 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; @@ -145,24 +139,14 @@ static bool initGlobals() { unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_last_update_map")}; if (gUidLastUpdateMapFd < 0) return false; - gPidTisMapFd = unique_fd{mapRetrieveRO(BPF_FS_PATH "map_time_in_state_pid_time_in_state_map")}; - if (gPidTisMapFd < 0) return false; - - unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_map")); - if (trackedPidMapFd < 0) return false; - gInitialized = true; return true; } -static int retrieveProgramFd(const std::string &eventType, const std::string &eventName) { +static bool attachTracepointProgram(const std::string &eventType, const std::string &eventName) { std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s", eventType.c_str(), eventName.c_str()); - return retrieveProgram(path.c_str()); -} - -static bool attachTracepointProgram(const std::string &eventType, const std::string &eventName) { - int prog_fd = retrieveProgramFd(eventType, eventName); + int prog_fd = retrieveProgram(path.c_str()); if (prog_fd < 0) return false; return bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) >= 0; } @@ -178,17 +162,6 @@ static std::optional<uint32_t> getPolicyFreqIdx(uint32_t policy) { return {}; } -// Check if tracking is expected to work without activating it. -bool isTrackingUidTimesSupported() { - auto freqs = getCpuFreqs(); - if (!freqs || freqs->empty()) return false; - if (gTracking) return true; - if (retrieveProgramFd("sched", "sched_switch") < 0) return false; - if (retrieveProgramFd("power", "cpu_frequency") < 0) return false; - if (retrieveProgramFd("sched", "sched_process_free") < 0) return false; - return true; -} - // Start tracking and aggregating data to be reported by getUidCpuFreqTimes and getUidsCpuFreqTimes. // Returns true on success, false otherwise. // Tracking is active only once a live process has successfully called this function; if the calling @@ -249,8 +222,7 @@ bool startTrackingUidTimes() { } gTracking = attachTracepointProgram("sched", "sched_switch") && - attachTracepointProgram("power", "cpu_frequency") && - attachTracepointProgram("sched", "sched_process_free"); + attachTracepointProgram("power", "cpu_frequency"); return gTracking; } @@ -259,31 +231,6 @@ 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, ...], @@ -304,7 +251,7 @@ std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t ui for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) { key.bucket = i; if (findMapEntry(gTisMapFd, &key, vals.data())) { - if (errno != ENOENT || getFirstMapKey(gTisMapFd, &key)) return {}; + if (errno != ENOENT) return {}; continue; } @@ -415,7 +362,7 @@ std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry) time_key_t key = {.uid = uid}; for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) { if (findMapEntry(gConcurrentMapFd, &key, vals.data())) { - if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &key)) return {}; + if (errno != ENOENT) return {}; continue; } auto offset = key.bucket * CPUS_PER_ENTRY; @@ -478,7 +425,6 @@ std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsUpdatedCon uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0; do { - if (key.bucket > (gNCpus - 1) / CPUS_PER_ENTRY) return {}; if (lastUpdate) { auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate); if (!uidUpdated.has_value()) return {}; @@ -555,106 +501,5 @@ bool clearUidTimes(uint32_t uid) { return true; } -bool startTrackingProcessCpuTimes(pid_t pid) { - if (!gInitialized && !initGlobals()) return false; - - unique_fd trackedPidHashMapFd( - mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_hash_map")); - if (trackedPidHashMapFd < 0) return false; - - unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_map")); - if (trackedPidMapFd < 0) return false; - - for (uint32_t index = 0; index < MAX_TRACKED_PIDS; index++) { - // Find first available [index, pid] entry in the pid_tracked_hash_map map - if (writeToMapEntry(trackedPidHashMapFd, &index, &pid, BPF_NOEXIST) != 0) { - if (errno != EEXIST) { - return false; - } - continue; // This index is already taken - } - - tracked_pid_t tracked_pid = {.pid = pid, .state = TRACKED_PID_STATE_ACTIVE}; - if (writeToMapEntry(trackedPidMapFd, &index, &tracked_pid, BPF_ANY) != 0) { - return false; - } - return true; - } - return false; -} - -// Marks the specified task identified by its PID (aka TID) for CPU time-in-state tracking -// aggregated with other tasks sharing the same TGID and aggregation key. -bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) { - if (!gInitialized && !initGlobals()) return false; - - unique_fd taskAggregationMapFd( - mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_task_aggregation_map")); - if (taskAggregationMapFd < 0) return false; - - return writeToMapEntry(taskAggregationMapFd, &pid, &aggregationKey, BPF_ANY) == 0; -} - -// Retrieves the times in ns that each thread spent running at each CPU freq, aggregated by -// aggregation key. -// Return contains no value on error, otherwise it contains a map from aggregation keys -// to vectors of vectors using the format: -// { aggKey0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...], -// aggKey1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... } -// where ti_j_k is the ns tid i spent running on the jth cluster at the cluster's kth lowest freq. -std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> -getAggregatedTaskCpuFreqTimes(pid_t tgid, const std::vector<uint16_t> &aggregationKeys) { - if (!gInitialized && !initGlobals()) return {}; - - uint32_t maxFreqCount = 0; - std::vector<std::vector<uint64_t>> mapFormat; - for (const auto &freqList : gPolicyFreqs) { - if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size(); - mapFormat.emplace_back(freqList.size(), 0); - } - - bool dataCollected = false; - std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>> map; - std::vector<tis_val_t> vals(gNCpus); - for (uint16_t aggregationKey : aggregationKeys) { - map.emplace(aggregationKey, mapFormat); - - aggregated_task_tis_key_t key{.tgid = tgid, .aggregation_key = aggregationKey}; - for (key.bucket = 0; key.bucket <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++key.bucket) { - if (findMapEntry(gPidTisMapFd, &key, vals.data()) != 0) { - if (errno != ENOENT) { - return {}; - } - continue; - } else { - dataCollected = true; - } - - // Combine data by aggregating time-in-state data grouped by CPU cluster aka policy. - uint32_t offset = key.bucket * FREQS_PER_ENTRY; - uint32_t nextOffset = offset + FREQS_PER_ENTRY; - for (uint32_t j = 0; j < gNPolicies; ++j) { - if (offset >= gPolicyFreqs[j].size()) continue; - auto begin = map[key.aggregation_key][j].begin() + offset; - auto end = nextOffset < gPolicyFreqs[j].size() ? begin + FREQS_PER_ENTRY - : map[key.aggregation_key][j].end(); - for (const auto &cpu : gPolicyCpus[j]) { - std::transform(begin, end, std::begin(vals[cpu].ar), begin, - std::plus<uint64_t>()); - } - } - } - } - - if (!dataCollected) { - // Check if eBPF is supported on this device. If it is, gTisMap should not be empty. - time_key_t key; - if (getFirstMapKey(gTisMapFd, &key) != 0) { - return {}; - } - } - return map; -} - } // namespace bpf } // namespace android diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h index 41453745a0..b7600f59e0 100644 --- a/libs/cputimeinstate/cputimeinstate.h +++ b/libs/cputimeinstate/cputimeinstate.h @@ -22,9 +22,7 @@ namespace android { namespace bpf { -bool isTrackingUidTimesSupported(); 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(); @@ -43,10 +41,5 @@ std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsUpdatedConcurrentTimes(uint64_t *lastUpdate); bool clearUidTimes(unsigned int uid); -bool startTrackingProcessCpuTimes(pid_t pid); -bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey); -std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> -getAggregatedTaskCpuFreqTimes(pid_t pid, const std::vector<uint16_t> &aggregationKeys); - } // namespace bpf } // namespace android diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 2112b10ebc..ea2a2008b7 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -19,8 +19,6 @@ #include <sys/sysinfo.h> -#include <pthread.h> -#include <semaphore.h> #include <numeric> #include <unordered_map> #include <vector> @@ -40,17 +38,6 @@ static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365; using std::vector; -TEST(TimeInStateTest, IsTrackingSupported) { - isTrackingUidTimesSupported(); - SUCCEED(); -} - -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()); @@ -197,31 +184,6 @@ 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)}; @@ -328,22 +290,6 @@ 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()); @@ -441,28 +387,6 @@ TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) { } } -TEST(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) { - uint32_t uid = 0; - { - // Find an unused UID - auto map = getUidsConcurrentTimes(); - ASSERT_TRUE(map.has_value()); - ASSERT_FALSE(map->empty()); - for (const auto &kv : *map) uid = std::max(uid, kv.first); - ++uid; - } - android::base::unique_fd fd{ - bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; - ASSERT_GE(fd, 0); - uint32_t nCpus = get_nprocs_conf(); - uint32_t maxBucket = (nCpus - 1) / CPUS_PER_ENTRY; - time_key_t key = {.uid = uid, .bucket = maxBucket + 1}; - std::vector<concurrent_val_t> vals(nCpus); - ASSERT_FALSE(writeToMapEntry(fd, &key, vals.data(), BPF_NOEXIST)); - EXPECT_FALSE(getUidsConcurrentTimes().has_value()); - ASSERT_FALSE(deleteMapEntry(fd, &key)); -} - TEST(TimeInStateTest, AllUidTimesConsistent) { auto tisMap = getUidsCpuFreqTimes(); ASSERT_TRUE(tisMap.has_value()); @@ -558,85 +482,5 @@ TEST(TimeInStateTest, GetCpuFreqs) { for (size_t i = 0; i < freqs->size(); ++i) EXPECT_EQ((*freqs)[i].size(), (*times)[i].size()); } -uint64_t timeNanos() { - struct timespec spec; - clock_gettime(CLOCK_MONOTONIC, &spec); - return spec.tv_sec * 1000000000 + spec.tv_nsec; -} - -// Keeps CPU busy with some number crunching -void useCpu() { - long sum = 0; - for (int i = 0; i < 100000; i++) { - sum *= i; - } -} - -sem_t pingsem, pongsem; - -void *testThread(void *) { - for (int i = 0; i < 10; i++) { - sem_wait(&pingsem); - useCpu(); - sem_post(&pongsem); - } - return nullptr; -} - -TEST(TimeInStateTest, GetAggregatedTaskCpuFreqTimes) { - uint64_t startTimeNs = timeNanos(); - - sem_init(&pingsem, 0, 1); - sem_init(&pongsem, 0, 0); - - pthread_t thread; - ASSERT_EQ(pthread_create(&thread, NULL, &testThread, NULL), 0); - - // This process may have been running for some time, so when we start tracking - // CPU time, the very first switch may include the accumulated time. - // Yield the remainder of this timeslice to the newly created thread. - sem_wait(&pongsem); - sem_post(&pingsem); - - pid_t tgid = getpid(); - startTrackingProcessCpuTimes(tgid); - - pid_t tid = pthread_gettid_np(thread); - startAggregatingTaskCpuTimes(tid, 42); - - // Play ping-pong with the other thread to ensure that both threads get - // some CPU time. - for (int i = 0; i < 9; i++) { - sem_wait(&pongsem); - useCpu(); - sem_post(&pingsem); - } - - pthread_join(thread, NULL); - - std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> optionalMap = - getAggregatedTaskCpuFreqTimes(tgid, {0, 42}); - ASSERT_TRUE(optionalMap); - - std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>> map = *optionalMap; - ASSERT_EQ(map.size(), 2u); - - uint64_t testDurationNs = timeNanos() - startTimeNs; - for (auto pair : map) { - uint16_t aggregationKey = pair.first; - ASSERT_TRUE(aggregationKey == 0 || aggregationKey == 42); - - std::vector<std::vector<uint64_t>> timesInState = pair.second; - uint64_t totalCpuTime = 0; - for (size_t i = 0; i < timesInState.size(); i++) { - for (size_t j = 0; j < timesInState[i].size(); j++) { - totalCpuTime += timesInState[i][j]; - } - } - ASSERT_GT(totalCpuTime, 0ul); - ASSERT_LE(totalCpuTime, testDurationNs); - } -} - } // namespace bpf } // namespace android |