blob: a41f30c2bece0cadd72eeea100b14f1f18b749c6 [file] [log] [blame]
/*
* 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 <set>
#include <stack>
#include <utils/Log.h>
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;
// 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;
void writeDimensionsValueProtoToStream(const DimensionsValue& dimensionsValue,
ProtoOutputStream* protoOutput) {
protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimensionsValue.field());
switch (dimensionsValue.value_case()) {
case DimensionsValue::ValueCase::kValueStr:
protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
dimensionsValue.value_str());
break;
case DimensionsValue::ValueCase::kValueInt:
protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT,
dimensionsValue.value_int());
break;
case DimensionsValue::ValueCase::kValueLong:
protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG,
dimensionsValue.value_long());
break;
case DimensionsValue::ValueCase::kValueBool:
protoOutput->write(FIELD_TYPE_BOOL | DIMENSIONS_VALUE_VALUE_BOOL,
dimensionsValue.value_bool());
break;
case DimensionsValue::ValueCase::kValueFloat:
protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT,
dimensionsValue.value_float());
break;
case DimensionsValue::ValueCase::kValueTuple:
{
long long tupleToken = protoOutput->start(
FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
for (int i = 0; i < dimensionsValue.value_tuple().dimensions_value_size(); ++i) {
long long token = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED
| FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO);
writeDimensionsValueProtoToStream(
dimensionsValue.value_tuple().dimensions_value(i), protoOutput);
protoOutput->end(token);
}
protoOutput->end(tupleToken);
}
break;
default:
break;
}
}
// for Field Proto
const int FIELD_FIELD = 1;
const int FIELD_POSITION_INDEX = 2;
const int FIELD_CHILD = 3;
void writeFieldProtoToStream(
const Field& field, util::ProtoOutputStream* protoOutput) {
protoOutput->write(FIELD_TYPE_INT32 | FIELD_FIELD, field.field());
if (field.has_position_index()) {
protoOutput->write(FIELD_TYPE_INT32 | FIELD_POSITION_INDEX, field.position_index());
}
for (int i = 0; i < field.child_size(); ++i) {
long long childToken = protoOutput->start(
FIELD_TYPE_MESSAGE| FIELD_COUNT_REPEATED | FIELD_CHILD);
writeFieldProtoToStream(field.child(i), protoOutput);
protoOutput->end(childToken);
}
}
namespace {
void addOrUpdateChildrenMap(
const Field& root,
const Field& node,
std::map<Field, std::set<Field, FieldCmp>, FieldCmp> *childrenMap) {
Field parentNode = root;
if (node.has_position_index()) {
appendLeaf(&parentNode, node.field(), node.position_index());
} else {
appendLeaf(&parentNode, node.field());
}
if (childrenMap->find(parentNode) == childrenMap->end()) {
childrenMap->insert(std::make_pair(parentNode, std::set<Field, FieldCmp>{}));
}
auto it = childrenMap->find(parentNode);
for (int i = 0; i < node.child_size(); ++i) {
auto child = node.child(i);
Field childNode = parentNode;
if (child.has_position_index()) {
appendLeaf(&childNode, child.field(), child.position_index());
} else {
appendLeaf(&childNode, child.field());
}
it->second.insert(childNode);
addOrUpdateChildrenMap(parentNode, child, childrenMap);
}
}
void addOrUpdateChildrenMap(
const Field& field,
std::map<Field, std::set<Field, FieldCmp>, FieldCmp> *childrenMap) {
Field root;
addOrUpdateChildrenMap(root, field, childrenMap);
}
} // namespace
void writeFieldValueTreeToStream(const FieldValueMap &fieldValueMap,
util::ProtoOutputStream* protoOutput) {
std::map<Field, std::set<Field, FieldCmp>, FieldCmp> childrenMap;
// Rebuild the field tree.
for (auto it = fieldValueMap.begin(); it != fieldValueMap.end(); ++it) {
addOrUpdateChildrenMap(it->first, &childrenMap);
}
std::stack<std::pair<long long, Field>> tokenStack;
// Iterate over the node tree to fill the Atom proto.
for (auto it = childrenMap.begin(); it != childrenMap.end(); ++it) {
const Field* nodeLeaf = getSingleLeaf(&it->first);
const int fieldNum = nodeLeaf->field();
while (!tokenStack.empty()) {
auto currentMsgNode = tokenStack.top().second;
auto currentMsgNodeChildrenIt = childrenMap.find(currentMsgNode);
if (currentMsgNodeChildrenIt->second.find(it->first) ==
currentMsgNodeChildrenIt->second.end()) {
protoOutput->end(tokenStack.top().first);
tokenStack.pop();
} else {
break;
}
}
if (it->second.size() == 0) {
auto itValue = fieldValueMap.find(it->first);
if (itValue != fieldValueMap.end()) {
const DimensionsValue& value = itValue->second;
switch (value.value_case()) {
case DimensionsValue::ValueCase::kValueStr:
protoOutput->write(FIELD_TYPE_STRING | fieldNum,
value.value_str());
break;
case DimensionsValue::ValueCase::kValueInt:
protoOutput->write(FIELD_TYPE_INT32 | fieldNum,
value.value_int());
break;
case DimensionsValue::ValueCase::kValueLong:
protoOutput->write(FIELD_TYPE_INT64 | fieldNum,
value.value_long());
break;
case DimensionsValue::ValueCase::kValueBool:
protoOutput->write(FIELD_TYPE_BOOL | fieldNum,
value.value_bool());
break;
case DimensionsValue::ValueCase::kValueFloat:
protoOutput->write(FIELD_TYPE_FLOAT | fieldNum,
value.value_float());
break;
// This would not happen as the node has no child.
case DimensionsValue::ValueCase::kValueTuple:
break;
default:
break;
}
} else {
ALOGE("Leaf node value not found. This should never happen.");
}
} else {
long long token;
if (nodeLeaf->has_position_index()) {
token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum);
} else {
token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum);
}
tokenStack.push(std::make_pair(token, it->first));
}
}
while (!tokenStack.empty()) {
protoOutput->end(tokenStack.top().first);
tokenStack.pop();
}
}
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<int, StatsdStats::PulledAtomStats>& 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