Merge "Merge "Increase size limit for pulled StatsEvent." into rvc-dev am: 4c21a3b737" into rvc-dev-plus-aosp

This commit is contained in:
Automerger Merge Worker
2020-06-05 17:38:32 +00:00
committed by Android (Google) Code Review
2 changed files with 202 additions and 8 deletions

View File

@@ -26,6 +26,8 @@ import android.os.SystemClock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.Arrays;
/**
* StatsEvent builds and stores the buffer sent over the statsd socket.
* This class defines and encapsulates the socket protocol.
@@ -224,7 +226,9 @@ public final class StatsEvent {
// Max payload size is 4 bytes less as 4 bytes are reserved for statsEventTag.
// See android_util_StatsLog.cpp.
private static final int MAX_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;
private static final int MAX_PUSH_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;
private static final int MAX_PULL_PAYLOAD_SIZE = 50 * 1024; // 50 KB
private final int mAtomId;
private final byte[] mPayload;
@@ -619,6 +623,7 @@ public final class StatsEvent {
@NonNull
public Builder usePooledBuffer() {
mUsePooledBuffer = true;
mBuffer.setMaxSize(MAX_PUSH_PAYLOAD_SIZE, mPos);
return this;
}
@@ -694,8 +699,9 @@ public final class StatsEvent {
@GuardedBy("sLock")
private static Buffer sPool;
private final byte[] mBytes = new byte[MAX_PAYLOAD_SIZE];
private byte[] mBytes = new byte[MAX_PUSH_PAYLOAD_SIZE];
private boolean mOverflow = false;
private int mMaxSize = MAX_PULL_PAYLOAD_SIZE;
@NonNull
private static Buffer obtain() {
@@ -717,15 +723,26 @@ public final class StatsEvent {
}
private void release() {
synchronized (sLock) {
if (null == sPool) {
sPool = this;
// Recycle this Buffer if its size is MAX_PUSH_PAYLOAD_SIZE or under.
if (mBytes.length <= MAX_PUSH_PAYLOAD_SIZE) {
synchronized (sLock) {
if (null == sPool) {
sPool = this;
}
}
}
}
private void reset() {
mOverflow = false;
mMaxSize = MAX_PULL_PAYLOAD_SIZE;
}
private void setMaxSize(final int maxSize, final int numBytesWritten) {
mMaxSize = maxSize;
if (numBytesWritten > maxSize) {
mOverflow = true;
}
}
private boolean hasOverflowed() {
@@ -740,11 +757,28 @@ public final class StatsEvent {
* @return true if space is available, false otherwise.
**/
private boolean hasEnoughSpace(final int index, final int numBytes) {
final boolean result = index + numBytes < MAX_PAYLOAD_SIZE;
if (!result) {
final int totalBytesNeeded = index + numBytes;
if (totalBytesNeeded > mMaxSize) {
mOverflow = true;
return false;
}
return result;
// Expand buffer if needed.
if (mBytes.length < mMaxSize && totalBytesNeeded > mBytes.length) {
int newSize = mBytes.length;
do {
newSize *= 2;
} while (newSize <= totalBytesNeeded);
if (newSize > mMaxSize) {
newSize = mMaxSize;
}
mBytes = Arrays.copyOf(mBytes, newSize);
}
return true;
}
/**

View File

@@ -33,6 +33,7 @@ import org.junit.runner.RunWith;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Random;
/**
* Internal tests for {@link StatsEvent}.
@@ -644,6 +645,165 @@ public class StatsEventTest {
statsEvent.release();
}
@Test
public void testLargePulledEvent() {
final int expectedAtomId = 10_020;
byte[] field1 = new byte[10 * 1024];
new Random().nextBytes(field1);
final long minTimestamp = SystemClock.elapsedRealtimeNanos();
final StatsEvent statsEvent =
StatsEvent.newBuilder().setAtomId(expectedAtomId).writeByteArray(field1).build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
final ByteBuffer buffer =
ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
assertWithMessage("Root element in buffer is not TYPE_OBJECT")
.that(buffer.get())
.isEqualTo(StatsEvent.TYPE_OBJECT);
assertWithMessage("Incorrect number of elements in root object")
.that(buffer.get())
.isEqualTo(3);
assertWithMessage("First element is not timestamp")
.that(buffer.get())
.isEqualTo(StatsEvent.TYPE_LONG);
assertWithMessage("Incorrect timestamp")
.that(buffer.getLong())
.isIn(Range.closed(minTimestamp, maxTimestamp));
assertWithMessage("Second element is not atom id")
.that(buffer.get())
.isEqualTo(StatsEvent.TYPE_INT);
assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
assertWithMessage("Third element is not byte array")
.that(buffer.get())
.isEqualTo(StatsEvent.TYPE_BYTE_ARRAY);
final byte[] field1Actual = getByteArrayFromByteBuffer(buffer);
assertWithMessage("Incorrect field 1").that(field1Actual).isEqualTo(field1);
assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
statsEvent.release();
}
@Test
public void testPulledEventOverflow() {
final int expectedAtomId = 10_020;
byte[] field1 = new byte[50 * 1024];
new Random().nextBytes(field1);
final long minTimestamp = SystemClock.elapsedRealtimeNanos();
final StatsEvent statsEvent =
StatsEvent.newBuilder().setAtomId(expectedAtomId).writeByteArray(field1).build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
final ByteBuffer buffer =
ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
assertWithMessage("Root element in buffer is not TYPE_OBJECT")
.that(buffer.get())
.isEqualTo(StatsEvent.TYPE_OBJECT);
assertWithMessage("Incorrect number of elements in root object")
.that(buffer.get())
.isEqualTo(3);
assertWithMessage("First element is not timestamp")
.that(buffer.get())
.isEqualTo(StatsEvent.TYPE_LONG);
assertWithMessage("Incorrect timestamp")
.that(buffer.getLong())
.isIn(Range.closed(minTimestamp, maxTimestamp));
assertWithMessage("Second element is not atom id")
.that(buffer.get())
.isEqualTo(StatsEvent.TYPE_INT);
assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
assertWithMessage("Third element is not errors type")
.that(buffer.get())
.isEqualTo(StatsEvent.TYPE_ERRORS);
final int errorMask = buffer.getInt();
assertWithMessage("ERROR_OVERFLOW should be the only error in the error mask")
.that(errorMask)
.isEqualTo(StatsEvent.ERROR_OVERFLOW);
assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
statsEvent.release();
}
@Test
public void testPushedEventOverflow() {
final int expectedAtomId = 10_020;
byte[] field1 = new byte[10 * 1024];
new Random().nextBytes(field1);
final long minTimestamp = SystemClock.elapsedRealtimeNanos();
final StatsEvent statsEvent = StatsEvent.newBuilder()
.setAtomId(expectedAtomId)
.writeByteArray(field1)
.usePooledBuffer()
.build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
final ByteBuffer buffer =
ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
assertWithMessage("Root element in buffer is not TYPE_OBJECT")
.that(buffer.get())
.isEqualTo(StatsEvent.TYPE_OBJECT);
assertWithMessage("Incorrect number of elements in root object")
.that(buffer.get())
.isEqualTo(3);
assertWithMessage("First element is not timestamp")
.that(buffer.get())
.isEqualTo(StatsEvent.TYPE_LONG);
assertWithMessage("Incorrect timestamp")
.that(buffer.getLong())
.isIn(Range.closed(minTimestamp, maxTimestamp));
assertWithMessage("Second element is not atom id")
.that(buffer.get())
.isEqualTo(StatsEvent.TYPE_INT);
assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
assertWithMessage("Third element is not errors type")
.that(buffer.get())
.isEqualTo(StatsEvent.TYPE_ERRORS);
final int errorMask = buffer.getInt();
assertWithMessage("ERROR_OVERFLOW should be the only error in the error mask")
.that(errorMask)
.isEqualTo(StatsEvent.ERROR_OVERFLOW);
assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
statsEvent.release();
}
private static byte[] getByteArrayFromByteBuffer(final ByteBuffer buffer) {
final int numBytes = buffer.getInt();
byte[] bytes = new byte[numBytes];