diff options
-rw-r--r-- | libs/cputimeinstate/cputimeinstate.cpp | 134 | ||||
-rw-r--r-- | libs/cputimeinstate/testtimeinstate.cpp | 6 | ||||
-rw-r--r-- | libs/cputimeinstate/timeinstate.h | 11 |
3 files changed, 91 insertions, 60 deletions
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 6f50a1e623..4c8d52efd9 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -49,6 +49,7 @@ namespace bpf { static std::mutex gInitializedMutex; static bool gInitialized = false; static uint32_t gNPolicies = 0; +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; @@ -86,6 +87,8 @@ static bool initGlobals() { std::lock_guard<std::mutex> guard(gInitializedMutex); if (gInitialized) return true; + gNCpus = get_nprocs_conf(); + struct dirent **dirlist; const char basepath[] = "/sys/devices/system/cpu/cpufreq"; int ret = scandir(basepath, &dirlist, isPolicyFile, comparePolicyFiles); @@ -152,6 +155,21 @@ bool startTrackingUidCpuFreqTimes() { } } + unique_fd fd2(bpf_obj_get(BPF_FS_PATH "map_time_in_state_freq_to_idx_map")); + if (fd2 < 0) return false; + freq_idx_key_t key; + for (uint32_t i = 0; i < gNPolicies; ++i) { + key.policy = i; + for (uint32_t j = 0; j < gPolicyFreqs[i].size(); ++j) { + key.freq = gPolicyFreqs[i][j]; + // Start indexes at 1 so that uninitialized state is distinguishable from lowest freq. + // The uid_times map still uses 0-based indexes, and the sched_switch program handles + // conversion between them, so this does not affect our map reading code. + uint32_t idx = j + 1; + if (writeToMapEntry(fd2, &key, &idx, BPF_ANY)) return false; + } + } + return attachTracepointProgram("sched", "sched_switch") && attachTracepointProgram("power", "cpu_frequency"); } @@ -163,30 +181,33 @@ bool startTrackingUidCpuFreqTimes() { // where ti_j is the ns that uid spent running on the ith cluster at that cluster's jth lowest freq. std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid) { if (!gInitialized && !initGlobals()) return {}; - time_key_t key = {.uid = uid, .freq = 0}; - - std::vector<std::vector<uint64_t>> out(gNPolicies); - std::vector<uint32_t> idxs(gNPolicies, 0); - - val_t value; - for (uint32_t freq : gAllFreqs) { - key.freq = freq; - int ret = findMapEntry(gMapFd, &key, &value); - if (ret) { - if (errno == ENOENT) - memset(&value.ar, 0, sizeof(value.ar)); - else - 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<val_t> vals(gNCpus); + time_key_t key = {.uid = uid}; + for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) { + key.bucket = i; + if (findMapEntry(gMapFd, &key, vals.data())) { + if (errno != ENOENT) return {}; + continue; } - for (uint32_t i = 0; i < gNPolicies; ++i) { - uint64_t time = 0; - for (uint32_t cpu : gPolicyCpus[i]) time += value.ar[cpu]; - if (idxs[i] == gPolicyFreqs[i].size() || freq != gPolicyFreqs[i][idxs[i]]) { - if (time != 0) return {}; - else continue; + + auto offset = i * FREQS_PER_ENTRY; + auto nextOffset = (i + 1) * FREQS_PER_ENTRY; + for (uint32_t j = 0; j < gNPolicies; ++j) { + if (offset >= gPolicyFreqs[j].size()) continue; + auto begin = out[j].begin() + offset; + auto end = nextOffset < gPolicyFreqs[j].size() ? begin + FREQS_PER_ENTRY : out[j].end(); + + for (const auto &cpu : gPolicyCpus[j]) { + std::transform(begin, end, std::begin(vals[cpu].ar), begin, std::plus<uint64_t>()); } - idxs[i] += 1; - out[i].emplace_back(time); } } @@ -202,49 +223,52 @@ std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t ui std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> getUidsCpuFreqTimes() { if (!gInitialized && !initGlobals()) return {}; + time_key_t key, prevKey; + std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> map; + if (getFirstMapKey(gMapFd, &key)) { + if (errno == ENOENT) return map; + return std::nullopt; + } - int fd = bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times_map"); - if (fd < 0) return {}; - BpfMap<time_key_t, val_t> m(fd); + std::vector<std::vector<uint64_t>> mapFormat; + for (const auto &freqList : gPolicyFreqs) mapFormat.emplace_back(freqList.size(), 0); - std::vector<std::unordered_map<uint32_t, uint32_t>> policyFreqIdxs; - for (uint32_t i = 0; i < gNPolicies; ++i) { - std::unordered_map<uint32_t, uint32_t> freqIdxs; - for (size_t j = 0; j < gPolicyFreqs[i].size(); ++j) freqIdxs[gPolicyFreqs[i][j]] = j; - policyFreqIdxs.emplace_back(freqIdxs); - } - std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> map; - auto fn = [&map, &policyFreqIdxs](const time_key_t &key, const val_t &val, - const BpfMap<time_key_t, val_t> &) { - if (map.find(key.uid) == map.end()) { - map[key.uid].resize(gNPolicies); - for (uint32_t i = 0; i < gNPolicies; ++i) { - map[key.uid][i].resize(gPolicyFreqs[i].size(), 0); - } - } + std::vector<val_t> vals(gNCpus); + do { + if (findMapEntry(gMapFd, &key, vals.data())) return {}; + if (map.find(key.uid) == map.end()) map.emplace(key.uid, mapFormat); - for (size_t policy = 0; policy < gNPolicies; ++policy) { - uint64_t time = 0; - for (const auto &cpu : gPolicyCpus[policy]) time += val.ar[cpu]; - if (!time) continue; - auto it = policyFreqIdxs[policy].find(key.freq); - if (it == policyFreqIdxs[policy].end()) return android::netdutils::Status(-1); - map[key.uid][policy][it->second] += time; + auto offset = key.bucket * FREQS_PER_ENTRY; + auto nextOffset = (key.bucket + 1) * FREQS_PER_ENTRY; + for (uint32_t i = 0; i < gNPolicies; ++i) { + if (offset >= gPolicyFreqs[i].size()) continue; + auto begin = map[key.uid][i].begin() + offset; + auto end = nextOffset < gPolicyFreqs[i].size() ? begin + FREQS_PER_ENTRY : + map[key.uid][i].end(); + for (const auto &cpu : gPolicyCpus[i]) { + std::transform(begin, end, std::begin(vals[cpu].ar), begin, std::plus<uint64_t>()); + } } - return android::netdutils::status::ok; - }; - if (isOk(m.iterateWithValue(fn))) return map; - return {}; + prevKey = key; + } while (!getNextMapKey(gMapFd, &prevKey, &key)); + if (errno != ENOENT) return {}; + return map; } // Clear all time in state data for a given uid. Returns false on error, true otherwise. bool clearUidCpuFreqTimes(uint32_t uid) { if (!gInitialized && !initGlobals()) return false; - time_key_t key = {.uid = uid, .freq = 0}; - std::vector<uint64_t> vals(get_nprocs_conf(), 0); - for (auto freq : gAllFreqs) { - key.freq = freq; + time_key_t key = {.uid = uid}; + + uint32_t maxFreqCount = 0; + for (const auto &freqList : gPolicyFreqs) { + if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size(); + } + + val_t zeros = {0}; + std::vector<val_t> vals(gNCpus, zeros); + for (key.bucket = 0; key.bucket <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++key.bucket) { if (writeToMapEntry(gMapFd, &key, vals.data(), BPF_EXIST) && errno != ENOENT) return false; if (deleteMapEntry(gMapFd, &key) && errno != ENOENT) return false; } diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 6347de166a..39007e4603 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -126,10 +126,10 @@ TEST(TimeInStateTest, RemoveUid) { ASSERT_GE(fd, 0); time_key_t k; ASSERT_FALSE(getFirstMapKey(fd, &k)); - val_t val; - ASSERT_FALSE(findMapEntry(fd, &k, &val)); + std::vector<val_t> vals(get_nprocs_conf()); + ASSERT_FALSE(findMapEntry(fd, &k, vals.data())); k.uid = uid; - ASSERT_FALSE(writeToMapEntry(fd, &k, &val, BPF_NOEXIST)); + ASSERT_FALSE(writeToMapEntry(fd, &k, vals.data(), BPF_NOEXIST)); } auto times = getUidCpuFreqTimes(uid); ASSERT_TRUE(times.has_value()); diff --git a/libs/cputimeinstate/timeinstate.h b/libs/cputimeinstate/timeinstate.h index cf66ae7077..41d0af07a2 100644 --- a/libs/cputimeinstate/timeinstate.h +++ b/libs/cputimeinstate/timeinstate.h @@ -18,11 +18,18 @@ #define BPF_FS_PATH "/sys/fs/bpf/" +#define FREQS_PER_ENTRY 32 + struct time_key_t { uint32_t uid; - uint32_t freq; + uint32_t bucket; }; struct val_t { - uint64_t ar[100]; + uint64_t ar[FREQS_PER_ENTRY]; +}; + +struct freq_idx_key_t { + uint32_t policy; + uint32_t freq; }; |