Loading apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java 0 → 100644 +172 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 android.os; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.LongArrayMultiStateCounter; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @LargeTest public class LongArrayMultiStateCounterPerfTest { @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); /** * A complete line-for-line reimplementation of * {@link }com.android.internal.os.CpuTimeInFreqMultiStateCounter}, only in Java instead of * native. */ private static class TestLongArrayMultiStateCounter { private final int mStateCount; private final int mArrayLength; private int mCurrentState; private long mLastStateChangeTimestampMs; private long mLastUpdateTimestampMs; private static class State { private long mTimeInStateSinceUpdate; private long[] mCounter; } private final State[] mStates; private final long[] mLastTimeInFreq; private final long[] mDelta; TestLongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState, long timestampMs) { mStateCount = stateCount; mArrayLength = arrayLength; mCurrentState = initialState; mLastStateChangeTimestampMs = timestampMs; mLastUpdateTimestampMs = timestampMs; mStates = new State[stateCount]; for (int i = 0; i < mStateCount; i++) { mStates[i] = new State(); mStates[i].mCounter = new long[mArrayLength]; } mLastTimeInFreq = new long[mArrayLength]; mDelta = new long[mArrayLength]; } public void setState(int state, long timestampMs) { if (timestampMs >= mLastStateChangeTimestampMs) { mStates[mCurrentState].mTimeInStateSinceUpdate += timestampMs - mLastStateChangeTimestampMs; } else { for (int i = 0; i < mStateCount; i++) { mStates[i].mTimeInStateSinceUpdate = 0; } } mCurrentState = state; mLastStateChangeTimestampMs = timestampMs; } public void updateValue(long[] timeInFreq, long timestampMs) { setState(mCurrentState, timestampMs); if (timestampMs > mLastUpdateTimestampMs) { if (delta(mLastTimeInFreq, timeInFreq, mDelta)) { long timeSinceUpdate = timestampMs - mLastUpdateTimestampMs; for (int i = 0; i < mStateCount; i++) { long timeInState = mStates[i].mTimeInStateSinceUpdate; if (timeInState > 0) { add(mStates[i].mCounter, mDelta, timeInState, timeSinceUpdate); mStates[i].mTimeInStateSinceUpdate = 0; } } } else { throw new RuntimeException(); } } else if (timestampMs < mLastUpdateTimestampMs) { throw new RuntimeException(); } System.arraycopy(timeInFreq, 0, mLastTimeInFreq, 0, mArrayLength); mLastUpdateTimestampMs = timestampMs; } private boolean delta(long[] timeInFreq1, long[] timeInFreq2, long[] delta) { if (delta.length != mArrayLength) { throw new RuntimeException(); } boolean is_delta_valid = true; for (int i = 0; i < mStateCount; i++) { if (timeInFreq2[i] >= timeInFreq1[i]) { delta[i] = timeInFreq2[i] - timeInFreq1[i]; } else { delta[i] = 0; is_delta_valid = false; } } return is_delta_valid; } private void add(long[] counter, long[] delta, long numerator, long denominator) { if (numerator != denominator) { for (int i = 0; i < mArrayLength; i++) { counter[i] += delta[i] * numerator / denominator; } } else { for (int i = 0; i < mArrayLength; i++) { counter[i] += delta[i]; } } } } @Test public void javaImplementation() { TestLongArrayMultiStateCounter counter = new TestLongArrayMultiStateCounter(2, 4, 0, 1000); final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); long time = 1000; long[] timeInFreq = {100, 200, 300, 400}; while (state.keepRunning()) { counter.setState(1, time); counter.setState(0, time + 1000); counter.updateValue(timeInFreq, time + 2000); time += 10000; } } @Test public void nativeImplementation() { LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4, 0, 1000); final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); long time = 1000; LongArrayMultiStateCounter.LongArrayContainer timeInFreq = new LongArrayMultiStateCounter.LongArrayContainer(4); timeInFreq.setValues(new long[]{100, 200, 300, 400}); while (state.keepRunning()) { counter.setState(1, time); counter.setState(0, time + 1000); counter.updateValues(timeInFreq, time + 2000); time += 10000; } } } core/java/com/android/internal/os/LongArrayMultiStateCounter.java 0 → 100644 +181 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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; import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; import libcore.util.NativeAllocationRegistry; /** * Performs per-state counting of multi-element values over time. The class' behavior is illustrated * by this example: * <pre> * // At 0 ms, the state of the tracked object is 0 * counter.setState(0, 0); * * // At 1000 ms, the state changes to 1 * counter.setState(1, 1000); * * // At 3000 ms, the tracked values are updated to {100, 200} * arrayContainer.setValues(new long[]{{30, 300}}; * counter.updateValues(arrayContainer, 3000); * * // The values are distributed between states 0 and 1 according to the time * // spent in those respective states. In this specific case, 1000 and 2000 ms. * counter.getValues(arrayContainer, 0); * // arrayContainer now has values {10, 100} * counter.getValues(arrayContainer, 1); * // arrayContainer now has values {20, 200} * </pre> * * The tracked values are expected to increase monotonically. * * @hide */ public class LongArrayMultiStateCounter { /** * Container for a native equivalent of a long[]. */ public static class LongArrayContainer { private static final NativeAllocationRegistry sRegistry = NativeAllocationRegistry.createMalloced( LongArrayContainer.class.getClassLoader(), native_getReleaseFunc()); private final long mNativeObject; private final int mLength; public LongArrayContainer(int length) { mLength = length; mNativeObject = native_init(length); sRegistry.registerNativeAllocation(this, mNativeObject); } /** * Copies the supplied values into the underlying native array. */ public void setValues(long[] array) { if (array.length != mLength) { throw new IllegalArgumentException( "Invalid array length: " + mLength + ", expected: " + mLength); } native_setValues(mNativeObject, array); } /** * Copies the underlying native array values to the supplied array. */ public void getValues(long[] array) { if (array.length != mLength) { throw new IllegalArgumentException( "Invalid array length: " + mLength + ", expected: " + mLength); } native_getValues(mNativeObject, array); } @CriticalNative private static native long native_init(int length); @CriticalNative private static native long native_getReleaseFunc(); @FastNative private native void native_setValues(long nativeObject, long[] array); @FastNative private native void native_getValues(long nativeObject, long[] array); } private static final NativeAllocationRegistry sRegistry = NativeAllocationRegistry.createMalloced( LongArrayMultiStateCounter.class.getClassLoader(), native_getReleaseFunc()); private final int mStateCount; private final int mLength; private final long mNativeObject; public LongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState, long timestampMs) { mStateCount = stateCount; mLength = arrayLength; mNativeObject = native_init(stateCount, arrayLength, initialState, timestampMs); sRegistry.registerNativeAllocation(this, mNativeObject); } /** * Sets the current state to the supplied value. */ public void setState(int state, long timestampMs) { if (state < 0 || state >= mStateCount) { throw new IllegalArgumentException( "State: " + state + ", outside the range: [0-" + mStateCount + "]"); } native_setState(mNativeObject, state, timestampMs); } /** * Sets the new values. The delta between the previously set values and these values * is distributed among the state according to the time the object spent in those states * since the previous call to updateValues. */ public void updateValues(LongArrayContainer longArrayContainer, long timestampMs) { if (longArrayContainer.mLength != mLength) { throw new IllegalArgumentException( "Invalid array length: " + longArrayContainer.mLength + ", expected: " + mLength); } native_updateValues(mNativeObject, longArrayContainer.mNativeObject, timestampMs); } /** * Populates longArrayContainer with the accumulated counts for the specified state. */ public void getCounts(LongArrayContainer longArrayContainer, int state) { if (state < 0 || state >= mStateCount) { throw new IllegalArgumentException( "State: " + state + ", outside the range: [0-" + mStateCount + "]"); } native_getCounts(mNativeObject, longArrayContainer.mNativeObject, state); } @Override public String toString() { return native_toString(mNativeObject); } @CriticalNative private static native long native_init(int stateCount, int arrayLength, int initialState, long timestampMs); @CriticalNative private static native long native_getReleaseFunc(); @CriticalNative private static native void native_setState(long nativeObject, int state, long timestampMs); @CriticalNative private static native void native_updateValues(long nativeObject, long longArrayContainerNativeObject, long timestampMs); @CriticalNative private static native void native_getCounts(long nativeObject, long longArrayContainerNativeObject, int state); @FastNative private native String native_toString(long nativeObject); } core/jni/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -216,6 +216,7 @@ cc_library_shared { "com_android_internal_os_KernelCpuUidBpfMapReader.cpp", "com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp", "com_android_internal_os_KernelSingleUidTimeReader.cpp", "com_android_internal_os_LongArrayMultiStateCounter.cpp", "com_android_internal_os_Zygote.cpp", "com_android_internal_os_ZygoteCommandBuffer.cpp", "com_android_internal_os_ZygoteInit.cpp", Loading Loading @@ -246,6 +247,7 @@ cc_library_shared { "av-types-aidl-cpp", "android.hardware.camera.device@3.2", "libandroidicu", "libbattery", "libbpf_android", "libnetdbpf", "libnetdutils", Loading core/jni/AndroidRuntime.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -203,6 +203,7 @@ extern int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv* e 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); extern int register_com_android_internal_os_LongArrayMultiStateCounter(JNIEnv* env); extern int register_com_android_internal_os_Zygote(JNIEnv *env); extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env); extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env); Loading Loading @@ -1585,6 +1586,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_com_android_internal_content_om_OverlayConfig), REG_JNI(register_com_android_internal_net_NetworkUtilsInternal), REG_JNI(register_com_android_internal_os_ClassLoaderFactory), REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter), REG_JNI(register_com_android_internal_os_Zygote), REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer), REG_JNI(register_com_android_internal_os_ZygoteInit), Loading core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp 0 → 100644 +144 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 <nativehelper/ScopedPrimitiveArray.h> #include <cstring> #include "LongArrayMultiStateCounter.h" #include "core_jni_helpers.h" namespace android { static jlong native_init(jint stateCount, jint arrayLength, jint initialState, jlong timestamp) { battery::LongArrayMultiStateCounter *counter = new battery::LongArrayMultiStateCounter(stateCount, initialState, std::vector<uint64_t>(arrayLength), timestamp); return reinterpret_cast<jlong>(counter); } static void native_dispose(void *nativePtr) { battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr); delete counter; } static jlong native_getReleaseFunc() { return reinterpret_cast<jlong>(native_dispose); } static void native_setState(jlong nativePtr, jint state, jlong timestamp) { battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr); counter->setState(state, timestamp); } static void native_updateValues(jlong nativePtr, jlong longArrayContainerNativePtr, jlong timestamp) { battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr); std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr); counter->updateValue(*vector, timestamp); } static void native_getCounts(jlong nativePtr, jlong longArrayContainerNativePtr, jint state) { battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr); std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr); *vector = counter->getCount(state); } static jobject native_toString(JNIEnv *env, jlong nativePtr, jobject self) { battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr); return env->NewStringUTF(counter->toString().c_str()); } static jlong native_init_LongArrayContainer(jint length) { return reinterpret_cast<jlong>(new std::vector<uint64_t>(length)); } static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = { // @CriticalNative {"native_init", "(IIIJ)J", (void *)native_init}, // @CriticalNative {"native_getReleaseFunc", "()J", (void *)native_getReleaseFunc}, // @CriticalNative {"native_setState", "(JIJ)V", (void *)native_setState}, // @CriticalNative {"native_updateValues", "(JJJ)V", (void *)native_updateValues}, // @CriticalNative {"native_getCounts", "(JJI)V", (void *)native_getCounts}, // @FastNative {"native_toString", "(J)Ljava/lang/String;", (void *)native_toString}, }; /////////////////////// LongArrayMultiStateCounter.LongArrayContainer //////////////////////// static void native_dispose_LongArrayContainer(jlong nativePtr) { std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr); delete vector; } static jlong native_getReleaseFunc_LongArrayContainer() { return reinterpret_cast<jlong>(native_dispose_LongArrayContainer); } static void native_setValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr, jlongArray jarray) { std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr); ScopedLongArrayRO scopedArray(env, jarray); const uint64_t *array = reinterpret_cast<const uint64_t *>(scopedArray.get()); uint8_t size = scopedArray.size(); // Boundary checks are performed in the Java layer std::copy(array, array + size, vector->data()); } static void native_getValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr, jlongArray jarray) { std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr); ScopedLongArrayRW scopedArray(env, jarray); // Boundary checks are performed in the Java layer std::copy(vector->data(), vector->data() + vector->size(), scopedArray.get()); } static const JNINativeMethod g_LongArrayContainer_methods[] = { // @CriticalNative {"native_init", "(I)J", (void *)native_init_LongArrayContainer}, // @CriticalNative {"native_getReleaseFunc", "()J", (void *)native_getReleaseFunc_LongArrayContainer}, // @FastNative {"native_setValues", "(J[J)V", (void *)native_setValues_LongArrayContainer}, // @FastNative {"native_getValues", "(J[J)V", (void *)native_getValues_LongArrayContainer}, }; int register_com_android_internal_os_LongArrayMultiStateCounter(JNIEnv *env) { // 0 represents success, thus "|" and not "&" return RegisterMethodsOrDie(env, "com/android/internal/os/LongArrayMultiStateCounter", g_LongArrayMultiStateCounter_methods, NELEM(g_LongArrayMultiStateCounter_methods)) | RegisterMethodsOrDie(env, "com/android/internal/os/LongArrayMultiStateCounter" "$LongArrayContainer", g_LongArrayContainer_methods, NELEM(g_LongArrayContainer_methods)); } } // namespace android Loading
apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java 0 → 100644 +172 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 android.os; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.LongArrayMultiStateCounter; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @LargeTest public class LongArrayMultiStateCounterPerfTest { @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); /** * A complete line-for-line reimplementation of * {@link }com.android.internal.os.CpuTimeInFreqMultiStateCounter}, only in Java instead of * native. */ private static class TestLongArrayMultiStateCounter { private final int mStateCount; private final int mArrayLength; private int mCurrentState; private long mLastStateChangeTimestampMs; private long mLastUpdateTimestampMs; private static class State { private long mTimeInStateSinceUpdate; private long[] mCounter; } private final State[] mStates; private final long[] mLastTimeInFreq; private final long[] mDelta; TestLongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState, long timestampMs) { mStateCount = stateCount; mArrayLength = arrayLength; mCurrentState = initialState; mLastStateChangeTimestampMs = timestampMs; mLastUpdateTimestampMs = timestampMs; mStates = new State[stateCount]; for (int i = 0; i < mStateCount; i++) { mStates[i] = new State(); mStates[i].mCounter = new long[mArrayLength]; } mLastTimeInFreq = new long[mArrayLength]; mDelta = new long[mArrayLength]; } public void setState(int state, long timestampMs) { if (timestampMs >= mLastStateChangeTimestampMs) { mStates[mCurrentState].mTimeInStateSinceUpdate += timestampMs - mLastStateChangeTimestampMs; } else { for (int i = 0; i < mStateCount; i++) { mStates[i].mTimeInStateSinceUpdate = 0; } } mCurrentState = state; mLastStateChangeTimestampMs = timestampMs; } public void updateValue(long[] timeInFreq, long timestampMs) { setState(mCurrentState, timestampMs); if (timestampMs > mLastUpdateTimestampMs) { if (delta(mLastTimeInFreq, timeInFreq, mDelta)) { long timeSinceUpdate = timestampMs - mLastUpdateTimestampMs; for (int i = 0; i < mStateCount; i++) { long timeInState = mStates[i].mTimeInStateSinceUpdate; if (timeInState > 0) { add(mStates[i].mCounter, mDelta, timeInState, timeSinceUpdate); mStates[i].mTimeInStateSinceUpdate = 0; } } } else { throw new RuntimeException(); } } else if (timestampMs < mLastUpdateTimestampMs) { throw new RuntimeException(); } System.arraycopy(timeInFreq, 0, mLastTimeInFreq, 0, mArrayLength); mLastUpdateTimestampMs = timestampMs; } private boolean delta(long[] timeInFreq1, long[] timeInFreq2, long[] delta) { if (delta.length != mArrayLength) { throw new RuntimeException(); } boolean is_delta_valid = true; for (int i = 0; i < mStateCount; i++) { if (timeInFreq2[i] >= timeInFreq1[i]) { delta[i] = timeInFreq2[i] - timeInFreq1[i]; } else { delta[i] = 0; is_delta_valid = false; } } return is_delta_valid; } private void add(long[] counter, long[] delta, long numerator, long denominator) { if (numerator != denominator) { for (int i = 0; i < mArrayLength; i++) { counter[i] += delta[i] * numerator / denominator; } } else { for (int i = 0; i < mArrayLength; i++) { counter[i] += delta[i]; } } } } @Test public void javaImplementation() { TestLongArrayMultiStateCounter counter = new TestLongArrayMultiStateCounter(2, 4, 0, 1000); final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); long time = 1000; long[] timeInFreq = {100, 200, 300, 400}; while (state.keepRunning()) { counter.setState(1, time); counter.setState(0, time + 1000); counter.updateValue(timeInFreq, time + 2000); time += 10000; } } @Test public void nativeImplementation() { LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4, 0, 1000); final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); long time = 1000; LongArrayMultiStateCounter.LongArrayContainer timeInFreq = new LongArrayMultiStateCounter.LongArrayContainer(4); timeInFreq.setValues(new long[]{100, 200, 300, 400}); while (state.keepRunning()) { counter.setState(1, time); counter.setState(0, time + 1000); counter.updateValues(timeInFreq, time + 2000); time += 10000; } } }
core/java/com/android/internal/os/LongArrayMultiStateCounter.java 0 → 100644 +181 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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; import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; import libcore.util.NativeAllocationRegistry; /** * Performs per-state counting of multi-element values over time. The class' behavior is illustrated * by this example: * <pre> * // At 0 ms, the state of the tracked object is 0 * counter.setState(0, 0); * * // At 1000 ms, the state changes to 1 * counter.setState(1, 1000); * * // At 3000 ms, the tracked values are updated to {100, 200} * arrayContainer.setValues(new long[]{{30, 300}}; * counter.updateValues(arrayContainer, 3000); * * // The values are distributed between states 0 and 1 according to the time * // spent in those respective states. In this specific case, 1000 and 2000 ms. * counter.getValues(arrayContainer, 0); * // arrayContainer now has values {10, 100} * counter.getValues(arrayContainer, 1); * // arrayContainer now has values {20, 200} * </pre> * * The tracked values are expected to increase monotonically. * * @hide */ public class LongArrayMultiStateCounter { /** * Container for a native equivalent of a long[]. */ public static class LongArrayContainer { private static final NativeAllocationRegistry sRegistry = NativeAllocationRegistry.createMalloced( LongArrayContainer.class.getClassLoader(), native_getReleaseFunc()); private final long mNativeObject; private final int mLength; public LongArrayContainer(int length) { mLength = length; mNativeObject = native_init(length); sRegistry.registerNativeAllocation(this, mNativeObject); } /** * Copies the supplied values into the underlying native array. */ public void setValues(long[] array) { if (array.length != mLength) { throw new IllegalArgumentException( "Invalid array length: " + mLength + ", expected: " + mLength); } native_setValues(mNativeObject, array); } /** * Copies the underlying native array values to the supplied array. */ public void getValues(long[] array) { if (array.length != mLength) { throw new IllegalArgumentException( "Invalid array length: " + mLength + ", expected: " + mLength); } native_getValues(mNativeObject, array); } @CriticalNative private static native long native_init(int length); @CriticalNative private static native long native_getReleaseFunc(); @FastNative private native void native_setValues(long nativeObject, long[] array); @FastNative private native void native_getValues(long nativeObject, long[] array); } private static final NativeAllocationRegistry sRegistry = NativeAllocationRegistry.createMalloced( LongArrayMultiStateCounter.class.getClassLoader(), native_getReleaseFunc()); private final int mStateCount; private final int mLength; private final long mNativeObject; public LongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState, long timestampMs) { mStateCount = stateCount; mLength = arrayLength; mNativeObject = native_init(stateCount, arrayLength, initialState, timestampMs); sRegistry.registerNativeAllocation(this, mNativeObject); } /** * Sets the current state to the supplied value. */ public void setState(int state, long timestampMs) { if (state < 0 || state >= mStateCount) { throw new IllegalArgumentException( "State: " + state + ", outside the range: [0-" + mStateCount + "]"); } native_setState(mNativeObject, state, timestampMs); } /** * Sets the new values. The delta between the previously set values and these values * is distributed among the state according to the time the object spent in those states * since the previous call to updateValues. */ public void updateValues(LongArrayContainer longArrayContainer, long timestampMs) { if (longArrayContainer.mLength != mLength) { throw new IllegalArgumentException( "Invalid array length: " + longArrayContainer.mLength + ", expected: " + mLength); } native_updateValues(mNativeObject, longArrayContainer.mNativeObject, timestampMs); } /** * Populates longArrayContainer with the accumulated counts for the specified state. */ public void getCounts(LongArrayContainer longArrayContainer, int state) { if (state < 0 || state >= mStateCount) { throw new IllegalArgumentException( "State: " + state + ", outside the range: [0-" + mStateCount + "]"); } native_getCounts(mNativeObject, longArrayContainer.mNativeObject, state); } @Override public String toString() { return native_toString(mNativeObject); } @CriticalNative private static native long native_init(int stateCount, int arrayLength, int initialState, long timestampMs); @CriticalNative private static native long native_getReleaseFunc(); @CriticalNative private static native void native_setState(long nativeObject, int state, long timestampMs); @CriticalNative private static native void native_updateValues(long nativeObject, long longArrayContainerNativeObject, long timestampMs); @CriticalNative private static native void native_getCounts(long nativeObject, long longArrayContainerNativeObject, int state); @FastNative private native String native_toString(long nativeObject); }
core/jni/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -216,6 +216,7 @@ cc_library_shared { "com_android_internal_os_KernelCpuUidBpfMapReader.cpp", "com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp", "com_android_internal_os_KernelSingleUidTimeReader.cpp", "com_android_internal_os_LongArrayMultiStateCounter.cpp", "com_android_internal_os_Zygote.cpp", "com_android_internal_os_ZygoteCommandBuffer.cpp", "com_android_internal_os_ZygoteInit.cpp", Loading Loading @@ -246,6 +247,7 @@ cc_library_shared { "av-types-aidl-cpp", "android.hardware.camera.device@3.2", "libandroidicu", "libbattery", "libbpf_android", "libnetdbpf", "libnetdutils", Loading
core/jni/AndroidRuntime.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -203,6 +203,7 @@ extern int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv* e 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); extern int register_com_android_internal_os_LongArrayMultiStateCounter(JNIEnv* env); extern int register_com_android_internal_os_Zygote(JNIEnv *env); extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env); extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env); Loading Loading @@ -1585,6 +1586,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_com_android_internal_content_om_OverlayConfig), REG_JNI(register_com_android_internal_net_NetworkUtilsInternal), REG_JNI(register_com_android_internal_os_ClassLoaderFactory), REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter), REG_JNI(register_com_android_internal_os_Zygote), REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer), REG_JNI(register_com_android_internal_os_ZygoteInit), Loading
core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp 0 → 100644 +144 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 <nativehelper/ScopedPrimitiveArray.h> #include <cstring> #include "LongArrayMultiStateCounter.h" #include "core_jni_helpers.h" namespace android { static jlong native_init(jint stateCount, jint arrayLength, jint initialState, jlong timestamp) { battery::LongArrayMultiStateCounter *counter = new battery::LongArrayMultiStateCounter(stateCount, initialState, std::vector<uint64_t>(arrayLength), timestamp); return reinterpret_cast<jlong>(counter); } static void native_dispose(void *nativePtr) { battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr); delete counter; } static jlong native_getReleaseFunc() { return reinterpret_cast<jlong>(native_dispose); } static void native_setState(jlong nativePtr, jint state, jlong timestamp) { battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr); counter->setState(state, timestamp); } static void native_updateValues(jlong nativePtr, jlong longArrayContainerNativePtr, jlong timestamp) { battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr); std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr); counter->updateValue(*vector, timestamp); } static void native_getCounts(jlong nativePtr, jlong longArrayContainerNativePtr, jint state) { battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr); std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr); *vector = counter->getCount(state); } static jobject native_toString(JNIEnv *env, jlong nativePtr, jobject self) { battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr); return env->NewStringUTF(counter->toString().c_str()); } static jlong native_init_LongArrayContainer(jint length) { return reinterpret_cast<jlong>(new std::vector<uint64_t>(length)); } static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = { // @CriticalNative {"native_init", "(IIIJ)J", (void *)native_init}, // @CriticalNative {"native_getReleaseFunc", "()J", (void *)native_getReleaseFunc}, // @CriticalNative {"native_setState", "(JIJ)V", (void *)native_setState}, // @CriticalNative {"native_updateValues", "(JJJ)V", (void *)native_updateValues}, // @CriticalNative {"native_getCounts", "(JJI)V", (void *)native_getCounts}, // @FastNative {"native_toString", "(J)Ljava/lang/String;", (void *)native_toString}, }; /////////////////////// LongArrayMultiStateCounter.LongArrayContainer //////////////////////// static void native_dispose_LongArrayContainer(jlong nativePtr) { std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr); delete vector; } static jlong native_getReleaseFunc_LongArrayContainer() { return reinterpret_cast<jlong>(native_dispose_LongArrayContainer); } static void native_setValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr, jlongArray jarray) { std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr); ScopedLongArrayRO scopedArray(env, jarray); const uint64_t *array = reinterpret_cast<const uint64_t *>(scopedArray.get()); uint8_t size = scopedArray.size(); // Boundary checks are performed in the Java layer std::copy(array, array + size, vector->data()); } static void native_getValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr, jlongArray jarray) { std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr); ScopedLongArrayRW scopedArray(env, jarray); // Boundary checks are performed in the Java layer std::copy(vector->data(), vector->data() + vector->size(), scopedArray.get()); } static const JNINativeMethod g_LongArrayContainer_methods[] = { // @CriticalNative {"native_init", "(I)J", (void *)native_init_LongArrayContainer}, // @CriticalNative {"native_getReleaseFunc", "()J", (void *)native_getReleaseFunc_LongArrayContainer}, // @FastNative {"native_setValues", "(J[J)V", (void *)native_setValues_LongArrayContainer}, // @FastNative {"native_getValues", "(J[J)V", (void *)native_getValues_LongArrayContainer}, }; int register_com_android_internal_os_LongArrayMultiStateCounter(JNIEnv *env) { // 0 represents success, thus "|" and not "&" return RegisterMethodsOrDie(env, "com/android/internal/os/LongArrayMultiStateCounter", g_LongArrayMultiStateCounter_methods, NELEM(g_LongArrayMultiStateCounter_methods)) | RegisterMethodsOrDie(env, "com/android/internal/os/LongArrayMultiStateCounter" "$LongArrayContainer", g_LongArrayContainer_methods, NELEM(g_LongArrayContainer_methods)); } } // namespace android