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

Commit 741d1045 authored by Ruchir Rastogi's avatar Ruchir Rastogi Committed by Automerger Merge Worker
Browse files

Merge "Store annotation info in LogEvent/FieldValue" into rvc-dev am: c0a89245

Change-Id: I4187eb19728d29da7a3ee5ce50af1d3bb6881ce9
parents a2c34761 c0a89245
Loading
Loading
Loading
Loading
+52 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#pragma once

#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "annotations.h"

namespace android {
namespace os {
@@ -357,6 +358,56 @@ struct Value {
    Value& operator=(const Value& that);
};

class Annotations {
public:
    Annotations() {}

    // This enum stores where particular annotations can be found in the
    // bitmask. Note that these pos do not correspond to annotation ids.
    enum {
        NESTED_POS = 0x0,
        PRIMARY_POS = 0x1,
        EXCLUSIVE_POS = 0x2
    };

    inline void setNested(bool nested) { setBitmaskAtPos(NESTED_POS, nested); }

    inline void setPrimaryField(bool primary) { setBitmaskAtPos(PRIMARY_POS, primary); }

    inline void setExclusiveState(bool exclusive) { setBitmaskAtPos(EXCLUSIVE_POS, exclusive); }

    inline void setResetState(int resetState) { mResetState = resetState; }

    // Default value = false
    inline bool isNested() const { return getValueFromBitmask(NESTED_POS); }

    // Default value = false
    inline bool isPrimaryField() const { return getValueFromBitmask(PRIMARY_POS); }

    // Default value = false
    inline bool isExclusiveState() const { return getValueFromBitmask(EXCLUSIVE_POS); }

    // If a reset state is not sent in the StatsEvent, returns -1. Note that a
    // reset satate is only sent if and only if a reset should be triggered.
    inline int getResetState() const { return mResetState; }

private:
    inline void setBitmaskAtPos(int pos, bool value) {
        mBooleanBitmask &= ~(1 << pos); // clear
        mBooleanBitmask |= (value << pos); // set
    }

    inline bool getValueFromBitmask(int pos) const {
        return (mBooleanBitmask >> pos) & 0x1;
    }

    // This is a bitmask over all annotations stored in boolean form. Because
    // there are only 3 booleans, just one byte is required.
    uint8_t mBooleanBitmask = 0;

