all pullers have a default 1s cool down before next pull. We can adjust these later. Also add puller stats in StatsdStats Test: unit test Change-Id: I71894a24c41e059d841591312dbb852f54387b7d
273 lines
11 KiB
C++
273 lines
11 KiB
C++
/*
|
|
* 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
|