Merge "Manage time_in_state tracking from one place" into sc-dev

This commit is contained in:
Rafal Slawik
2021-02-22 19:44:45 +00:00
committed by Android (Google) Code Review
7 changed files with 189 additions and 134 deletions

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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] =
powerProfile.getAveragePowerForCpuCore(cluster, freqClusterIndex);
lastFreq = currFreq;
}
lastCluster = cluster;
clusters = cluster + 1;
freqsPowers[freqIndex] =
powerProfile.getAveragePowerForCpuCore(cluster, freqClusterIndex);
}
}
// 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(