    int mResetState = -1;
};

/**
 * Represents a log item, or a dimension item (They are essentially the same).
 */
@@ -384,6 +435,7 @@ struct FieldValue {

    Field mField;
    Value mValue;
    Annotations mAnnotations;
};

bool HasPositionANY(const FieldMatcher& matcher);
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.
 */

#pragma once

namespace android {
namespace os {
namespace statsd {

const uint8_t ANNOTATION_ID_IS_UID = 1;
const uint8_t ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2;
const uint8_t ANNOTATION_ID_STATE_OPTION = 3;
const uint8_t ANNOTATION_ID_RESET_STATE = 5;
const uint8_t ANNOTATION_ID_STATE_NESTED = 6;

const int32_t STATE_OPTION_PRIMARY_FIELD = 1;
const int32_t STATE_OPTION_EXCLUSIVE_STATE = 2;
const int32_t STATE_OPTION_PRIMARY_FIELD_FIRST_UID = 3;

} // namespace statsd
} // namespace os
} // namespace android
+90 −10
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#define DEBUG false  // STOPSHIP if true
#include "logd/LogEvent.h"

#include "annotations.h"
#include "stats_log_util.h"
#include "statslog_statsd.h"

@@ -447,6 +448,7 @@ void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8

void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last,
                                     uint8_t numAnnotations) {
    int firstUidInChainIndex = mValues.size();
    int32_t numNodes = readNextValue<uint8_t>();
    for (pos[1] = 1; pos[1] <= numNodes; pos[1]++) {
        last[1] = (pos[1] == numNodes);
@@ -461,26 +463,103 @@ void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last,
        parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
    }

    parseAnnotations(numAnnotations);
    parseAnnotations(numAnnotations, firstUidInChainIndex);

    pos[1] = pos[2] = 1;
    last[1] = last[2] = false;
}

// TODO(b/151109630): store annotation information within LogEvent
void LogEvent::parseAnnotations(uint8_t numAnnotations) {
void LogEvent::parseIsUidAnnotation(uint8_t annotationType) {
    if (mValues.empty() || annotationType != BOOL_TYPE) {
        mValid = false;
        return;
    }

    bool isUid = readNextValue<uint8_t>();
    if (isUid) mUidFieldIndex = mValues.size() - 1;
}

void LogEvent::parseTruncateTimestampAnnotation(uint8_t annotationType) {
    if (!mValues.empty() || annotationType != BOOL_TYPE) {
        mValid = false;
        return;
    }

    mTruncateTimestamp = readNextValue<uint8_t>();
}

void LogEvent::parseStateOptionAnnotation(uint8_t annotationType, int firstUidInChainIndex) {
    if (mValues.empty() || annotationType != INT32_TYPE) {
        mValid = false;
        return;
    }

    int32_t stateOption = readNextValue<int32_t>();
    switch (stateOption) {
        case STATE_OPTION_EXCLUSIVE_STATE:
            mValues[mValues.size() - 1].mAnnotations.setExclusiveState(true);
            break;
        case STATE_OPTION_PRIMARY_FIELD:
            mValues[mValues.size() - 1].mAnnotations.setPrimaryField(true);
            break;
        case STATE_OPTION_PRIMARY_FIELD_FIRST_UID:
            if (firstUidInChainIndex == -1) {
                mValid = false;
            } else {
                mValues[firstUidInChainIndex].mAnnotations.setPrimaryField(true);
            }
            break;
        default:
            mValid = false;
    }
}

void LogEvent::parseResetStateAnnotation(uint8_t annotationType) {
    if (mValues.empty() || annotationType != INT32_TYPE) {
        mValid = false;
        return;
    }

    int32_t resetState = readNextValue<int32_t>();
    mValues[mValues.size() - 1].mAnnotations.setResetState(resetState);
}

void LogEvent::parseStateNestedAnnotation(uint8_t annotationType) {
    if (mValues.empty() || annotationType != BOOL_TYPE) {
        mValid = false;
        return;
    }

    bool nested = readNextValue<uint8_t>();
    mValues[mValues.size() - 1].mAnnotations.setNested(nested);
}

// firstUidInChainIndex is a default parameter that is only needed when parsing
// annotations for attribution chains.
void LogEvent::parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex) {
    for (uint8_t i = 0; i < numAnnotations; i++) {
        /*uint8_t annotationId = */ readNextValue<uint8_t>();
        uint8_t annotationId = readNextValue<uint8_t>();
        uint8_t annotationType = readNextValue<uint8_t>();
        switch (annotationType) {
            case BOOL_TYPE:
                /*bool annotationValue = */ readNextValue<uint8_t>();

        switch (annotationId) {
            case ANNOTATION_ID_IS_UID:
                parseIsUidAnnotation(annotationType);
                break;
            case INT32_TYPE:
                /*int32_t annotationValue =*/ readNextValue<int32_t>();
            case ANNOTATION_ID_TRUNCATE_TIMESTAMP:
                parseTruncateTimestampAnnotation(annotationType);
                break;
            case ANNOTATION_ID_STATE_OPTION:
                parseStateOptionAnnotation(annotationType, firstUidInChainIndex);
                break;
            case ANNOTATION_ID_RESET_STATE:
                parseResetStateAnnotation(annotationType);
                break;
            case ANNOTATION_ID_STATE_NESTED:
                parseStateNestedAnnotation(annotationType);
                break;
            default:
                mValid = false;
                return;
        }
    }
}
@@ -509,8 +588,8 @@ bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
    typeInfo = readNextValue<uint8_t>();
    if (getTypeId(typeInfo) != INT32_TYPE) mValid = false;
    mTagId = readNextValue<int32_t>();
    parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations
    numElements--;
    parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations


    for (pos[0] = 1; pos[0] <= numElements && mValid; pos[0]++) {
@@ -544,6 +623,7 @@ bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
                break;
            case ATTRIBUTION_CHAIN_TYPE:
                parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
                if (mAttributionChainIndex == -1) mAttributionChainIndex = pos[0];
                break;
            default:
                mValid = false;
+38 −1
Original line number Diff line number Diff line
@@ -206,6 +206,32 @@ public:
        return &mValues;
    }

    // Default value = false
    inline bool shouldTruncateTimestamp() {
        return mTruncateTimestamp;
    }

    // Returns the index of the uid field within the FieldValues vector if the
    // uid exists. If there is no uid field, returns -1.
    //
    // If the index within the atom definition is desired, do the following:
    //    int vectorIndex = LogEvent.getUidFieldIndex();
    //    if (vectorIndex != -1) {
    //        FieldValue& v = LogEvent.getValues()[vectorIndex];
    //        int atomIndex = v.mField.getPosAtDepth(0);
    //    }
    // Note that atomIndex is 1-indexed.
    inline int getUidFieldIndex() {
        return mUidFieldIndex;
    }

    // Returns the index of (the first) attribution chain within the atom
    // definition. Note that the value is 1-indexed. If there is no attribution
    // chain, returns -1.
    inline int getAttributionChainIndex() {
        return mAttributionChainIndex;
    }

    inline LogEvent makeCopy() {
        return LogEvent(*this);
    }
@@ -240,7 +266,13 @@ private:
    void parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
    void parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
    void parseAttributionChain(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
    void parseAnnotations(uint8_t numAnnotations);

    void parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex = -1);
    void parseIsUidAnnotation(uint8_t annotationType);
    void parseTruncateTimestampAnnotation(uint8_t annotationType);
    void parseStateOptionAnnotation(uint8_t annotationType, int firstUidInChainIndex);
    void parseResetStateAnnotation(uint8_t annotationType);
    void parseStateNestedAnnotation(uint8_t annotationType);

    /**
     * The below three variables are only valid during the execution of
@@ -322,6 +354,11 @@ private:

    // The pid of the logging client (defaults to -1).
    int32_t mLogPid = -1;

    // Annotations
    bool mTruncateTimestamp = false;
    int mUidFieldIndex = -1;
    int mAttributionChainIndex = -1;
};

void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);
+115 −3
Original line number Diff line number Diff line
@@ -12,12 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#include "src/logd/LogEvent.h"
#include <gtest/gtest.h>
#include <log/log_event_list.h>

#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
#include "frameworks/base/core/proto/android/stats/launcher/launcher.pb.h"
#include <stats_event.h>
#include "log/log_event_list.h"
#include "src/logd/LogEvent.h"
#include "stats_event.h"


#ifdef __ANDROID__
@@ -243,6 +244,117 @@ TEST(LogEventTest, TestAttributionChain) {
    AStatsEvent_release(event);
}

void createIntWithBoolAnnotationLogEvent(LogEvent* logEvent, uint8_t annotationId,
                                         bool annotationValue) {
    AStatsEvent* statsEvent = AStatsEvent_obtain();
    AStatsEvent_setAtomId(statsEvent, /*atomId=*/100);
    AStatsEvent_writeInt32(statsEvent, 10);
    AStatsEvent_addBoolAnnotation(statsEvent, annotationId, annotationValue);
    AStatsEvent_build(statsEvent);

