Merge "Fix StatsEvent memory usage for pulled events"

This commit is contained in:
TreeHugger Robot
2019-12-10 23:18:07 +00:00
committed by Android (Google) Code Review
4 changed files with 69 additions and 10 deletions

View File

@@ -31,14 +31,23 @@ import com.android.internal.annotations.VisibleForTesting;
*
* <p>Usage:</p>
* <pre>
* // Pushed event
* StatsEvent statsEvent = StatsEvent.newBuilder()
* .setAtomId(atomId)
* .writeBoolean(false)
* .writeString("annotated String field")
* .addBooleanAnnotation(annotationId, true)
* .usePooledBuffer()
* .build();
* StatsLog.write(statsEvent);
*
* // Pulled event
* StatsEvent statsEvent = StatsEvent.newBuilder()
* .setAtomId(atomId)
* .writeBoolean(false)
* .writeString("annotated String field")
* .addBooleanAnnotation(annotationId, true)
* .build();
*
* StatsLog.write(statsEvent);
* </pre>
* @hide
**/
@@ -210,12 +219,15 @@ public final class StatsEvent {
private static final int MAX_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;
private final int mAtomId;
private final Buffer mBuffer;
private final byte[] mPayload;
private Buffer mBuffer;
private final int mNumBytes;
private StatsEvent(final int atomId, @NonNull final Buffer buffer, final int numBytes) {
private StatsEvent(final int atomId, @Nullable final Buffer buffer,
@NonNull final byte[] payload, final int numBytes) {
mAtomId = atomId;
mBuffer = buffer;
mPayload = payload;
mNumBytes = numBytes;
}
@@ -243,7 +255,7 @@ public final class StatsEvent {
**/
@NonNull
public byte[] getBytes() {
return mBuffer.getBytes();
return mPayload;
}
/**
@@ -256,10 +268,14 @@ public final class StatsEvent {
}
/**
* Recycle this StatsEvent object.
* Recycle resources used by this StatsEvent object.
* No actions should be taken on this StatsEvent after release() is called.
**/
public void release() {
mBuffer.release();
if (mBuffer != null) {
mBuffer.release();
mBuffer = null;
}
}
/**
@@ -280,7 +296,18 @@ public final class StatsEvent {
* optional string field3 = 3 [(annotation1) = true];
* }
*
* // StatsEvent construction.
* // StatsEvent construction for pushed event.
* StatsEvent.newBuilder()
* StatsEvent statsEvent = StatsEvent.newBuilder()
* .setAtomId(atomId)
* .writeInt(3) // field1
* .writeLong(8L) // field2
* .writeString("foo") // field 3
* .addBooleanAnnotation(annotation1Id, true)
* .usePooledBuffer()
* .build();
*
* // StatsEvent construction for pulled event.
* StatsEvent.newBuilder()
* StatsEvent statsEvent = StatsEvent.newBuilder()
* .setAtomId(atomId)
@@ -306,6 +333,7 @@ public final class StatsEvent {
private byte mLastType;
private int mNumElements;
private int mErrorMask;
private boolean mUsePooledBuffer = false;
private Builder(final Buffer buffer) {
mBuffer = buffer;
@@ -568,6 +596,17 @@ public final class StatsEvent {
return this;
}
/**
* Indicates to reuse Buffer's byte array as the underlying payload in StatsEvent.
* This should be called for pushed events to reduce memory allocations and garbage
* collections.
**/
@NonNull
public Builder usePooledBuffer() {
mUsePooledBuffer = true;
return this;
}
/**
* Builds a StatsEvent object with values entered in this Builder.
**/
@@ -599,7 +638,18 @@ public final class StatsEvent {
size = mPos;
}
return new StatsEvent(mAtomId, mBuffer, size);
if (mUsePooledBuffer) {
return new StatsEvent(mAtomId, mBuffer, mBuffer.getBytes(), size);
} else {
// Create a copy of the buffer with the required number of bytes.
final byte[] payload = new byte[size];
System.arraycopy(mBuffer.getBytes(), 0, payload, 0, size);
// Return Buffer instance to the pool.
mBuffer.release();
return new StatsEvent(mAtomId, null, payload, size);
}
}
private void writeTypeId(final byte typeId) {

View File

@@ -248,12 +248,15 @@ public final class StatsLog extends StatsLogInternal {
/**
* Write an event to stats log using the raw format encapsulated in StatsEvent.
* After writing to stats log, release() is called on the StatsEvent object.
* No further action should be taken on the StatsEvent object following this call.
*
* @param statsEvent The StatsEvent object containing the encoded buffer of data to write.
* @hide
*/
public static void write(@NonNull final StatsEvent statsEvent) {
writeImpl(statsEvent.getBytes(), statsEvent.getNumBytes(), statsEvent.getAtomId());
statsEvent.release();
}
private static void enforceDumpCallingPermission(Context context) {

View File

@@ -44,7 +44,7 @@ public class StatsEventTest {
@Test
public void testNoFields() {
final long minTimestamp = SystemClock.elapsedRealtimeNanos();
final StatsEvent statsEvent = StatsEvent.newBuilder().build();
final StatsEvent statsEvent = StatsEvent.newBuilder().usePooledBuffer().build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
final int expectedAtomId = 0;
@@ -99,6 +99,7 @@ public class StatsEventTest {
.writeBoolean(field2)
.writeInt(field3)
.writeInt(field4)
.usePooledBuffer()
.build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
@@ -167,6 +168,7 @@ public class StatsEventTest {
.writeString(field1)
.writeFloat(field2)
.writeByteArray(field3)
.usePooledBuffer()
.build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
@@ -230,6 +232,7 @@ public class StatsEventTest {
.setAtomId(expectedAtomId)
.writeAttributionChain(uids, tags)
.writeLong(field2)
.usePooledBuffer()
.build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
@@ -299,6 +302,7 @@ public class StatsEventTest {
final StatsEvent statsEvent = StatsEvent.newBuilder()
.setAtomId(expectedAtomId)
.writeKeyValuePairs(intMap, longMap, stringMap, floatMap)
.usePooledBuffer()
.build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
@@ -392,6 +396,7 @@ public class StatsEventTest {
.addBooleanAnnotation(field1AnnotationId, field1AnnotationValue)
.writeBoolean(field2)
.addIntAnnotation(field2AnnotationId, field2AnnotationValue)
.usePooledBuffer()
.build();
final long maxTimestamp = SystemClock.elapsedRealtimeNanos();

View File

@@ -189,6 +189,7 @@ static int write_java_methods(
}
fprintf(out, "\n");
fprintf(out, "%s builder.usePooledBuffer();\n", indent.c_str());
fprintf(out, "%s StatsLog.write(builder.build());\n", indent.c_str());
// Add support for writing using Q schema if this is not the default module.