/* * Copyright (C) 2017 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. */ #include "stats_log_util.h" #include #include #include #include using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_BOOL; using android::util::FIELD_TYPE_FLOAT; using android::util::FIELD_TYPE_INT32; using android::util::FIELD_TYPE_INT64; using android::util::FIELD_TYPE_MESSAGE; using android::util::FIELD_TYPE_STRING; using android::util::ProtoOutputStream; namespace android { namespace os { namespace statsd { // for DimensionsValue Proto const int DIMENSIONS_VALUE_FIELD = 1; const int DIMENSIONS_VALUE_VALUE_STR = 2; const int DIMENSIONS_VALUE_VALUE_INT = 3; const int DIMENSIONS_VALUE_VALUE_LONG = 4; const int DIMENSIONS_VALUE_VALUE_BOOL = 5; const int DIMENSIONS_VALUE_VALUE_FLOAT = 6; const int DIMENSIONS_VALUE_VALUE_TUPLE = 7; const int DIMENSIONS_VALUE_TUPLE_VALUE = 1; // for MessageValue Proto const int FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO = 1; // for PulledAtomStats proto const int FIELD_ID_PULLED_ATOM_STATS = 10; const int FIELD_ID_PULL_ATOM_ID = 1; const int FIELD_ID_TOTAL_PULL = 2; const int FIELD_ID_TOTAL_PULL_FROM_CACHE = 3; const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4; namespace { void writeDimensionToProtoHelper(const std::vector& dims, size_t* index, int depth, int prefix, ProtoOutputStream* protoOutput) { size_t count = dims.size(); while (*index < count) { const auto& dim = dims[*index]; const int valueDepth = dim.mField.getDepth(); const int valuePrefix = dim.mField.getPrefix(depth); const int fieldNum = dim.mField.getPosAtDepth(depth); if (valueDepth > 2) { ALOGE("Depth > 2 not supported"); return; } if (depth == valueDepth && valuePrefix == prefix) { long long token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE); protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); switch (dim.mValue.getType()) { case INT: protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT, dim.mValue.int_value); break; case LONG: protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG, (long long)dim.mValue.long_value); break; case FLOAT: protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT, dim.mValue.float_value); break; case STRING: protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR, dim.mValue.str_value); break; default: break; } if (token != 0) { protoOutput->end(token); } (*index)++; } else if (valueDepth > depth && valuePrefix == prefix) { // Writing the sub tree long long dimensionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE); protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); long long tupleToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); writeDimensionToProtoHelper(dims, index, valueDepth, dim.mField.getPrefix(valueDepth), protoOutput); protoOutput->end(tupleToken); protoOutput->end(dimensionToken); } else { // Done with the prev sub tree return; } } } } // namespace void writeDimensionToProto(const HashableDimensionKey& dimension, ProtoOutputStream* protoOutput) { if (dimension.getValues().size() == 0) { return; } protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimension.getValues()[0].mField.getTag()); long long topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); size_t index = 0; writeDimensionToProtoHelper(dimension.getValues(), &index, 0, 0, protoOutput); protoOutput->end(topToken); } // for Field Proto const int FIELD_FIELD = 1; const int FIELD_POSITION_INDEX = 2; const int FIELD_CHILD = 3; // Supported Atoms format // XYZ_Atom { // repeated SubMsg field_1 = 1; // SubMsg2 field_2 = 2; // int32/float/string/int63 field_3 = 3; // } // logd's msg format, doesn't allow us to distinguish between the 2 cases below // Case (1): // Atom { // SubMsg { // int i = 1; // int j = 2; // } // repeated SubMsg // } // // and case (2): // Atom { // SubMsg { // repeated int i = 1; // repeated int j = 2; // } // optional SubMsg = 1; // } // // void writeFieldValueTreeToStreamHelper(const std::vector& dims, size_t* index, int depth, int prefix, ProtoOutputStream* protoOutput) { size_t count = dims.size(); while (*index < count) { const auto& dim = dims[*index]; const int valueDepth = dim.mField.getDepth(); const int valuePrefix = dim.mField.getPrefix(depth); const int fieldNum = dim.mField.getPosAtDepth(depth); if (valueDepth > 2) { ALOGE("Depth > 2 not supported"); return; } if (depth == valueDepth && valuePrefix == prefix) { switch (dim.mValue.getType()) { case INT: protoOutput->write(FIELD_TYPE_INT32 | fieldNum, dim.mValue.int_value); break; case LONG: protoOutput->write(FIELD_TYPE_INT64 | fieldNum, (long long)dim.mValue.long_value); break; case FLOAT: protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value); break; case STRING: protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value); break; } (*index)++; } else if (valueDepth > depth && valuePrefix == prefix) { // Writing the sub tree long long msg_token = 0; if (valueDepth == depth + 2) { msg_token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum); } else if (valueDepth == depth + 1) { msg_token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum); } // Directly jump to the leaf value because the repeated position field is implied // by the position of the sub msg in the parent field. writeFieldValueTreeToStreamHelper(dims, index, valueDepth, dim.mField.getPrefix(valueDepth), protoOutput); if (msg_token != 0) { protoOutput->end(msg_token); } } else { // Done with the prev sub tree return; } } } void writeFieldValueTreeToStream(int tagId, const std::vector& values, util::ProtoOutputStream* protoOutput) { long long atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId); size_t index = 0; writeFieldValueTreeToStreamHelper(values, &index, 0, 0, protoOutput); protoOutput->end(atomToken); } int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) { switch (unit) { case ONE_MINUTE: return 60 * 1000LL; case FIVE_MINUTES: return 5 * 60 * 1000LL; case TEN_MINUTES: return 10 * 60 * 1000LL; case THIRTY_MINUTES: return 30 * 60 * 1000LL; case ONE_HOUR: return 60 * 60 * 1000LL; case THREE_HOURS: return 3 * 60 * 60 * 1000LL; case SIX_HOURS: return 6 * 60 * 60 * 1000LL; case TWELVE_HOURS: return 12 * 60 * 60 * 1000LL; case ONE_DAY: return 24 * 60 * 60 * 1000LL; case CTS: return 1000; case TIME_UNIT_UNSPECIFIED: default: return -1; } } void writePullerStatsToStream(const std::pair& pair, util::ProtoOutputStream* protoOutput) { long long token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_PULLED_ATOM_STATS | FIELD_COUNT_REPEATED); protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_PULL_ATOM_ID, (int32_t)pair.first); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL, (long long)pair.second.totalPull); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL_FROM_CACHE, (long long)pair.second.totalPullFromCache); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_PULL_INTERVAL_SEC, (long long)pair.second.minPullIntervalSec); protoOutput->end(token); } } // namespace statsd } // namespace os } // namespace android