    size_t size;
    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
    EXPECT_TRUE(logEvent->parseBuffer(buf, size));

    AStatsEvent_release(statsEvent);
}

TEST(LogEventTest, TestAnnotationIdIsUid) {
    LogEvent event(/*uid=*/0, /*pid=*/0);
    createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_IS_UID, true);

    const vector<FieldValue>& values = event.getValues();
    EXPECT_EQ(values.size(), 1);
    EXPECT_EQ(event.getUidFieldIndex(), 0);
}

TEST(LogEventTest, TestAnnotationIdStateNested) {
    LogEvent event(/*uid=*/0, /*pid=*/0);
    createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_STATE_NESTED, true);

    const vector<FieldValue>& values = event.getValues();
    EXPECT_EQ(values.size(), 1);
    EXPECT_TRUE(values[0].mAnnotations.isNested());
}

void createIntWithIntAnnotationLogEvent(LogEvent* logEvent, uint8_t annotationId,
                                        int annotationValue) {
    AStatsEvent* statsEvent = AStatsEvent_obtain();
    AStatsEvent_setAtomId(statsEvent, /*atomId=*/100);
    AStatsEvent_writeInt32(statsEvent, 10);
    AStatsEvent_addInt32Annotation(statsEvent, annotationId, annotationValue);
    AStatsEvent_build(statsEvent);

    size_t size;
    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
    EXPECT_TRUE(logEvent->parseBuffer(buf, size));

    AStatsEvent_release(statsEvent);
}

