diff options
7 files changed, 188 insertions, 133 deletions
diff --git a/core/java/com/android/internal/os/KernelCpuBpfTracking.java b/core/java/com/android/internal/os/KernelCpuBpfTracking.java index 28525478be05..387d3270f5e4 100644 --- a/core/java/com/android/internal/os/KernelCpuBpfTracking.java +++ b/core/java/com/android/internal/os/KernelCpuBpfTracking.java @@ -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; + } } diff --git a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java index 06760e140de1..553eda48e61d 100644 --- a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java +++ b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java @@ -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(); } diff --git a/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java index dafb924f37bd..52c0c3fa93df 100644 --- a/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java +++ b/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java @@ -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) { diff --git a/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp b/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp index e6a82f6d0cb5..6b41b2ec8f93 100644 --- a/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp +++ b/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp @@ -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) { diff --git a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp index 72492381e31a..ad43014d321f 100644 --- a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp +++ b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp @@ -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) { diff --git a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp index 7c68de504417..7900d301dbb0 100644 --- a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp +++ b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp @@ -82,25 +82,9 @@ static jboolean KernelCpuUidFreqTimeBpfMapReader_readBpfData(JNIEnv *env, jobjec return true; } -static jlongArray KernelCpuUidFreqTimeBpfMapReader_getDataDimensions(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 const JNINativeMethod gFreqTimeMethods[] = { {"removeUidRange", "(II)Z", (void *)KernelCpuUidFreqTimeBpfMapReader_removeUidRange}, {"readBpfData", "()Z", (void *)KernelCpuUidFreqTimeBpfMapReader_readBpfData}, - {"getDataDimensions", "()[J", (void *)KernelCpuUidFreqTimeBpfMapReader_getDataDimensions}, }; static jboolean KernelCpuUidActiveTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) { @@ -186,10 +170,6 @@ static const readerMethods gAllMethods[] = { {"KernelCpuUidClusterTimeBpfMapReader", gClusterTimeMethods, NELEM(gClusterTimeMethods)}, }; -static jboolean KernelCpuUidBpfMapReader_startTrackingBpfTimes(JNIEnv *, jobject) { - return android::bpf::startTrackingUidTimes(); -} - int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env) { gSparseArrayClassInfo.clazz = FindClassOrDie(env, "android/util/SparseArray"); gSparseArrayClassInfo.clazz = MakeGlobalRefOrDie(env, gSparseArrayClassInfo.clazz); @@ -198,14 +178,10 @@ int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env) { gSparseArrayClassInfo.get = GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "get", "(I)Ljava/lang/Object;"); constexpr auto readerName = "com/android/internal/os/KernelCpuUidBpfMapReader"; - constexpr JNINativeMethod method = {"startTrackingBpfTimes", "()Z", - (void *)KernelCpuUidBpfMapReader_startTrackingBpfTimes}; - - int ret = RegisterMethodsOrDie(env, readerName, &method, 1); - if (ret < 0) return ret; auto c = FindClassOrDie(env, readerName); gmData = GetFieldIDOrDie(env, c, "mData", "Landroid/util/SparseArray;"); + int ret = 0; for (const auto &m : gAllMethods) { auto fullName = android::base::StringPrintf("%s$%s", readerName, m.name); ret = RegisterMethodsOrDie(env, fullName.c_str(), m.methods, m.numMethods); diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 342dbfa3f59d..6aa7c8a290c1 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -1471,12 +1471,18 @@ public class StatsPullAtomService extends SystemService { } int pullCpuTimePerClusterFreqLocked(int atomTag, List<StatsEvent> pulledData) { - boolean success = KernelCpuTotalBpfMapReader.read((cluster, freq, timeMs) -> { - pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, cluster, freq, timeMs)); - }); - if (!success) { + int[] freqsClusters = KernelCpuBpfTracking.getFreqsClusters(); + long[] freqs = KernelCpuBpfTracking.getFreqs(); + long[] timesMs = KernelCpuTotalBpfMapReader.read(); + if (timesMs == null) { return StatsManager.PULL_SKIP; } + for (int freqIndex = 0; freqIndex < timesMs.length; ++freqIndex) { + int cluster = freqsClusters[freqIndex]; + long freq = freqs[freqIndex]; + long timeMs = timesMs[freqIndex]; + pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, cluster, freq, timeMs)); + } return StatsManager.PULL_SUCCESS; } @@ -1503,48 +1509,42 @@ public class StatsPullAtomService extends SystemService { } private void registerCpuCyclesPerUidCluster() { - int tagId = FrameworkStatsLog.CPU_CYCLES_PER_UID_CLUSTER; - PullAtomMetadata metadata = new PullAtomMetadata.Builder() - .setAdditiveFields(new int[] {3, 4, 5}) - .build(); - mStatsManager.setPullAtomCallback( - tagId, - metadata, - DIRECT_EXECUTOR, - mStatsCallbackImpl - ); + // If eBPF tracking is not support, the procfs fallback is used if the kernel knows about + // CPU frequencies. + if (KernelCpuBpfTracking.isSupported() || KernelCpuBpfTracking.getClusters() > 0) { + int tagId = FrameworkStatsLog.CPU_CYCLES_PER_UID_CLUSTER; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {3, 4, 5}) + .build(); + mStatsManager.setPullAtomCallback( + tagId, + metadata, + DIRECT_EXECUTOR, + mStatsCallbackImpl + ); + } } int pullCpuCyclesPerUidClusterLocked(int atomTag, List<StatsEvent> pulledData) { - // TODO(b/179485697): Remove power profile dependency. PowerProfile powerProfile = new PowerProfile(mContext); - // Frequency index to frequency mapping. - long[] freqs = mCpuUidFreqTimeReader.readFreqs(powerProfile); - // Frequency index to cluster mapping. - int[] freqClusters = new int[freqs.length]; - // Frequency index to power mapping. - double[] freqPowers = new double[freqs.length]; - // Number of clusters. - int clusters; - - // Initialize frequency mappings. + int[] freqsClusters = KernelCpuBpfTracking.getFreqsClusters(); + int clusters = KernelCpuBpfTracking.getClusters(); + long[] freqs = KernelCpuBpfTracking.getFreqs(); + double[] freqsPowers = new double[freqs.length]; + // Initialize frequency power mapping. { - int cluster = 0; int freqClusterIndex = 0; - long lastFreq = -1; + int lastCluster = -1; for (int freqIndex = 0; freqIndex < freqs.length; ++freqIndex, ++freqClusterIndex) { - long currFreq = freqs[freqIndex]; - if (currFreq <= lastFreq) { - cluster++; + int cluster = freqsClusters[freqIndex]; + if (cluster != lastCluster) { freqClusterIndex = 0; } - freqClusters[freqIndex] = cluster; - freqPowers[freqIndex] = + lastCluster = cluster; + + freqsPowers[freqIndex] = powerProfile.getAveragePowerForCpuCore(cluster, freqClusterIndex); - lastFreq = currFreq; } - - clusters = cluster + 1; } // Aggregate 0: mcycles, 1: runtime ms, 2: power profile estimate for the same uids for @@ -1570,12 +1570,12 @@ public class StatsPullAtomService extends SystemService { } for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) { - int cluster = freqClusters[freqIndex]; + int cluster = freqsClusters[freqIndex]; long timeMs = cpuFreqTimeMs[freqIndex]; values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES] += freqs[freqIndex] * timeMs; values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 1] += timeMs; values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 2] += - freqPowers[freqIndex] * timeMs; + freqsPowers[freqIndex] * timeMs; } }); @@ -1665,34 +1665,6 @@ public class StatsPullAtomService extends SystemService { } int pullCpuCyclesPerThreadGroupCluster(int atomTag, List<StatsEvent> pulledData) { - // TODO(b/179485697): Remove power profile dependency. - PowerProfile powerProfile = new PowerProfile(mContext); - // Frequency index to frequency mapping. - long[] freqs = mCpuUidFreqTimeReader.readFreqs(powerProfile); - if (freqs == null) { - return StatsManager.PULL_SKIP; - } - // Frequency index to cluster mapping. - int[] freqClusters = new int[freqs.length]; - // Number of clusters. - int clusters; - - // Initialize frequency mappings. - { - int cluster = 0; - long lastFreq = -1; - for (int freqIndex = 0; freqIndex < freqs.length; ++freqIndex) { - long currFreq = freqs[freqIndex]; - if (currFreq <= lastFreq) { - cluster++; - } - freqClusters[freqIndex] = cluster; - lastFreq = currFreq; - } - - clusters = cluster + 1; - } - SystemServiceCpuThreadTimes times = LocalServices.getService(BatteryStatsInternal.class) .getSystemServiceCpuThreadTimes(); if (times == null) { @@ -1701,22 +1673,24 @@ public class StatsPullAtomService extends SystemService { addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData, FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER, - times.threadCpuTimesUs, clusters, freqs, freqClusters); + times.threadCpuTimesUs); addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData, FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER_BINDER, - times.binderThreadCpuTimesUs, clusters, freqs, freqClusters); + times.binderThreadCpuTimesUs); return StatsManager.PULL_SUCCESS; } private static void addCpuCyclesPerThreadGroupClusterAtoms( - int atomTag, List<StatsEvent> pulledData, int threadGroup, long[] cpuTimesUs, - int clusters, long[] freqs, int[] freqClusters) { + int atomTag, List<StatsEvent> pulledData, int threadGroup, long[] cpuTimesUs) { + int[] freqsClusters = KernelCpuBpfTracking.getFreqsClusters(); + int clusters = KernelCpuBpfTracking.getClusters(); + long[] freqs = KernelCpuBpfTracking.getFreqs(); long[] aggregatedCycles = new long[clusters]; long[] aggregatedTimesUs = new long[clusters]; for (int i = 0; i < cpuTimesUs.length; ++i) { - aggregatedCycles[freqClusters[i]] += freqs[i] * cpuTimesUs[i] / 1_000; - aggregatedTimesUs[freqClusters[i]] += cpuTimesUs[i]; + aggregatedCycles[freqsClusters[i]] += freqs[i] * cpuTimesUs[i] / 1_000; + aggregatedTimesUs[freqsClusters[i]] += cpuTimesUs[i]; } for (int cluster = 0; cluster < clusters; ++cluster) { pulledData.add(FrameworkStatsLog.buildStatsEvent( |