Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e4e0566e authored by Ruchir Rastogi's avatar Ruchir Rastogi Committed by Android (Google) Code Review
Browse files

Merge "Support atom-level annotations within AStatsEvent" into rvc-dev

parents fcdaee48 e712eed2
Loading
Loading
Loading
Loading
+7 −9
Original line number Diff line number Diff line
@@ -29,8 +29,9 @@
 *      AStatsEvent* event = AStatsEvent_obtain();
 *
 *      AStatsEvent_setAtomId(event, atomId);
 *      AStatsEvent_addBoolAnnotation(event, 5, false); // atom-level annotation
 *      AStatsEvent_writeInt32(event, 24);
 *      AStatsEvent_addBoolAnnotation(event, 1, true); // annotations apply to the previous field
 *      AStatsEvent_addBoolAnnotation(event, 1, true); // annotation for preceding atom field
 *      AStatsEvent_addInt32Annotation(event, 2, 128);
 *      AStatsEvent_writeFloat(event, 2.0);
 *
@@ -38,13 +39,8 @@
 *      AStatsEvent_write(event);
 *      AStatsEvent_release(event);
 *
 * Notes:
 *    (a) write_<type>() and add_<type>_annotation() should be called in the order that fields
 *        and annotations are defined in the atom.
 *    (b) set_atom_id() can be called anytime before stats_event_write().
 *    (c) add_<type>_annotation() calls apply to the previous field.
 *    (d) If errors occur, stats_event_write() will write a bitmask of the errors to the socket.
 *    (e) All strings should be encoded using UTF8.
 * Note that calls to add atom fields and annotations should be made in the
 * order that they are defined in the atom.
 */

#ifdef __cplusplus
@@ -84,7 +80,7 @@ void AStatsEvent_build(AStatsEvent* event);
int AStatsEvent_write(AStatsEvent* event);

/**
 * Frees the memory held by this StatsEvent
 * Frees the memory held by this StatsEvent.
 *
 * After calling this, the StatsEvent must not be used or modified in any way.
 */
@@ -92,6 +88,8 @@ void AStatsEvent_release(AStatsEvent* event);

/**
 * Sets the atom id for this StatsEvent.
 *
 * This function should be called immediately after AStatsEvent_obtain.
 **/
void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId);

+31 −26
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@
#define POS_NUM_ELEMENTS 1
#define POS_TIMESTAMP (POS_NUM_ELEMENTS + sizeof(uint8_t))
#define POS_ATOM_ID (POS_TIMESTAMP + sizeof(uint8_t) + sizeof(uint64_t))
#define POS_FIRST_FIELD (POS_ATOM_ID + sizeof(uint8_t) + sizeof(uint32_t))