TEST(LogEventTest, TestPrimaryFieldAnnotation) {
    LogEvent event(/*uid=*/0, /*pid=*/0);
    createIntWithIntAnnotationLogEvent(&event, ANNOTATION_ID_STATE_OPTION,
                                       STATE_OPTION_PRIMARY_FIELD);

    const vector<FieldValue>& values = event.getValues();
    EXPECT_EQ(values.size(), 1);
    EXPECT_TRUE(values[0].mAnnotations.isPrimaryField());
}

TEST(LogEventTest, TestExclusiveStateAnnotation) {
    LogEvent event(/*uid=*/0, /*pid=*/0);
    createIntWithIntAnnotationLogEvent(&event, ANNOTATION_ID_STATE_OPTION,
                                       STATE_OPTION_EXCLUSIVE_STATE);

    const vector<FieldValue>& values = event.getValues();
    EXPECT_EQ(values.size(), 1);
    EXPECT_TRUE(values[0].mAnnotations.isExclusiveState());
}

TEST(LogEventTest, TestPrimaryFieldFirstUidAnnotation) {
    // Event has 10 ints and then an attribution chain
    int numInts = 10;
    int firstUidInChainIndex = numInts;
    string tag1 = "tag1";
    string tag2 = "tag2";
    uint32_t uids[] = {1001, 1002};
    const char* tags[] = {tag1.c_str(), tag2.c_str()};

    // Construct AStatsEvent
    AStatsEvent* statsEvent = AStatsEvent_obtain();
    AStatsEvent_setAtomId(statsEvent, 100);
    for (int i = 0; i < numInts; i++) {
        AStatsEvent_writeInt32(statsEvent, 10);
    }
    AStatsEvent_writeAttributionChain(statsEvent, uids, tags, 2);
    AStatsEvent_addInt32Annotation(statsEvent, ANNOTATION_ID_STATE_OPTION,
                                   STATE_OPTION_PRIMARY_FIELD_FIRST_UID);
    AStatsEvent_build(statsEvent);

    // Construct LogEvent
    size_t size;
    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
    LogEvent logEvent(/*uid=*/0, /*pid=*/0);
    EXPECT_TRUE(logEvent.parseBuffer(buf, size));
    AStatsEvent_release(statsEvent);

    // Check annotation
    const vector<FieldValue>& values = logEvent.getValues();
    EXPECT_EQ(values.size(), numInts + 4);
    EXPECT_TRUE(values[firstUidInChainIndex].mAnnotations.isPrimaryField());
}

TEST(LogEventTest, TestResetStateAnnotation) {
    int32_t resetState = 10;
    LogEvent event(/*uid=*/0, /*pid=*/0);
    createIntWithIntAnnotationLogEvent(&event, ANNOTATION_ID_RESET_STATE, resetState);

    const vector<FieldValue>& values = event.getValues();
    EXPECT_EQ(values.size(), 1);
    EXPECT_EQ(values[0].mAnnotations.getResetState(), resetState);
}

}  // namespace statsd
}  // namespace os
}  // namespace android