Loading core/java/com/android/internal/os/KernelSingleUidTimeReader.java +18 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,8 @@ public class KernelSingleUidTimeReader { private int mReadErrorCounter; @GuardedBy("this") private boolean mSingleUidCpuTimesAvailable = true; @GuardedBy("this") private boolean mBpfTimesAvailable = true; // We use the freq count obtained from /proc/uid_time_in_state to decide how many longs // to read from each /proc/uid/<uid>/time_in_state. On the first read, verify if this is // correct and if not, set {@link #mSingleUidCpuTimesAvailable} to false. This flag will Loading @@ -62,6 +64,8 @@ public class KernelSingleUidTimeReader { private final Injector mInjector; private static final native boolean canReadBpfTimes(); KernelSingleUidTimeReader(int cpuFreqsCount) { this(cpuFreqsCount, new Injector()); } Loading @@ -83,6 +87,18 @@ public class KernelSingleUidTimeReader { if (!mSingleUidCpuTimesAvailable) { return null; } if (mBpfTimesAvailable) { final long[] cpuTimesMs = mInjector.readBpfData(uid); if (cpuTimesMs.length == 0) { mBpfTimesAvailable = false; } else if (!mCpuFreqsCountVerified && cpuTimesMs.length != mCpuFreqsCount) { mSingleUidCpuTimesAvailable = false; return null; } else { mCpuFreqsCountVerified = true; return computeDelta(uid, cpuTimesMs); } } // Read total cpu times from the proc file. final String procFile = new StringBuilder(PROC_FILE_DIR) .append(uid) Loading Loading @@ -230,6 +246,8 @@ public class KernelSingleUidTimeReader { public byte[] readData(String procFile) throws IOException { return Files.readAllBytes(Paths.get(procFile)); } public native long[] readBpfData(int uid); } @VisibleForTesting Loading core/jni/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -208,6 +208,7 @@ cc_library_shared { "com_android_internal_os_ClassLoaderFactory.cpp", "com_android_internal_os_FuseAppLoop.cpp", "com_android_internal_os_KernelCpuUidBpfMapReader.cpp", "com_android_internal_os_KernelSingleUidTimeReader.cpp", "com_android_internal_os_Zygote.cpp", "com_android_internal_os_ZygoteInit.cpp", "com_android_internal_util_VirtualRefBasePtr.cpp", Loading core/jni/AndroidRuntime.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -231,6 +231,7 @@ extern int register_com_android_internal_os_AtomicDirectory(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_KernelCpuUidBpfMapReader(JNIEnv *env); extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env); extern int register_com_android_internal_os_Zygote(JNIEnv *env); extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env); extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env); Loading Loading @@ -1642,6 +1643,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_com_android_internal_os_AtomicDirectory), REG_JNI(register_com_android_internal_os_FuseAppLoop), REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader), REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader), }; /* Loading core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * 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 <cputimeinstate.h> namespace android { static constexpr uint64_t NSEC_PER_MSEC = 1000000; static jlongArray copyVecsToArray(JNIEnv *env, std::vector<std::vector<uint64_t>> &vec) { jsize s = 0; for (const auto &subVec : vec) s += subVec.size(); jlongArray ar = env->NewLongArray(s); jsize start = 0; for (auto &subVec : vec) { for (uint32_t i = 0; i < subVec.size(); ++i) subVec[i] /= NSEC_PER_MSEC; env->SetLongArrayRegion(ar, start, subVec.size(), reinterpret_cast<const jlong*>(subVec.data())); start += subVec.size(); } return ar; } static jlongArray getUidCpuFreqTimeMs(JNIEnv *env, jclass, jint uid) { auto out = android::bpf::getUidCpuFreqTimes(uid); if (!out) return env->NewLongArray(0); return copyVecsToArray(env, out.value()); } static const JNINativeMethod g_single_methods[] = { {"readBpfData", "(I)[J", (void *)getUidCpuFreqTimeMs}, }; int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env) { return RegisterMethodsOrDie(env, "com/android/internal/os/KernelSingleUidTimeReader$Injector", g_single_methods, NELEM(g_single_methods)); } } core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java +24 −1 Original line number Diff line number Diff line Loading @@ -31,20 +31,33 @@ import com.android.internal.os.KernelSingleUidTimeReader.Injector; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Collection; @SmallTest @RunWith(AndroidJUnit4.class) @RunWith(Parameterized.class) public class KernelSingleUidTimeReaderTest { private final static int TEST_UID = 2222; private final static int TEST_FREQ_COUNT = 5; private KernelSingleUidTimeReader mReader; private TestInjector mInjector; protected boolean mUseBpf; @Parameters(name="useBpf={0}") public static Collection<Object[]> data() { return Arrays.asList(new Object[][] { {true}, {false} }); } public KernelSingleUidTimeReaderTest(boolean useBpf) { mUseBpf = useBpf; } @Before public void setUp() { Loading Loading @@ -273,6 +286,7 @@ public class KernelSingleUidTimeReaderTest { class TestInjector extends Injector { private byte[] mData; private long[] mBpfData; private boolean mThrowExcpetion; @Override Loading @@ -284,6 +298,14 @@ public class KernelSingleUidTimeReaderTest { } } @Override public long[] readBpfData(int uid) { if (!mUseBpf || mBpfData == null) { return new long[0]; } return mBpfData; } public void setData(long[] cpuTimes) { final ByteBuffer buffer = ByteBuffer.allocate(cpuTimes.length * Long.BYTES); buffer.order(ByteOrder.nativeOrder()); Loading @@ -291,6 +313,7 @@ public class KernelSingleUidTimeReaderTest { buffer.putLong(time / 10); } mData = buffer.array(); mBpfData = cpuTimes.clone(); } public void letReadDataThrowException(boolean throwException) { Loading Loading
core/java/com/android/internal/os/KernelSingleUidTimeReader.java +18 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,8 @@ public class KernelSingleUidTimeReader { private int mReadErrorCounter; @GuardedBy("this") private boolean mSingleUidCpuTimesAvailable = true; @GuardedBy("this") private boolean mBpfTimesAvailable = true; // We use the freq count obtained from /proc/uid_time_in_state to decide how many longs // to read from each /proc/uid/<uid>/time_in_state. On the first read, verify if this is // correct and if not, set {@link #mSingleUidCpuTimesAvailable} to false. This flag will Loading @@ -62,6 +64,8 @@ public class KernelSingleUidTimeReader { private final Injector mInjector; private static final native boolean canReadBpfTimes(); KernelSingleUidTimeReader(int cpuFreqsCount) { this(cpuFreqsCount, new Injector()); } Loading @@ -83,6 +87,18 @@ public class KernelSingleUidTimeReader { if (!mSingleUidCpuTimesAvailable) { return null; } if (mBpfTimesAvailable) { final long[] cpuTimesMs = mInjector.readBpfData(uid); if (cpuTimesMs.length == 0) { mBpfTimesAvailable = false; } else if (!mCpuFreqsCountVerified && cpuTimesMs.length != mCpuFreqsCount) { mSingleUidCpuTimesAvailable = false; return null; } else { mCpuFreqsCountVerified = true; return computeDelta(uid, cpuTimesMs); } } // Read total cpu times from the proc file. final String procFile = new StringBuilder(PROC_FILE_DIR) .append(uid) Loading Loading @@ -230,6 +246,8 @@ public class KernelSingleUidTimeReader { public byte[] readData(String procFile) throws IOException { return Files.readAllBytes(Paths.get(procFile)); } public native long[] readBpfData(int uid); } @VisibleForTesting Loading
core/jni/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -208,6 +208,7 @@ cc_library_shared { "com_android_internal_os_ClassLoaderFactory.cpp", "com_android_internal_os_FuseAppLoop.cpp", "com_android_internal_os_KernelCpuUidBpfMapReader.cpp", "com_android_internal_os_KernelSingleUidTimeReader.cpp", "com_android_internal_os_Zygote.cpp", "com_android_internal_os_ZygoteInit.cpp", "com_android_internal_util_VirtualRefBasePtr.cpp", Loading
core/jni/AndroidRuntime.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -231,6 +231,7 @@ extern int register_com_android_internal_os_AtomicDirectory(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_KernelCpuUidBpfMapReader(JNIEnv *env); extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env); extern int register_com_android_internal_os_Zygote(JNIEnv *env); extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env); extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env); Loading Loading @@ -1642,6 +1643,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_com_android_internal_os_AtomicDirectory), REG_JNI(register_com_android_internal_os_FuseAppLoop), REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader), REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader), }; /* Loading
core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * 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 <cputimeinstate.h> namespace android { static constexpr uint64_t NSEC_PER_MSEC = 1000000; static jlongArray copyVecsToArray(JNIEnv *env, std::vector<std::vector<uint64_t>> &vec) { jsize s = 0; for (const auto &subVec : vec) s += subVec.size(); jlongArray ar = env->NewLongArray(s); jsize start = 0; for (auto &subVec : vec) { for (uint32_t i = 0; i < subVec.size(); ++i) subVec[i] /= NSEC_PER_MSEC; env->SetLongArrayRegion(ar, start, subVec.size(), reinterpret_cast<const jlong*>(subVec.data())); start += subVec.size(); } return ar; } static jlongArray getUidCpuFreqTimeMs(JNIEnv *env, jclass, jint uid) { auto out = android::bpf::getUidCpuFreqTimes(uid); if (!out) return env->NewLongArray(0); return copyVecsToArray(env, out.value()); } static const JNINativeMethod g_single_methods[] = { {"readBpfData", "(I)[J", (void *)getUidCpuFreqTimeMs}, }; int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env) { return RegisterMethodsOrDie(env, "com/android/internal/os/KernelSingleUidTimeReader$Injector", g_single_methods, NELEM(g_single_methods)); } }
core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java +24 −1 Original line number Diff line number Diff line Loading @@ -31,20 +31,33 @@ import com.android.internal.os.KernelSingleUidTimeReader.Injector; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Collection; @SmallTest @RunWith(AndroidJUnit4.class) @RunWith(Parameterized.class) public class KernelSingleUidTimeReaderTest { private final static int TEST_UID = 2222; private final static int TEST_FREQ_COUNT = 5; private KernelSingleUidTimeReader mReader; private TestInjector mInjector; protected boolean mUseBpf; @Parameters(name="useBpf={0}") public static Collection<Object[]> data() { return Arrays.asList(new Object[][] { {true}, {false} }); } public KernelSingleUidTimeReaderTest(boolean useBpf) { mUseBpf = useBpf; } @Before public void setUp() { Loading Loading @@ -273,6 +286,7 @@ public class KernelSingleUidTimeReaderTest { class TestInjector extends Injector { private byte[] mData; private long[] mBpfData; private boolean mThrowExcpetion; @Override Loading @@ -284,6 +298,14 @@ public class KernelSingleUidTimeReaderTest { } } @Override public long[] readBpfData(int uid) { if (!mUseBpf || mBpfData == null) { return new long[0]; } return mBpfData; } public void setData(long[] cpuTimes) { final ByteBuffer buffer = ByteBuffer.allocate(cpuTimes.length * Long.BYTES); buffer.order(ByteOrder.nativeOrder()); Loading @@ -291,6 +313,7 @@ public class KernelSingleUidTimeReaderTest { buffer.putLong(time / 10); } mData = buffer.array(); mBpfData = cpuTimes.clone(); } public void letReadDataThrowException(boolean throwException) { Loading