Make LongArrayMultiStateCounter parcelable

Bug: 197162116
Test: atest FrameworksCoreTests:LongArrayMultiStateCounterTest
      atest CorePerfTests:LongArrayMultiStateCounterPerfTest

Change-Id: Ibc94620f2f106695fa7f17fc0737496f0e32ba48
This commit is contained in:
Dmitri Plotnikov
2021-09-08 10:42:27 -07:00
parent 37b480c72a
commit e5292b4a24
6 changed files with 253 additions and 55 deletions

View File

@@ -37,15 +37,15 @@ public class LongArrayMultiStateCounterPerfTest {
/**
* A complete line-for-line reimplementation of
* {@link }com.android.internal.os.CpuTimeInFreqMultiStateCounter}, only in Java instead of
* {@link com.android.internal.os.LongArrayMultiStateCounter}, 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 long mLastStateChangeTimestampMs = -1;
private long mLastUpdateTimestampMs = -1;
private static class State {
private long mTimeInStateSinceUpdate;
@@ -56,13 +56,9 @@ public class LongArrayMultiStateCounterPerfTest {
private final long[] mLastTimeInFreq;
private final long[] mDelta;
TestLongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState,
long timestampMs) {
TestLongArrayMultiStateCounter(int stateCount, int arrayLength) {
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();
@@ -73,12 +69,14 @@ public class LongArrayMultiStateCounterPerfTest {
}
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;
if (mLastStateChangeTimestampMs > 0) {
if (timestampMs >= mLastStateChangeTimestampMs) {
mStates[mCurrentState].mTimeInStateSinceUpdate +=
timestampMs - mLastStateChangeTimestampMs;
} else {
for (int i = 0; i < mStateCount; i++) {
mStates[i].mTimeInStateSinceUpdate = 0;
}
}
}
mCurrentState = state;
@@ -88,21 +86,23 @@ public class LongArrayMultiStateCounterPerfTest {
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;
if (mLastUpdateTimestampMs >= 0) {
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 {
} else if (timestampMs < mLastUpdateTimestampMs) {
throw new RuntimeException();
}
} else if (timestampMs < mLastUpdateTimestampMs) {
throw new RuntimeException();
}
System.arraycopy(timeInFreq, 0, mLastTimeInFreq, 0, mArrayLength);
mLastUpdateTimestampMs = timestampMs;
@@ -142,7 +142,7 @@ public class LongArrayMultiStateCounterPerfTest {
@Test
public void javaImplementation() {
TestLongArrayMultiStateCounter counter =
new TestLongArrayMultiStateCounter(2, 4, 0, 1000);
new TestLongArrayMultiStateCounter(2, 4);
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
long time = 1000;
long[] timeInFreq = {100, 200, 300, 400};
@@ -156,7 +156,7 @@ public class LongArrayMultiStateCounterPerfTest {
@Test
public void nativeImplementation() {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4, 0, 1000);
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
long time = 1000;
LongArrayMultiStateCounter.LongArrayContainer timeInFreq =

View File

@@ -16,6 +16,11 @@
package com.android.internal.os;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -47,7 +52,7 @@ import libcore.util.NativeAllocationRegistry;
*
* @hide
*/
public class LongArrayMultiStateCounter {
public final class LongArrayMultiStateCounter implements Parcelable {
/**
* Container for a native equivalent of a long[].
@@ -112,14 +117,22 @@ public class LongArrayMultiStateCounter {
// methods.
final long mNativeObject;
public LongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState,
long timestampMs) {
public LongArrayMultiStateCounter(int stateCount, int arrayLength) {
Preconditions.checkArgumentPositive(stateCount, "stateCount must be greater than 0");
mStateCount = stateCount;
mLength = arrayLength;
mNativeObject = native_init(stateCount, arrayLength, initialState, timestampMs);
mNativeObject = native_init(stateCount, arrayLength);
sRegistry.registerNativeAllocation(this, mNativeObject);
}
private LongArrayMultiStateCounter(Parcel in) {
mNativeObject = native_initFromParcel(in);
sRegistry.registerNativeAllocation(this, mNativeObject);
mStateCount = native_getStateCount(mNativeObject);
mLength = native_getArrayLength(mNativeObject);
}
/**
* Sets the current state to the supplied value.
*/
@@ -161,9 +174,32 @@ public class LongArrayMultiStateCounter {
return native_toString(mNativeObject);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
native_writeToParcel(mNativeObject, dest, flags);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<LongArrayMultiStateCounter> CREATOR =
new Creator<LongArrayMultiStateCounter>() {
@Override
public LongArrayMultiStateCounter createFromParcel(Parcel in) {
return new LongArrayMultiStateCounter(in);
}
@Override
public LongArrayMultiStateCounter[] newArray(int size) {
return new LongArrayMultiStateCounter[size];
}
};
@CriticalNative
private static native long native_init(int stateCount, int arrayLength, int initialState,
long timestampMs);
private static native long native_init(int stateCount, int arrayLength);
@CriticalNative
private static native long native_getReleaseFunc();
@@ -181,4 +217,16 @@ public class LongArrayMultiStateCounter {
@FastNative
private native String native_toString(long nativeObject);
@FastNative
private native void native_writeToParcel(long nativeObject, Parcel dest, int flags);
@FastNative
private static native long native_initFromParcel(Parcel parcel);
@CriticalNative
private static native int native_getStateCount(long nativeObject);
@CriticalNative
private static native int native_getArrayLength(long nativeObject);
}

View File

@@ -263,6 +263,7 @@ cc_library_shared {
"libdebuggerd_client",
"libutils",
"libbinder",
"libbinder_ndk",
"libui",
"libgraphicsenv",
"libgui",

View File

@@ -14,17 +14,22 @@
* limitations under the License.
*/
#include <android/binder_parcel.h>
#include <android/binder_parcel_jni.h>
#include <android/binder_parcel_utils.h>
#include <android_runtime/Log.h>
#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) {
static jlong native_init(jint stateCount, jint arrayLength) {
battery::LongArrayMultiStateCounter *counter =
new battery::LongArrayMultiStateCounter(stateCount, initialState,
std::vector<uint64_t>(arrayLength), timestamp);
new battery::LongArrayMultiStateCounter(stateCount, std::vector<uint64_t>(arrayLength));
return reinterpret_cast<jlong>(counter);
}
@@ -69,13 +74,95 @@ static jobject native_toString(JNIEnv *env, jobject self, jlong nativePtr) {
return env->NewStringUTF(counter->toString().c_str());
}
static void throwWriteRE(JNIEnv *env, binder_status_t status) {
ALOGE("Could not write LongArrayMultiStateCounter to Parcel, status = %d", status);
jniThrowRuntimeException(env, "Could not write LongArrayMultiStateCounter to Parcel");
}
#define THROW_ON_WRITE_ERROR(expr) \
{ \
binder_status_t status = expr; \
if (status != STATUS_OK) { \
throwWriteRE(env, status); \
} \
}
static void native_writeToParcel(JNIEnv *env, jobject self, jlong nativePtr, jobject jParcel,
jint flags) {
battery::LongArrayMultiStateCounter *counter =
reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
AParcel *parcel = AParcel_fromJavaParcel(env, jParcel);
uint16_t stateCount = counter->getStateCount();
THROW_ON_WRITE_ERROR(AParcel_writeInt32(parcel, stateCount));
// LongArrayMultiStateCounter has at least state 0
const std::vector<uint64_t> &anyState = counter->getCount(0);
THROW_ON_WRITE_ERROR(AParcel_writeInt32(parcel, anyState.size()));
for (battery::state_t state = 0; state < stateCount; state++) {
THROW_ON_WRITE_ERROR(ndk::AParcel_writeVector(parcel, counter->getCount(state)));
}
}
static void throwReadRE(JNIEnv *env, binder_status_t status) {
ALOGE("Could not read LongArrayMultiStateCounter from Parcel, status = %d", status);
jniThrowRuntimeException(env, "Could not read LongArrayMultiStateCounter from Parcel");
}
#define THROW_ON_READ_ERROR(expr) \
{ \
binder_status_t status = expr; \
if (status != STATUS_OK) { \
throwReadRE(env, status); \
} \
}
static jlong native_initFromParcel(JNIEnv *env, jclass theClass, jobject jParcel) {
AParcel *parcel = AParcel_fromJavaParcel(env, jParcel);
int32_t stateCount;
THROW_ON_READ_ERROR(AParcel_readInt32(parcel, &stateCount));
int32_t arrayLength;
THROW_ON_READ_ERROR(AParcel_readInt32(parcel, &arrayLength));
battery::LongArrayMultiStateCounter *counter =
new battery::LongArrayMultiStateCounter(stateCount, std::vector<uint64_t>(arrayLength));
std::vector<uint64_t> value;
value.reserve(arrayLength);
for (battery::state_t state = 0; state < stateCount; state++) {
THROW_ON_READ_ERROR(ndk::AParcel_readVector(parcel, &value));
counter->setValue(state, value);
}
return reinterpret_cast<jlong>(counter);
}
static jint native_getStateCount(jlong nativePtr) {
battery::LongArrayMultiStateCounter *counter =
reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
return counter->getStateCount();
}
static jint native_getArrayLength(jlong nativePtr) {
battery::LongArrayMultiStateCounter *counter =
reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
// LongArrayMultiStateCounter has at least state 0
const std::vector<uint64_t> &anyState = counter->getCount(0);
return anyState.size();
}
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},
{"native_init", "(II)J", (void *)native_init},
// @CriticalNative
{"native_getReleaseFunc", "()J", (void *)native_getReleaseFunc},
// @CriticalNative
@@ -86,6 +173,14 @@ static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = {
{"native_getCounts", "(JJI)V", (void *)native_getCounts},
// @FastNative
{"native_toString", "(J)Ljava/lang/String;", (void *)native_toString},
// @FastNative
{"native_writeToParcel", "(JLandroid/os/Parcel;I)V", (void *)native_writeToParcel},
// @FastNative
{"native_initFromParcel", "(Landroid/os/Parcel;)J", (void *)native_initFromParcel},
// @CriticalNative
{"native_getStateCount", "(J)I", (void *)native_getStateCount},
// @CriticalNative
{"native_getArrayLength", "(J)I", (void *)native_getArrayLength},
};
/////////////////////// LongArrayMultiStateCounter.LongArrayContainer ////////////////////////

View File

@@ -282,7 +282,11 @@ public class KernelSingleUidTimeReaderTest {
@Test
public void testAddDeltaFromBpf() {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 5, 0, 0);
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 5);
counter.setState(0, 0);
mInjector.setCpuTimeInStatePerClusterNs(new long[][]{{0, 0, 0}, {0, 0}});
boolean success = mInjector.addDelta(TEST_UID, counter, 0);
assertThat(success).isTrue();
// Nanoseconds
mInjector.setCpuTimeInStatePerClusterNs(
@@ -290,7 +294,7 @@ public class KernelSingleUidTimeReaderTest {
{1_000_000, 2_000_000, 3_000_000},
{4_000_000, 5_000_000}});
boolean success = mInjector.addDelta(TEST_UID, counter, 2000);
success = mInjector.addDelta(TEST_UID, counter, 2000);
assertThat(success).isTrue();
LongArrayMultiStateCounter.LongArrayContainer array =

View File

@@ -18,6 +18,8 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -30,26 +32,74 @@ public class LongArrayMultiStateCounterTest {
@Test
public void setStateAndUpdateValue() {
LongArrayMultiStateCounter.LongArrayContainer longArrayContainer =
new LongArrayMultiStateCounter.LongArrayContainer(4);
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4, 0, 1000);
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
updateValue(counter, new long[]{0, 0, 0, 0}, 1000);
counter.setState(0, 1000);
counter.setState(1, 2000);
counter.setState(0, 4000);
longArrayContainer.setValues(new long[]{100, 200, 300, 400});
counter.updateValues(longArrayContainer, 9000);
counter.getCounts(longArrayContainer, 0);
updateValue(counter, new long[]{100, 200, 300, 400}, 9000);
long[] result = new long[4];
longArrayContainer.getValues(result);
assertThat(result).isEqualTo(new long[]{75, 150, 225, 300});
counter.getCounts(longArrayContainer, 1);
longArrayContainer.getValues(result);
assertThat(result).isEqualTo(new long[]{25, 50, 75, 100});
assertCounts(counter, 0, new long[]{75, 150, 225, 300});
assertCounts(counter, 1, new long[]{25, 50, 75, 100});
assertThat(counter.toString()).isEqualTo(
"currentState: 0 lastStateChangeTimestamp: 9000 lastUpdateTimestamp: 9000 states:"
+ " [0: time: 0 counter: { 75, 150, 225, 300}"
+ ", 1: time: 0 counter: { 25, 50, 75, 100}]");
"[0: {75, 150, 225, 300}, 1: {25, 50, 75, 100}] updated: 9000 currentState: 0");
}
@Test
public void parceling() {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
updateValue(counter, new long[]{0, 0, 0, 0}, 1000);
counter.setState(0, 1000);
updateValue(counter, new long[]{100, 200, 300, 400}, 2000);
counter.setState(1, 2000);
updateValue(counter, new long[]{101, 202, 304, 408}, 3000);
assertCounts(counter, 0, new long[]{100, 200, 300, 400});
assertCounts(counter, 1, new long[]{1, 2, 4, 8});
Parcel parcel = Parcel.obtain();
counter.writeToParcel(parcel, 0);
byte[] bytes = parcel.marshall();
parcel.recycle();
parcel = Parcel.obtain();
parcel.unmarshall(bytes, 0, bytes.length);
parcel.setDataPosition(0);
LongArrayMultiStateCounter newCounter =
LongArrayMultiStateCounter.CREATOR.createFromParcel(parcel);
parcel.recycle();
assertCounts(newCounter, 0, new long[]{100, 200, 300, 400});
assertCounts(newCounter, 1, new long[]{1, 2, 4, 8});
// ==== Verify that the counter keeps accumulating after unparceling.
// State, last update timestamp and current counts are undefined at this point.
newCounter.setState(0, 100);
updateValue(newCounter, new long[]{300, 400, 500, 600}, 100);
// A new base state and counters are established; we can continue accumulating deltas
updateValue(newCounter, new long[]{316, 432, 564, 728}, 200);
assertCounts(newCounter, 0, new long[]{116, 232, 364, 528});
}
private void updateValue(LongArrayMultiStateCounter counter, long[] values, int timestamp) {
LongArrayMultiStateCounter.LongArrayContainer container =
new LongArrayMultiStateCounter.LongArrayContainer(values.length);
container.setValues(values);
counter.updateValues(container, timestamp);
}
private void assertCounts(LongArrayMultiStateCounter counter, int state, long[] expected) {
LongArrayMultiStateCounter.LongArrayContainer container =
new LongArrayMultiStateCounter.LongArrayContainer(expected.length);
long[] counts = new long[expected.length];
counter.getCounts(container, state);
container.getValues(counts);
assertThat(counts).isEqualTo(expected);
}
}