/* LIMITS */
#define MAX_ANNOTATION_COUNT 15
@@ -66,8 +65,11 @@
// within a buf. Also includes other required fields.
struct AStatsEvent {
    uint8_t* buf;
    size_t lastFieldPos;  // location of last field within the buf
    size_t size;          // number of valid bytes within buffer
    // Location of last field within the buf. Here, field denotes either a
    // metadata field (e.g. timestamp) or an atom field.
    size_t lastFieldPos;
    // Number of valid bytes within the buffer.
    size_t size;
    uint32_t numElements;
    uint32_t atomId;
    uint32_t errors;
@@ -85,20 +87,21 @@ static int64_t get_elapsed_realtime_ns() {
AStatsEvent* AStatsEvent_obtain() {
    AStatsEvent* event = malloc(sizeof(AStatsEvent));
    event->buf = (uint8_t*)calloc(MAX_EVENT_PAYLOAD, 1);
    event->buf[0] = OBJECT_TYPE;
    event->lastFieldPos = 0;
    event->size = 2;  // reserve first two bytes for outer event type and number of elements
    event->numElements = 0;
    event->atomId = 0;
    event->errors = 0;
    event->truncate = true;  // truncate for both pulled and pushed atoms
    event->built = false;

    // place the timestamp
    uint64_t timestampNs = get_elapsed_realtime_ns();
    event->buf[POS_TIMESTAMP] = INT64_TYPE;
    memcpy(&event->buf[POS_TIMESTAMP + sizeof(uint8_t)], &timestampNs, sizeof(timestampNs));
    event->buf[0] = OBJECT_TYPE;
    AStatsEvent_writeInt64(event, get_elapsed_realtime_ns());  // write the timestamp

    event->numElements = 1;
    event->lastFieldPos = 0;  // 0 since we haven't written a field yet
    event->size = POS_FIRST_FIELD;
    // Force client to set atom id immediately (this is required for atom-level
    // annotations to be written correctly). All atom field and annotation
    // writes will fail until the atom id is set because event->errors != 0.
    event->errors |= ERROR_NO_ATOM_ID;

    return event;
}
@@ -109,10 +112,12 @@ void AStatsEvent_release(AStatsEvent* event) {
}

void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId) {
    if ((event->errors & ERROR_NO_ATOM_ID) == 0) return;

    // Clear the ERROR_NO_ATOM_ID bit.
    event->errors &= ~ERROR_NO_ATOM_ID;
    event->atomId = atomId;
    event->buf[POS_ATOM_ID] = INT32_TYPE;
    memcpy(&event->buf[POS_ATOM_ID + sizeof(uint8_t)], &atomId, sizeof(atomId));
    event->numElements++;
    AStatsEvent_writeInt32(event, atomId);
}

// Overwrites the timestamp populated in AStatsEvent_obtain with a custom
@@ -306,23 +311,23 @@ void AStatsEvent_truncateBuffer(AStatsEvent* event, bool truncate) {
void AStatsEvent_build(AStatsEvent* event) {
    if (event->built) return;

    if (event->atomId == 0) event->errors |= ERROR_NO_ATOM_ID;

    if (event->numElements > MAX_BYTE_VALUE) {
        event->errors |= ERROR_TOO_MANY_FIELDS;
    } else {
        event->buf[POS_NUM_ELEMENTS] = event->numElements;
    }
    if (event->numElements > MAX_BYTE_VALUE) event->errors |= ERROR_TOO_MANY_FIELDS;

    // If there are errors, rewrite buffer.
    if (event->errors) {
        event->buf[POS_NUM_ELEMENTS] = 3;
        event->buf[POS_FIRST_FIELD] = ERROR_TYPE;
        memcpy(&event->buf[POS_FIRST_FIELD + sizeof(uint8_t)], &event->errors,
               sizeof(event->errors));
        event->size = POS_FIRST_FIELD + sizeof(uint8_t) + sizeof(uint32_t);
        // Discard everything after the atom id (including atom-level
        // annotations). This leaves only two elements (timestamp and atom id).
        event->numElements = 2;
        // Reset number of atom-level annotations to 0.
        event->buf[POS_ATOM_ID] = INT32_TYPE;
        // Now, write errors to the buffer immediately after the atom id.
        event->size = POS_ATOM_ID + sizeof(uint8_t) + sizeof(uint32_t);
        start_field(event, ERROR_TYPE);
        append_int32(event, event->errors);
    }

    event->buf[POS_NUM_ELEMENTS] = event->numElements;

    // Truncate the buffer to the appropriate length in order to limit our
    // memory usage.
    if (event->truncate) event->buf = (uint8_t*)realloc(event->buf, event->size);
+43 −4
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T a
}

void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime,
                   uint32_t atomId) {
                   uint32_t atomId, uint8_t numAtomLevelAnnotations = 0) {
    // All events start with OBJECT_TYPE id.
    checkTypeHeader(buffer, OBJECT_TYPE);

@@ -104,7 +104,7 @@ void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int
    EXPECT_LE(timestamp, endTime);

    // Check atom id
    checkTypeHeader(buffer, INT32_TYPE);
    checkTypeHeader(buffer, INT32_TYPE, numAtomLevelAnnotations);
    checkScalar(buffer, atomId);
}

@@ -240,7 +240,7 @@ TEST(StatsEventTest, TestAttributionChains) {
    AStatsEvent_release(event);
}

TEST(StatsEventTest, TestAnnotations) {
TEST(StatsEventTest, TestFieldAnnotations) {
    uint32_t atomId = 100;

    // first element information
@@ -259,7 +259,7 @@ TEST(StatsEventTest, TestAnnotations) {

    int64_t startTime = android::elapsedRealtimeNano();
    AStatsEvent* event = AStatsEvent_obtain();
    AStatsEvent_setAtomId(event, 100);
    AStatsEvent_setAtomId(event, atomId);
    AStatsEvent_writeBool(event, boolValue);
    AStatsEvent_addBoolAnnotation(event, boolAnnotation1Id, boolAnnotation1Value);
    AStatsEvent_addInt32Annotation(event, boolAnnotation2Id, boolAnnotation2Value);
@@ -292,6 +292,45 @@ TEST(StatsEventTest, TestAnnotations) {
    AStatsEvent_release(event);
}

TEST(StatsEventTest, TestAtomLevelAnnotations) {
    uint32_t atomId = 100;
    // atom-level annotation information
    uint8_t boolAnnotationId = 1;
    uint8_t int32AnnotationId = 2;
    bool boolAnnotationValue = false;
    int32_t int32AnnotationValue = 5;

    float fieldValue = -3.5;

    int64_t startTime = android::elapsedRealtimeNano();
    AStatsEvent* event = AStatsEvent_obtain();
    AStatsEvent_setAtomId(event, atomId);
    AStatsEvent_addBoolAnnotation(event, boolAnnotationId, boolAnnotationValue);
    AStatsEvent_addInt32Annotation(event, int32AnnotationId, int32AnnotationValue);
    AStatsEvent_writeFloat(event, fieldValue);
    AStatsEvent_build(event);
    int64_t endTime = android::elapsedRealtimeNano();

    size_t bufferSize;
    uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
    uint8_t* bufferEnd = buffer + bufferSize;

    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId,
                  /*numAtomLevelAnnotations=*/2);

    // check atom-level annotations
    checkAnnotation(&buffer, boolAnnotationId, BOOL_TYPE, boolAnnotationValue);
    checkAnnotation(&buffer, int32AnnotationId, INT32_TYPE, int32AnnotationValue);

    // check first element
    checkTypeHeader(&buffer, FLOAT_TYPE);
    checkScalar(&buffer, fieldValue);

    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
    EXPECT_EQ(AStatsEvent_getErrors(event), 0);
    AStatsEvent_release(event);
}

TEST(StatsEventTest, TestNoAtomIdError) {
    AStatsEvent* event = AStatsEvent_obtain();
    // Don't set the atom id in order to trigger the error.