Merge "Store annotation info in LogEvent/FieldValue" into rvc-dev am: c0a892456b am: 741d10458f am: 7be4c20aea
Change-Id: Ia98deed8701f2c13a4bbf196fb76be6813decfe3
This commit is contained in:
@@ -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
cmds/statsd/src/annotations.h
Normal file
35
cmds/statsd/src/annotations.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user