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

Commit e7f1410a authored by Rafal Slawik's avatar Rafal Slawik
Browse files

Manage time_in_state tracking from one place

Move functionality related to CPU time_in_state tracking to the
KernelCpuBpfTracking class:
- when to attach the eBPF program to start tracking
- provide available CPU frequencies
- provide CPU cluster (policy) information
- unify the interface of KernelCpuTotalBpfMapReader with other readers

This removes the dependency on the power profile when reading the
information about available frequencies and clusters. It also reduces
the code duplication for transforming that information in
StatsPullAtomService.

Bug: 179485697
Test: existing tests pass
Test: atest CpuStatsTests
Test: cmd stats pull-source 10010
Test: cmd stats pull-source 10095
Test: cmd stats pull-source 10096
Test: cmd stats pull-source 10098
Test: atest FrameworksCoreTests
Change-Id: I17041014343da167b8bdf027ad83887e78ba5c2d
parent 2441a6bb
Loading
Loading
Loading
Loading
+69 −1
Original line number Diff line number Diff line
@@ -16,11 +16,79 @@

package com.android.internal.os;

/** CPU tracking using eBPF. */
import android.annotation.Nullable;

/**
 * CPU tracking using eBPF.
 *
 * The tracking state and data about available frequencies are cached to avoid JNI calls and
 * creating temporary arrays. The data is stored in a format that is convenient for metrics
 * computation.
 *
 * Synchronization is not needed because the underlying native library can be invoked concurrently
 * and getters are idempotent.
 */
public final class KernelCpuBpfTracking {
    private static boolean sTracking = false;

    /** Cached mapping from frequency index to frequency in kHz. */
    private static long[] sFreqs = null;

    /** Cached mapping from frequency index to CPU cluster / policy. */
    private static int[] sFreqsClusters = null;

    private KernelCpuBpfTracking() {
    }

    /** Returns whether CPU tracking using eBPF is supported. */
    public static native boolean isSupported();

    /** Starts CPU tracking using eBPF. */
    public static boolean startTracking() {
        if (!sTracking) {
            sTracking = startTrackingInternal();
        }
        return sTracking;
    }

    private static native boolean startTrackingInternal();

    /** Returns frequencies in kHz on which CPU is tracked. Empty if not supported. */
    public static long[] getFreqs() {
        if (sFreqs == null) {
            long[] freqs = getFreqsInternal();
            if (freqs == null) {
                return new long[0];
            }
            sFreqs = freqs;
        }
        return sFreqs;
    }

    @Nullable
    static native long[] getFreqsInternal();

    /**
     * Returns the cluster (policy) number  for each frequency on which CPU is tracked. Empty if
     * not supported.
     */
    public static int[] getFreqsClusters() {
        if (sFreqsClusters == null) {
            int[] freqsClusters = getFreqsClustersInternal();
            if (freqsClusters == null) {
                return new int[0];
            }
            sFreqsClusters = freqsClusters;
        }
        return sFreqsClusters;
    }

    @Nullable
    private static native int[] getFreqsClustersInternal();

    /** Returns the number of clusters (policies). */
    public static int getClusters() {
        int[] freqClusters = getFreqsClusters();
        return freqClusters.length > 0 ? freqClusters[freqClusters.length - 1] + 1 : 0;
    }
}
+13 −13
Original line number Diff line number Diff line
@@ -16,22 +16,22 @@

package com.android.internal.os;

/**
 * Reads total CPU time bpf map.
 */
import android.annotation.Nullable;

/** Reads total CPU time bpf map. */
public final class KernelCpuTotalBpfMapReader {
    private KernelCpuTotalBpfMapReader() {
    }

    /** Reads total CPU time from bpf map. */
    public static native boolean read(Callback callback);

    /** Callback accepting values read from bpf map. */
    public interface Callback {
        /**
         * Accepts values read from bpf map: cluster index, frequency in kilohertz and time in
         * milliseconds that the cpu cluster spent at the frequency (excluding sleep).
         */
        void accept(int cluster, int freqKhz, long timeMs);
    /** Reads total CPU times (excluding sleep) per frequency in milliseconds from bpf map. */
    @Nullable
    public static long[] read() {
        if (!KernelCpuBpfTracking.startTracking()) {
            return null;
        }
        return readInternal();
    }

