summaryrefslogtreecommitdiff
path: root/libs/cputimeinstate
diff options
context:
space:
mode:
Diffstat (limited to 'libs/cputimeinstate')
-rw-r--r--libs/cputimeinstate/Android.bp11
-rw-r--r--libs/cputimeinstate/cputimeinstate.cpp167
-rw-r--r--libs/cputimeinstate/cputimeinstate.h7
-rw-r--r--libs/cputimeinstate/testtimeinstate.cpp156
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