Loading core/java/com/android/internal/os/KernelSingleUidTimeReader.java +28 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import dalvik.annotation.optimization.CriticalNative; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; Loading Loading @@ -248,6 +250,32 @@ public class KernelSingleUidTimeReader { } public native long[] readBpfData(int uid); /** * Reads CPU time-in-state data for the specified UID and adds the delta since the * previous call to the current state stats in the LongArrayMultiStateCounter. */ public boolean addDelta(int uid, LongArrayMultiStateCounter counter, long timestampMs) { return addDeltaFromBpf(uid, counter.mNativeObject, timestampMs); } @CriticalNative private static native boolean addDeltaFromBpf(int uid, long longArrayMultiStateCounterNativePointer, long timestampMs); /** * Used for testing. * * Takes mock cpu-time-in-frequency data and uses it the same way eBPF data would be used. */ public boolean addDeltaForTest(int uid, LongArrayMultiStateCounter counter, long timestampMs, long[][] timeInFreqDataNanos) { return addDeltaForTest(uid, counter.mNativeObject, timestampMs, timeInFreqDataNanos); } private static native boolean addDeltaForTest(int uid, long longArrayMultiStateCounterNativePointer, long timestampMs, long[][] timeInFreqDataNanos); } @VisibleForTesting Loading core/java/com/android/internal/os/LongArrayMultiStateCounter.java +5 −2 Original line number Diff line number Diff line Loading @@ -31,7 +31,7 @@ import libcore.util.NativeAllocationRegistry; * // At 1000 ms, the state changes to 1 * counter.setState(1, 1000); * * // At 3000 ms, the tracked values are updated to {100, 200} * // At 3000 ms, the tracked values are updated to {30, 300} * arrayContainer.setValues(new long[]{{30, 300}}; * counter.updateValues(arrayContainer, 3000); * Loading Loading @@ -107,7 +107,10 @@ public class LongArrayMultiStateCounter { private final int mStateCount; private final int mLength; private final long mNativeObject; // Visible to other objects in this package so that it can be passed to @CriticalNative // methods. final long mNativeObject; public LongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState, long timestampMs) { Loading core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp +67 −3 Original line number Diff line number Diff line Loading @@ -14,9 +14,11 @@ * limitations under the License. */ #include "core_jni_helpers.h" #include <cputimeinstate.h> #include <nativehelper/ScopedPrimitiveArray.h> #include "LongArrayMultiStateCounter.h" #include "core_jni_helpers.h" namespace android { Loading @@ -42,8 +44,70 @@ static jlongArray getUidCpuFreqTimeMs(JNIEnv *env, jclass, jint uid) { return copyVecsToArray(env, out.value()); } /** * Computes delta of CPU time-in-freq from the previously supplied counts and adds the delta * to the supplied multi-state counter in accordance with the counter's state. */ static jboolean addCpuTimeInFreqDelta( jint uid, jlong counterNativePtr, jlong timestampMs, std::optional<std::vector<std::vector<uint64_t>>> timeInFreqDataNanos) { if (!timeInFreqDataNanos) { return false; } battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(counterNativePtr); size_t s = 0; for (const auto &cluster : *timeInFreqDataNanos) s += cluster.size(); std::vector<uint64_t> flattened; flattened.reserve(s); auto offset = flattened.begin(); for (const auto &cluster : *timeInFreqDataNanos) { flattened.insert(offset, cluster.begin(), cluster.end()); offset += cluster.size(); } for (size_t i = 0; i < s; ++i) { flattened[i] /= NSEC_PER_MSEC; } counter->updateValue(flattened, timestampMs); return true; } static jboolean addDeltaFromBpf(jint uid, jlong counterNativePtr, jlong timestampMs) { return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs, android::bpf::getUidCpuFreqTimes(uid)); } static jboolean addDeltaForTest(JNIEnv *env, jclass, jint uid, jlong counterNativePtr, jlong timestampMs, jobjectArray timeInFreqDataNanos) { if (!timeInFreqDataNanos) { return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs, std::optional<std::vector<std::vector<uint64_t>>>()); } std::vector<std::vector<uint64_t>> timeInFreqData; jsize len = env->GetArrayLength(timeInFreqDataNanos); for (jsize i = 0; i < len; i++) { std::vector<uint64_t> cluster; ScopedLongArrayRO row(env, (jlongArray)env->GetObjectArrayElement(timeInFreqDataNanos, i)); cluster.reserve(row.size()); for (size_t j = 0; j < row.size(); j++) { cluster.push_back(row[j]); } timeInFreqData.push_back(cluster); } return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs, std::optional(timeInFreqData)); } static const JNINativeMethod g_single_methods[] = { {"readBpfData", "(I)[J", (void *)getUidCpuFreqTimeMs}, // @CriticalNative {"addDeltaFromBpf", "(IJJ)Z", (void *)addDeltaFromBpf}, // Used for testing {"addDeltaForTest", "(IJJ[[J)Z", (void *)addDeltaForTest}, }; int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env) { Loading core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java +52 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; Loading @@ -24,7 +26,6 @@ import static org.junit.Assert.assertTrue; import android.util.SparseArray; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.KernelSingleUidTimeReader.Injector; Loading Loading @@ -279,6 +280,46 @@ public class KernelSingleUidTimeReaderTest { 0, lastUidCpuTimes.size()); } @Test public void testAddDeltaFromBpf() { LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 5, 0, 0); // Nanoseconds mInjector.setCpuTimeInStatePerClusterNs( new long[][]{ {1_000_000, 2_000_000, 3_000_000}, {4_000_000, 5_000_000}}); boolean success = mInjector.addDelta(TEST_UID, counter, 2000); assertThat(success).isTrue(); LongArrayMultiStateCounter.LongArrayContainer array = new LongArrayMultiStateCounter.LongArrayContainer(5); long[] out = new long[5]; counter.getCounts(array, 0); array.getValues(out); assertThat(out).isEqualTo(new long[]{1, 2, 3, 4, 5}); counter.setState(1, 3000); mInjector.setCpuTimeInStatePerClusterNs( new long[][]{ {11_000_000, 22_000_000, 33_000_000}, {44_000_000, 55_000_000}}); success = mInjector.addDelta(TEST_UID, counter, 4000); assertThat(success).isTrue(); counter.getCounts(array, 0); array.getValues(out); assertThat(out).isEqualTo(new long[]{1 + 5, 2 + 10, 3 + 15, 4 + 20, 5 + 25}); counter.getCounts(array, 1); array.getValues(out); assertThat(out).isEqualTo(new long[]{5, 10, 15, 20, 25}); } private void assertCpuTimesEqual(long[] expected, long[] actual) { assertArrayEquals("Expected=" + Arrays.toString(expected) + ", Actual=" + Arrays.toString(actual), expected, actual); Loading @@ -288,6 +329,7 @@ public class KernelSingleUidTimeReaderTest { private byte[] mData; private long[] mBpfData; private boolean mThrowExcpetion; private long[][] mCpuTimeInStatePerClusterNs; @Override public byte[] readData(String procFile) throws IOException { Loading Loading @@ -316,8 +358,17 @@ public class KernelSingleUidTimeReaderTest { mBpfData = cpuTimes.clone(); } public void setCpuTimeInStatePerClusterNs(long[][] cpuTimeInStatePerClusterNs) { mCpuTimeInStatePerClusterNs = cpuTimeInStatePerClusterNs; } public void letReadDataThrowException(boolean throwException) { mThrowExcpetion = throwException; } @Override public boolean addDelta(int uid, LongArrayMultiStateCounter counter, long timestampMs) { return addDeltaForTest(uid, counter, timestampMs, mCpuTimeInStatePerClusterNs); } } } Loading
core/java/com/android/internal/os/KernelSingleUidTimeReader.java +28 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import dalvik.annotation.optimization.CriticalNative; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; Loading Loading @@ -248,6 +250,32 @@ public class KernelSingleUidTimeReader { } public native long[] readBpfData(int uid); /** * Reads CPU time-in-state data for the specified UID and adds the delta since the * previous call to the current state stats in the LongArrayMultiStateCounter. */ public boolean addDelta(int uid, LongArrayMultiStateCounter counter, long timestampMs) { return addDeltaFromBpf(uid, counter.mNativeObject, timestampMs); } @CriticalNative private static native boolean addDeltaFromBpf(int uid, long longArrayMultiStateCounterNativePointer, long timestampMs); /** * Used for testing. * * Takes mock cpu-time-in-frequency data and uses it the same way eBPF data would be used. */ public boolean addDeltaForTest(int uid, LongArrayMultiStateCounter counter, long timestampMs, long[][] timeInFreqDataNanos) { return addDeltaForTest(uid, counter.mNativeObject, timestampMs, timeInFreqDataNanos); } private static native boolean addDeltaForTest(int uid, long longArrayMultiStateCounterNativePointer, long timestampMs, long[][] timeInFreqDataNanos); } @VisibleForTesting Loading
core/java/com/android/internal/os/LongArrayMultiStateCounter.java +5 −2 Original line number Diff line number Diff line Loading @@ -31,7 +31,7 @@ import libcore.util.NativeAllocationRegistry; * // At 1000 ms, the state changes to 1 * counter.setState(1, 1000); * * // At 3000 ms, the tracked values are updated to {100, 200} * // At 3000 ms, the tracked values are updated to {30, 300} * arrayContainer.setValues(new long[]{{30, 300}}; * counter.updateValues(arrayContainer, 3000); * Loading Loading @@ -107,7 +107,10 @@ public class LongArrayMultiStateCounter { private final int mStateCount; private final int mLength; private final long mNativeObject; // Visible to other objects in this package so that it can be passed to @CriticalNative // methods. final long mNativeObject; public LongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState, long timestampMs) { Loading
core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp +67 −3 Original line number Diff line number Diff line Loading @@ -14,9 +14,11 @@ * limitations under the License. */ #include "core_jni_helpers.h" #include <cputimeinstate.h> #include <nativehelper/ScopedPrimitiveArray.h> #include "LongArrayMultiStateCounter.h" #include "core_jni_helpers.h" namespace android { Loading @@ -42,8 +44,70 @@ static jlongArray getUidCpuFreqTimeMs(JNIEnv *env, jclass, jint uid) { return copyVecsToArray(env, out.value()); } /** * Computes delta of CPU time-in-freq from the previously supplied counts and adds the delta * to the supplied multi-state counter in accordance with the counter's state. */ static jboolean addCpuTimeInFreqDelta( jint uid, jlong counterNativePtr, jlong timestampMs, std::optional<std::vector<std::vector<uint64_t>>> timeInFreqDataNanos) { if (!timeInFreqDataNanos) { return false; } battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(counterNativePtr); size_t s = 0; for (const auto &cluster : *timeInFreqDataNanos) s += cluster.size(); std::vector<uint64_t> flattened; flattened.reserve(s); auto offset = flattened.begin(); for (const auto &cluster : *timeInFreqDataNanos) { flattened.insert(offset, cluster.begin(), cluster.end()); offset += cluster.size(); } for (size_t i = 0; i < s; ++i) { flattened[i] /= NSEC_PER_MSEC; } counter->updateValue(flattened, timestampMs); return true; } static jboolean addDeltaFromBpf(jint uid, jlong counterNativePtr, jlong timestampMs) { return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs, android::bpf::getUidCpuFreqTimes(uid)); } static jboolean addDeltaForTest(JNIEnv *env, jclass, jint uid, jlong counterNativePtr, jlong timestampMs, jobjectArray timeInFreqDataNanos) { if (!timeInFreqDataNanos) { return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs, std::optional<std::vector<std::vector<uint64_t>>>()); } std::vector<std::vector<uint64_t>> timeInFreqData; jsize len = env->GetArrayLength(timeInFreqDataNanos); for (jsize i = 0; i < len; i++) { std::vector<uint64_t> cluster; ScopedLongArrayRO row(env, (jlongArray)env->GetObjectArrayElement(timeInFreqDataNanos, i)); cluster.reserve(row.size()); for (size_t j = 0; j < row.size(); j++) { cluster.push_back(row[j]); } timeInFreqData.push_back(cluster); } return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs, std::optional(timeInFreqData)); } static const JNINativeMethod g_single_methods[] = { {"readBpfData", "(I)[J", (void *)getUidCpuFreqTimeMs}, // @CriticalNative {"addDeltaFromBpf", "(IJJ)Z", (void *)addDeltaFromBpf}, // Used for testing {"addDeltaForTest", "(IJJ[[J)Z", (void *)addDeltaForTest}, }; int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env) { Loading
core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java +52 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; Loading @@ -24,7 +26,6 @@ import static org.junit.Assert.assertTrue; import android.util.SparseArray; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.KernelSingleUidTimeReader.Injector; Loading Loading @@ -279,6 +280,46 @@ public class KernelSingleUidTimeReaderTest { 0, lastUidCpuTimes.size()); } @Test public void testAddDeltaFromBpf() { LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 5, 0, 0); // Nanoseconds mInjector.setCpuTimeInStatePerClusterNs( new long[][]{ {1_000_000, 2_000_000, 3_000_000}, {4_000_000, 5_000_000}}); boolean success = mInjector.addDelta(TEST_UID, counter, 2000); assertThat(success).isTrue(); LongArrayMultiStateCounter.LongArrayContainer array = new LongArrayMultiStateCounter.LongArrayContainer(5); long[] out = new long[5]; counter.getCounts(array, 0); array.getValues(out); assertThat(out).isEqualTo(new long[]{1, 2, 3, 4, 5}); counter.setState(1, 3000); mInjector.setCpuTimeInStatePerClusterNs( new long[][]{ {11_000_000, 22_000_000, 33_000_000}, {44_000_000, 55_000_000}}); success = mInjector.addDelta(TEST_UID, counter, 4000); assertThat(success).isTrue(); counter.getCounts(array, 0); array.getValues(out); assertThat(out).isEqualTo(new long[]{1 + 5, 2 + 10, 3 + 15, 4 + 20, 5 + 25}); counter.getCounts(array, 1); array.getValues(out); assertThat(out).isEqualTo(new long[]{5, 10, 15, 20, 25}); } private void assertCpuTimesEqual(long[] expected, long[] actual) { assertArrayEquals("Expected=" + Arrays.toString(expected) + ", Actual=" + Arrays.toString(actual), expected, actual); Loading @@ -288,6 +329,7 @@ public class KernelSingleUidTimeReaderTest { private byte[] mData; private long[] mBpfData; private boolean mThrowExcpetion; private long[][] mCpuTimeInStatePerClusterNs; @Override public byte[] readData(String procFile) throws IOException { Loading Loading @@ -316,8 +358,17 @@ public class KernelSingleUidTimeReaderTest { mBpfData = cpuTimes.clone(); } public void setCpuTimeInStatePerClusterNs(long[][] cpuTimeInStatePerClusterNs) { mCpuTimeInStatePerClusterNs = cpuTimeInStatePerClusterNs; } public void letReadDataThrowException(boolean throwException) { mThrowExcpetion = throwException; } @Override public boolean addDelta(int uid, LongArrayMultiStateCounter counter, long timestampMs) { return addDeltaForTest(uid, counter, timestampMs, mCpuTimeInStatePerClusterNs); } } }