    @Nullable
    private static native long[] readInternal();
}
+7 −4
Original line number Diff line number Diff line
@@ -68,14 +68,15 @@ public abstract class KernelCpuUidBpfMapReader {

    final String mTag = this.getClass().getSimpleName();
    private int mErrors = 0;
    private boolean mTracking = false;
    protected SparseArray<long[]> mData = new SparseArray<>();
    private long mLastReadTime = 0;
    protected final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
    protected final ReentrantReadWriteLock.ReadLock mReadLock = mLock.readLock();
    protected final ReentrantReadWriteLock.WriteLock mWriteLock = mLock.writeLock();

    public native boolean startTrackingBpfTimes();
    public boolean startTrackingBpfTimes() {
        return KernelCpuBpfTracking.startTracking();
    }

    protected abstract boolean readBpfData();

@@ -116,7 +117,7 @@ public abstract class KernelCpuUidBpfMapReader {
        if (mErrors > ERROR_THRESHOLD) {
            return null;
        }
        if (!mTracking && !startTrackingBpfTimes()) {
        if (!startTrackingBpfTimes()) {
            Slog.w(mTag, "Failed to start tracking");
            mErrors++;
            return null;
@@ -182,7 +183,9 @@ public abstract class KernelCpuUidBpfMapReader {
        protected final native boolean readBpfData();

        @Override
        public final native long[] getDataDimensions();
        public final long[] getDataDimensions() {
            return KernelCpuBpfTracking.getFreqsInternal();
        }

        @Override
        public void removeUidsInRange(int startUid, int endUid) {
+40 −0
Original line number Diff line number Diff line
@@ -24,8 +24,48 @@ static jboolean KernelCpuBpfTracking_isSupported(JNIEnv *, jobject) {
    return android::bpf::isTrackingUidTimesSupported() ? JNI_TRUE : JNI_FALSE;
}

static jboolean KernelCpuBpfTracking_startTrackingInternal(JNIEnv *, jobject) {
    return android::bpf::startTrackingUidTimes();
}

static jlongArray KernelCpuBpfTracking_getFreqsInternal(JNIEnv *env, jobject) {
    auto freqs = android::bpf::getCpuFreqs();
    if (!freqs) return NULL;

    std::vector<uint64_t> allFreqs;
    for (const auto &vec : *freqs) std::copy(vec.begin(), vec.end(), std::back_inserter(allFreqs));

    auto ar = env->NewLongArray(allFreqs.size());
    if (ar != NULL) {
        env->SetLongArrayRegion(ar, 0, allFreqs.size(),
                                reinterpret_cast<const jlong *>(allFreqs.data()));
    }
    return ar;
}

static jintArray KernelCpuBpfTracking_getFreqsClustersInternal(JNIEnv *env, jobject) {
    auto freqs = android::bpf::getCpuFreqs();
    if (!freqs) return NULL;

    std::vector<uint32_t> freqsClusters;
    uint32_t clusters = freqs->size();
    for (uint32_t c = 0; c < clusters; ++c) {
        freqsClusters.insert(freqsClusters.end(), (*freqs)[c].size(), c);
    }

    auto ar = env->NewIntArray(freqsClusters.size());
    if (ar != NULL) {
        env->SetIntArrayRegion(ar, 0, freqsClusters.size(),
                               reinterpret_cast<const jint *>(freqsClusters.data()));
    }
    return ar;
}

static const JNINativeMethod methods[] = {
        {"isSupported", "()Z", (void *)KernelCpuBpfTracking_isSupported},
        {"startTrackingInternal", "()Z", (void *)KernelCpuBpfTracking_startTrackingInternal},
        {"getFreqsInternal", "()[J", (void *)KernelCpuBpfTracking_getFreqsInternal},
        {"getFreqsClustersInternal", "()[I", (void *)KernelCpuBpfTracking_getFreqsClustersInternal},
};

int register_com_android_internal_os_KernelCpuBpfTracking(JNIEnv *env) {
+13 −19
Original line number Diff line number Diff line
@@ -20,33 +20,27 @@

namespace android {

static jboolean KernelCpuTotalBpfMapReader_read(JNIEnv *env, jobject, jobject callback) {
    jclass callbackClass = env->GetObjectClass(callback);
    jmethodID callbackMethod = env->GetMethodID(callbackClass, "accept", "(IIJ)V");
    if (callbackMethod == 0) {
        return JNI_FALSE;
    }

    auto freqs = android::bpf::getCpuFreqs();
    if (!freqs) return JNI_FALSE;
static jlongArray KernelCpuTotalBpfMapReader_readInternal(JNIEnv *env, jobject) {
    auto freqTimes = android::bpf::getTotalCpuFreqTimes();
    if (!freqTimes) return JNI_FALSE;

    auto freqsClusterSize = (*freqs).size();
    for (uint32_t clusterIndex = 0; clusterIndex < freqsClusterSize; ++clusterIndex) {
        auto freqsSize = (*freqs)[clusterIndex].size();
        for (uint32_t freqIndex = 0; freqIndex < freqsSize; ++freqIndex) {
            env->CallVoidMethod(callback, callbackMethod, clusterIndex,
                                (*freqs)[clusterIndex][freqIndex],
                                (*freqTimes)[clusterIndex][freqIndex] / 1000000);
    std::vector<uint64_t> allTimes;
    for (const auto &vec : *freqTimes) {
        for (const auto &timeNs : vec) {
            allTimes.push_back(timeNs / 1000000);
        }
    }
    return JNI_TRUE;

    auto ar = env->NewLongArray(allTimes.size());
    if (ar != NULL) {
        env->SetLongArrayRegion(ar, 0, allTimes.size(),
                                reinterpret_cast<const jlong *>(allTimes.data()));
    }
    return ar;
}

static const JNINativeMethod methods[] = {
        {"read", "(Lcom/android/internal/os/KernelCpuTotalBpfMapReader$Callback;)Z",
         (void *)KernelCpuTotalBpfMapReader_read},
        {"readInternal", "()[J", (void *)KernelCpuTotalBpfMapReader_readInternal},
};

int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv *env) {
Loading