From f278e21a61b7ee5b4a537f457e016785f0b08981 Mon Sep 17 00:00:00 2001 From: Rafal Slawik Date: Thu, 10 Dec 2020 18:02:56 +0000 Subject: [PATCH] Measure total time in state Total CPU time in state added in https://r.android.com/1538886. Bug: 174245730 Test: cmd stats pull-source 10095 Change-Id: I6cd27ac172e36d2e889429808bd4bd583795d7c9 --- .../os/KernelCpuTotalBpfMapReader.java | 43 ++++++++++++++ core/jni/Android.bp | 1 + core/jni/AndroidRuntime.cpp | 2 + ...internal_os_KernelCpuTotalBpfMapReader.cpp | 57 +++++++++++++++++++ .../stats/pull/StatsPullAtomService.java | 32 +++++++++++ 5 files changed, 135 insertions(+) create mode 100644 core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java create mode 100644 core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp diff --git a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java new file mode 100644 index 0000000000000..fa552e3603c6b --- /dev/null +++ b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +/** + * Reads total CPU time bpf map. + */ +public final class KernelCpuTotalBpfMapReader { + private KernelCpuTotalBpfMapReader() { + } + + /** Returns whether total CPU time is measured. */ + public static boolean isSupported() { + // TODO(b/174245730): Implement this check. + return true; + } + + /** 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); + } +} diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 94ef64f43ce27..0b48e72a4acda 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -184,6 +184,7 @@ cc_library_shared { "com_android_internal_net_NetworkUtilsInternal.cpp", "com_android_internal_os_ClassLoaderFactory.cpp", "com_android_internal_os_FuseAppLoop.cpp", + "com_android_internal_os_KernelCpuTotalBpfMapReader.cpp", "com_android_internal_os_KernelCpuUidBpfMapReader.cpp", "com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp", "com_android_internal_os_KernelSingleUidTimeReader.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index cefa88c19901d..8879111bc2f56 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -190,6 +190,7 @@ extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env); extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env); extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env); extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env); +extern int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv* env); extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env); extern int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv* env); extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env); @@ -1585,6 +1586,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_security_Scrypt), REG_JNI(register_com_android_internal_content_NativeLibraryHelper), REG_JNI(register_com_android_internal_os_FuseAppLoop), + REG_JNI(register_com_android_internal_os_KernelCpuTotalBpfMapReader), REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader), REG_JNI(register_com_android_internal_os_KernelSingleProcessCpuThreadReader), REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader), diff --git a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp new file mode 100644 index 0000000000000..72492381e31ac --- /dev/null +++ b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "core_jni_helpers.h" + +#include + +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; + 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); + } + } + return JNI_TRUE; +} + +static const JNINativeMethod methods[] = { + {"read", "(Lcom/android/internal/os/KernelCpuTotalBpfMapReader$Callback;)Z", + (void *)KernelCpuTotalBpfMapReader_read}, +}; + +int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv *env) { + return RegisterMethodsOrDie(env, "com/android/internal/os/KernelCpuTotalBpfMapReader", methods, + NELEM(methods)); +} + +} // namespace android 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 d65661771a846..e7cda028a0b5e 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -141,6 +141,7 @@ import com.android.internal.os.BinderCallsStats.ExportedCallStat; import com.android.internal.os.KernelCpuThreadReader; import com.android.internal.os.KernelCpuThreadReaderDiff; import com.android.internal.os.KernelCpuThreadReaderSettingsObserver; +import com.android.internal.os.KernelCpuTotalBpfMapReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader; @@ -350,6 +351,7 @@ public class StatsPullAtomService extends SystemService { private final Object mDataBytesTransferLock = new Object(); private final Object mBluetoothBytesTransferLock = new Object(); private final Object mKernelWakelockLock = new Object(); + private final Object mCpuTimePerClusterFreqLock = new Object(); private final Object mCpuTimePerUidLock = new Object(); private final Object mCpuTimePerUidFreqLock = new Object(); private final Object mCpuActiveTimeLock = new Object(); @@ -438,6 +440,10 @@ public class StatsPullAtomService extends SystemService { synchronized (mKernelWakelockLock) { return pullKernelWakelockLocked(atomTag, data); } + case FrameworkStatsLog.CPU_TIME_PER_CLUSTER_FREQ: + synchronized (mCpuTimePerClusterFreqLock) { + return pullCpuTimePerClusterFreqLocked(atomTag, data); + } case FrameworkStatsLog.CPU_TIME_PER_UID: synchronized (mCpuTimePerUidLock) { return pullCpuTimePerUidLocked(atomTag, data); @@ -773,6 +779,7 @@ public class StatsPullAtomService extends SystemService { mStatsCallbackImpl = new StatsPullAtomCallbackImpl(); registerBluetoothBytesTransfer(); registerKernelWakelock(); + registerCpuTimePerClusterFreq(); registerCpuTimePerUid(); registerCpuCyclesPerUidCluster(); registerCpuTimePerUidFreq(); @@ -1444,6 +1451,31 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } + private void registerCpuTimePerClusterFreq() { + if (KernelCpuTotalBpfMapReader.isSupported()) { + int tagId = FrameworkStatsLog.CPU_TIME_PER_CLUSTER_FREQ; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {3}) + .build(); + mStatsManager.setPullAtomCallback( + tagId, + metadata, + DIRECT_EXECUTOR, + mStatsCallbackImpl + ); + } + } + + int pullCpuTimePerClusterFreqLocked(int atomTag, List pulledData) { + boolean success = KernelCpuTotalBpfMapReader.read((cluster, freq, timeMs) -> { + pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, cluster, freq, timeMs)); + }); + if (!success) { + return StatsManager.PULL_SKIP; + } + return StatsManager.PULL_SUCCESS; + } + private void registerCpuTimePerUid() { int tagId = FrameworkStatsLog.CPU_TIME_PER_UID; PullAtomMetadata metadata = new PullAtomMetadata.Builder()