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

Change-Id: I4187eb19728d29da7a3ee5ce50af1d3bb6881ce9
This commit is contained in:
Ruchir Rastogi
2020-04-02 16:05:47 +00:00
committed by Automerger Merge Worker
5 changed files with 330 additions and 14 deletions

View File

@@ -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);

View File

@@ -0,0 +1,35 @@
/*
* 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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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