Loading cmds/statsd/src/FieldValue.h +52 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #pragma once #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "annotations.h" namespace android { namespace os { Loading Loading @@ -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). */ Loading Loading @@ -384,6 +435,7 @@ struct FieldValue { Field mField; Value mValue; Annotations mAnnotations; }; bool HasPositionANY(const FieldMatcher& matcher); Loading cmds/statsd/src/annotations.h 0 → 100644 +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 cmds/statsd/src/logd/LogEvent.cpp +90 −10 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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); Loading @@ -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; } } } Loading Loading @@ -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]++) { Loading Loading @@ -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; Loading cmds/statsd/src/logd/LogEvent.h +38 −1 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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 Loading Loading @@ -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); Loading cmds/statsd/tests/LogEvent_test.cpp +115 −3 Original line number Diff line number Diff line Loading @@ -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__ Loading Loading @@ -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 Loading Loading
cmds/statsd/src/FieldValue.h +52 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #pragma once #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "annotations.h" namespace android { namespace os { Loading Loading @@ -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). */ Loading Loading @@ -384,6 +435,7 @@ struct FieldValue { Field mField; Value mValue; Annotations mAnnotations; }; bool HasPositionANY(const FieldMatcher& matcher); Loading
cmds/statsd/src/annotations.h 0 → 100644 +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
cmds/statsd/src/logd/LogEvent.cpp +90 −10 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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); Loading @@ -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; } } } Loading Loading @@ -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]++) { Loading Loading @@ -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; Loading
cmds/statsd/src/logd/LogEvent.h +38 −1 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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 Loading Loading @@ -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); Loading
cmds/statsd/tests/LogEvent_test.cpp +115 −3 Original line number Diff line number Diff line Loading @@ -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__ Loading Loading @@ -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 Loading