Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 27c48db1 authored by Rafal Slawik's avatar Rafal Slawik
Browse files

Measure total time in state

This measure is not affected by uid removals and is monotonic. That
makes it a good fit for CPU telemetry in statsd.

Bug: 174245730
Test: manually compare total with uid breakdown
Test: atest libtimeinstate_test
Change-Id: I8f17348f6eed49474826a1bd1cbd150207171e2c
parent dad360f4
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;

@@ -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, ...],
+1 −0
Original line number Diff line number Diff line
@@ -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();
+47 −0
Original line number Diff line number Diff line
@@ -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());
@@ -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)};
@@ -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());