Merge "Reduce statsd log data size." into pi-dev

am: b85e5616df

Change-Id: Id60db7bd29b103194904b8bdf5fc1b788cf7efc2
This commit is contained in:
android-build-team Robot
2018-05-01 17:56:08 -07:00
committed by android-build-merger
41 changed files with 1299 additions and 168 deletions

View File

@@ -19,6 +19,7 @@ statsd_common_src := \
../../core/java/android/os/IStatsManager.aidl \
src/statsd_config.proto \
src/FieldValue.cpp \
src/hash.cpp \
src/stats_log_util.cpp \
src/anomaly/AlarmMonitor.cpp \
src/anomaly/AlarmTracker.cpp \

View File

@@ -237,6 +237,18 @@ bool HasPositionANY(const FieldMatcher& matcher) {
return false;
}
bool HasPositionALL(const FieldMatcher& matcher) {
if (matcher.has_position() && matcher.position() == Position::ALL) {
return true;
}
for (const auto& child : matcher.child()) {
if (HasPositionALL(child)) {
return true;
}
}
return false;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -351,6 +351,7 @@ struct FieldValue {
};
bool HasPositionANY(const FieldMatcher& matcher);
bool HasPositionALL(const FieldMatcher& matcher);
bool isAttributionUidField(const FieldValue& value);

View File

@@ -65,6 +65,7 @@ const int FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS = 4;
const int FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS = 5;
const int FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS = 6;
const int FIELD_ID_DUMP_REPORT_REASON = 8;
const int FIELD_ID_STRINGS = 9;
#define NS_PER_HOUR 3600 * NS_PER_SEC
@@ -293,6 +294,7 @@ void StatsLogProcessor::dumpStates(FILE* out, bool verbose) {
*/
void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs,
const bool include_current_partial_bucket,
const bool include_string,
const DumpReportReason dumpReportReason,
vector<uint8_t>* outData) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
@@ -320,7 +322,7 @@ void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTim
uint64_t reportsToken =
proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS);
onConfigMetricsReportLocked(key, dumpTimeStampNs, include_current_partial_bucket,
dumpReportReason, &proto);
include_string, dumpReportReason, &proto);
proto.end(reportsToken);
// End of ConfigMetricsReport (reports).
} else {
@@ -349,6 +351,7 @@ void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTim
void StatsLogProcessor::onConfigMetricsReportLocked(const ConfigKey& key,
const int64_t dumpTimeStampNs,
const bool include_current_partial_bucket,
const bool include_string,
const DumpReportReason dumpReportReason,
ProtoOutputStream* proto) {
// We already checked whether key exists in mMetricsManagers in
@@ -360,13 +363,16 @@ void StatsLogProcessor::onConfigMetricsReportLocked(const ConfigKey& key,
int64_t lastReportTimeNs = it->second->getLastReportTimeNs();
int64_t lastReportWallClockNs = it->second->getLastReportWallClockNs();
std::set<string> str_set;
// First, fill in ConfigMetricsReport using current data on memory, which
// starts from filling in StatsLogReport's.
it->second->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, proto);
it->second->onDumpReport(dumpTimeStampNs, include_current_partial_bucket,
&str_set, proto);
// Fill in UidMap.
uint64_t uidMapToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP);
mUidMap->appendUidMap(dumpTimeStampNs, key, proto);
mUidMap->appendUidMap(dumpTimeStampNs, key, &str_set, proto);
proto->end(uidMapToken);
// Fill in the timestamps.
@@ -380,6 +386,12 @@ void StatsLogProcessor::onConfigMetricsReportLocked(const ConfigKey& key,
(long long)getWallClockNs());
// Dump report reason
proto->write(FIELD_TYPE_INT32 | FIELD_ID_DUMP_REPORT_REASON, dumpReportReason);
if (include_string) {
for (const auto& str : str_set) {
proto->write(FIELD_TYPE_STRING | FIELD_COUNT_REPEATED | FIELD_ID_STRINGS, str);
}
}
}
void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs,
@@ -465,7 +477,8 @@ void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key,
const DumpReportReason dumpReportReason) {
ProtoOutputStream proto;
onConfigMetricsReportLocked(key, getElapsedRealtimeNs(),
true /* include_current_partial_bucket*/, dumpReportReason, &proto);
true /* include_current_partial_bucket*/,
false /* include strings */, dumpReportReason, &proto);
string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR,
(long)getWallClockSec(), key.GetUid(), (long long)key.GetId());
android::base::unique_fd fd(open(file_name.c_str(),

View File

@@ -63,7 +63,7 @@ public:
size_t GetMetricsSize(const ConfigKey& key) const;
void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
const bool include_current_partial_bucket, const bool include_string,
const DumpReportReason dumpReportReason, vector<uint8_t>* outData);
/* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */
@@ -126,6 +126,7 @@ private:
void onConfigMetricsReportLocked(const ConfigKey& key, const int64_t dumpTimeStampNs,
const bool include_current_partial_bucket,
const bool include_string,
const DumpReportReason dumpReportReason,
util::ProtoOutputStream* proto);

View File

@@ -592,7 +592,8 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String
if (good) {
vector<uint8_t> data;
mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(),
false /* include_current_bucket*/, ADB_DUMP, &data);
false /* include_current_bucket*/,
true /* include strings */, ADB_DUMP, &data);
// TODO: print the returned StatsLogReport to file instead of printing to logcat.
if (proto) {
for (size_t i = 0; i < data.size(); i ++) {
@@ -865,8 +866,9 @@ Status StatsService::getData(int64_t key, const String16& packageName, vector<ui
IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
ConfigKey configKey(ipc->getCallingUid(), key);
mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/,
GET_DATA_CALLED, output);
mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
false /* include_current_bucket*/, true /* include strings */,
GET_DATA_CALLED, output);
return Status::ok();
}

130
cmds/statsd/src/hash.cpp Normal file
View File

@@ -0,0 +1,130 @@
/*
* 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 "hash.h"
namespace android {
namespace os {
namespace statsd {
namespace {
// Lower-level versions of Get... that read directly from a character buffer
// without any bounds checking.
inline uint32_t DecodeFixed32(const char *ptr) {
return ((static_cast<uint32_t>(static_cast<unsigned char>(ptr[0]))) |
(static_cast<uint32_t>(static_cast<unsigned char>(ptr[1])) << 8) |
(static_cast<uint32_t>(static_cast<unsigned char>(ptr[2])) << 16) |
(static_cast<uint32_t>(static_cast<unsigned char>(ptr[3])) << 24));
}
inline uint64_t DecodeFixed64(const char* ptr) {
uint64_t lo = DecodeFixed32(ptr);
uint64_t hi = DecodeFixed32(ptr + 4);
return (hi << 32) | lo;
}
// 0xff is in case char is signed.
static inline uint32_t ByteAs32(char c) { return static_cast<uint32_t>(c) & 0xff; }
static inline uint64_t ByteAs64(char c) { return static_cast<uint64_t>(c) & 0xff; }
} // namespace
uint32_t Hash32(const char *data, size_t n, uint32_t seed) {
// 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well.
const uint32_t m = 0x5bd1e995;
const int r = 24;
// Initialize the hash to a 'random' value
uint32_t h = static_cast<uint32_t>(seed ^ n);
// Mix 4 bytes at a time into the hash
while (n >= 4) {
uint32_t k = DecodeFixed32(data);
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
data += 4;
n -= 4;
}
// Handle the last few bytes of the input array
switch (n) {
case 3:
h ^= ByteAs32(data[2]) << 16;
case 2:
h ^= ByteAs32(data[1]) << 8;
case 1:
h ^= ByteAs32(data[0]);
h *= m;
}
// Do a few final mixes of the hash to ensure the last few
// bytes are well-incorporated.
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
uint64_t Hash64(const char* data, size_t n, uint64_t seed) {
const uint64_t m = 0xc6a4a7935bd1e995;
const int r = 47;
uint64_t h = seed ^ (n * m);
while (n >= 8) {
uint64_t k = DecodeFixed64(data);
data += 8;
n -= 8;
k *= m;
k ^= k >> r;
k *= m;
h ^= k;
h *= m;
}
switch (n) {
case 7:
h ^= ByteAs64(data[6]) << 48;
case 6:
h ^= ByteAs64(data[5]) << 40;
case 5:
h ^= ByteAs64(data[4]) << 32;
case 4:
h ^= ByteAs64(data[3]) << 24;
case 3:
h ^= ByteAs64(data[2]) << 16;
case 2:
h ^= ByteAs64(data[1]) << 8;
case 1:
h ^= ByteAs64(data[0]);
h *= m;
}
h ^= h >> r;
h *= m;
h ^= h >> r;
return h;
}
} // namespace statsd
} // namespace os
} // namespace android

46
cmds/statsd/src/hash.h Normal file
View File

@@ -0,0 +1,46 @@
/*
* 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.
*/
#pragma once
#include <string>
namespace android {
namespace os {
namespace statsd {
extern uint32_t Hash32(const char *data, size_t n, uint32_t seed);
extern uint64_t Hash64(const char* data, size_t n, uint64_t seed);
inline uint32_t Hash32(const char *data, size_t n) {
return Hash32(data, n, 0xBEEF);
}
inline uint32_t Hash32(const std::string &input) {
return Hash32(input.data(), input.size());
}
inline uint64_t Hash64(const char* data, size_t n) {
return Hash64(data, n, 0xDECAFCAFFE);
}
inline uint64_t Hash64(const std::string& str) {
return Hash64(str.data(), str.size());
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -45,16 +45,23 @@ namespace statsd {
// for StatsLogReport
const int FIELD_ID_ID = 1;
const int FIELD_ID_COUNT_METRICS = 5;
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
// for CountMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for CountMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
// for CountBucketInfo
const int FIELD_ID_START_BUCKET_ELAPSED_NANOS = 1;
const int FIELD_ID_END_BUCKET_ELAPSED_NANOS = 2;
const int FIELD_ID_COUNT = 3;
const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
const int conditionIndex,
@@ -74,6 +81,9 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
}
mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
HasPositionALL(metric.dimensions_in_condition());
if (metric.has_dimensions_in_condition()) {
translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
}
@@ -130,6 +140,7 @@ void CountMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
if (include_current_partial_bucket) {
flushLocked(dumpTimeNs);
@@ -140,6 +151,26 @@ void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
return;
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
// Fills the dimension path if not slicing by ALL.
if (!mSliceByPositionALL) {
if (!mDimensionsInWhat.empty()) {
uint64_t dimenPathToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
if (!mDimensionsInCondition.empty()) {
uint64_t dimenPathToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
protoOutput->end(dimenPathToken);
}
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
for (const auto& counter : mPastBuckets) {
@@ -150,27 +181,42 @@ void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
uint64_t dimensionInWhatToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput);
protoOutput->end(dimensionInWhatToken);
if (mSliceByPositionALL) {
uint64_t dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
if (dimensionKey.hasDimensionKeyInCondition()) {
uint64_t dimensionInConditionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput);
protoOutput->end(dimensionInConditionToken);
if (dimensionKey.hasDimensionKeyInCondition()) {
uint64_t dimensionInConditionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(),
str_set, protoOutput);
protoOutput->end(dimensionInConditionToken);
}
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
if (dimensionKey.hasDimensionKeyInCondition()) {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
FIELD_ID_DIMENSION_LEAF_IN_CONDITION,
str_set, protoOutput);
}
}
// Then fill bucket_info (CountBucketInfo).
for (const auto& bucket : counter.second) {
uint64_t bucketInfoToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_NANOS,
(long long)bucket.mBucketStartNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_NANOS,
(long long)bucket.mBucketEndNs);
// Partial bucket.
if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketStartNs));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketEndNs));
} else {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
(long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount);
protoOutput->end(bucketInfoToken);
VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,

View File

@@ -57,6 +57,7 @@ private:
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) override;
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;

View File

@@ -44,16 +44,23 @@ namespace statsd {
// for StatsLogReport
const int FIELD_ID_ID = 1;
const int FIELD_ID_DURATION_METRICS = 6;
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
// for DurationMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for DurationMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
// for DurationBucketInfo
const int FIELD_ID_START_BUCKET_ELAPSED_NANOS = 1;
const int FIELD_ID_END_BUCKET_ELAPSED_NANOS = 2;
const int FIELD_ID_DURATION = 3;
const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const DurationMetric& metric,
const int conditionIndex, const size_t startIndex,
@@ -99,6 +106,9 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat
translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
}
mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
HasPositionALL(metric.dimensions_in_condition());
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
Metric2Condition mc;
@@ -445,6 +455,7 @@ void DurationMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
if (include_current_partial_bucket) {
flushLocked(dumpTimeNs);
@@ -457,6 +468,24 @@ void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
if (!mSliceByPositionALL) {
if (!mDimensionsInWhat.empty()) {
uint64_t dimenPathToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
if (!mDimensionsInCondition.empty()) {
uint64_t dimenPathToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
protoOutput->end(dimenPathToken);
}
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
VLOG("Duration metric %lld dump report now...", (long long)mMetricId);
@@ -469,26 +498,41 @@ void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
uint64_t dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput);
protoOutput->end(dimensionToken);
if (mSliceByPositionALL) {
uint64_t dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
if (dimensionKey.hasDimensionKeyInCondition()) {
uint64_t dimensionInConditionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput);
protoOutput->end(dimensionInConditionToken);
if (dimensionKey.hasDimensionKeyInCondition()) {
uint64_t dimensionInConditionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(),
str_set, protoOutput);
protoOutput->end(dimensionInConditionToken);
}
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
if (dimensionKey.hasDimensionKeyInCondition()) {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
FIELD_ID_DIMENSION_LEAF_IN_CONDITION,
str_set, protoOutput);
}
}
// Then fill bucket_info (DurationBucketInfo).
for (const auto& bucket : pair.second) {
uint64_t bucketInfoToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_NANOS,
(long long)bucket.mBucketStartNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_NANOS,
(long long)bucket.mBucketEndNs);
if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketStartNs));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketEndNs));
} else {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
(long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration);
protoOutput->end(bucketInfoToken);
VLOG("\t bucket [%lld - %lld] duration: %lld", (long long)bucket.mBucketStartNs,

View File

@@ -63,6 +63,7 @@ private:
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) override;
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;

View File

@@ -106,6 +106,7 @@ void EventMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
if (mProto->size() <= 0) {
return;

View File

@@ -48,6 +48,7 @@ private:
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) override;
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;

View File

@@ -45,21 +45,28 @@ namespace statsd {
// for StatsLogReport
const int FIELD_ID_ID = 1;
const int FIELD_ID_GAUGE_METRICS = 8;
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
// for GaugeMetricDataWrapper
const int FIELD_ID_DATA = 1;
const int FIELD_ID_SKIPPED = 2;
const int FIELD_ID_SKIPPED_START = 1;
const int FIELD_ID_SKIPPED_END = 2;
const int FIELD_ID_SKIPPED_START_MILLIS = 3;
const int FIELD_ID_SKIPPED_END_MILLIS = 4;
// for GaugeMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
// for GaugeBucketInfo
const int FIELD_ID_START_BUCKET_ELAPSED_NANOS = 1;
const int FIELD_ID_END_BUCKET_ELAPSED_NANOS = 2;
const int FIELD_ID_ATOM = 3;
const int FIELD_ID_ELAPSED_ATOM_TIMESTAMP = 4;
const int FIELD_ID_WALL_CLOCK_ATOM_TIMESTAMP = 5;
const int FIELD_ID_BUCKET_NUM = 6;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 7;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8;
GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
const int conditionIndex,
@@ -113,6 +120,8 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric
}
}
mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
HasPositionALL(metric.dimensions_in_condition());
flushIfNeededLocked(startTimeNs);
// Kicks off the puller immediately.
@@ -168,6 +177,7 @@ void GaugeMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
VLOG("Gauge metric %lld report now...", (long long)mMetricId);
if (include_current_partial_bucket) {
@@ -182,13 +192,34 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
// Fills the dimension path if not slicing by ALL.
if (!mSliceByPositionALL) {
if (!mDimensionsInWhat.empty()) {
uint64_t dimenPathToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
if (!mDimensionsInCondition.empty()) {
uint64_t dimenPathToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
protoOutput->end(dimenPathToken);
}
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
for (const auto& pair : mSkippedBuckets) {
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START, (long long)pair.first);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END, (long long)pair.second);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS,
(long long)(NanoToMillis(pair.first)));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS,
(long long)(NanoToMillis(pair.second)));
protoOutput->end(wrapperToken);
}
mSkippedBuckets.clear();
@@ -201,26 +232,43 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
uint64_t dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput);
protoOutput->end(dimensionToken);
if (mSliceByPositionALL) {
uint64_t dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
if (dimensionKey.hasDimensionKeyInCondition()) {
uint64_t dimensionInConditionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput);
protoOutput->end(dimensionInConditionToken);
if (dimensionKey.hasDimensionKeyInCondition()) {
uint64_t dimensionInConditionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(),
str_set, protoOutput);
protoOutput->end(dimensionInConditionToken);
}
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
if (dimensionKey.hasDimensionKeyInCondition()) {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
FIELD_ID_DIMENSION_LEAF_IN_CONDITION,
str_set, protoOutput);
}
}
// Then fill bucket_info (GaugeBucketInfo).
for (const auto& bucket : pair.second) {
uint64_t bucketInfoToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_NANOS,
(long long)bucket.mBucketStartNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_NANOS,
(long long)bucket.mBucketEndNs);
if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketStartNs));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketEndNs));
} else {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
(long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
}
if (!bucket.mGaugeAtoms.empty()) {
for (const auto& atom : bucket.mGaugeAtoms) {

View File

@@ -90,6 +90,7 @@ protected:
private:
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) override;
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;

View File

@@ -52,6 +52,7 @@ public:
mWizard(wizard),
mConditionTrackerIndex(conditionIndex),
mContainANYPositionInDimensionsInWhat(false),
mSliceByPositionALL(false),
mSameConditionDimensionsInTracker(false),
mHasLinksToAllConditionDimensionsInTracker(false) {
}
@@ -114,9 +115,10 @@ public:
// This method clears all the past buckets.
void onDumpReport(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) {
std::lock_guard<std::mutex> lock(mMutex);
return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, protoOutput);
return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, str_set, protoOutput);
}
void clearPastBuckets(const int64_t dumpTimeNs) {
@@ -181,6 +183,7 @@ protected:
const int64_t eventTime) = 0;
virtual void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) = 0;
virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
virtual size_t byteSizeLocked() const = 0;
@@ -218,6 +221,10 @@ protected:
return mTimeBaseNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
}
int64_t getBucketNumFromEndTimeNs(const int64_t endNs) {
return (endNs - mTimeBaseNs) / mBucketSizeNs - 1;
}
virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
const int64_t mMetricId;
@@ -250,6 +257,7 @@ protected:
vector<Matcher> mDimensionsInCondition; // The dimensions_in_condition defined in statsd_config
bool mContainANYPositionInDimensionsInWhat;
bool mSliceByPositionALL;
// True iff the condition dimensions equal to the sliced dimensions in the simple condition
// tracker. This field is always false for combinational condition trackers.

View File

@@ -36,6 +36,7 @@ using android::util::FIELD_COUNT_REPEATED;
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;
using std::make_unique;
@@ -192,14 +193,16 @@ void MetricsManager::dropData(const int64_t dropTimeNs) {
void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
VLOG("=========================Metric Reports Start==========================");
// one StatsLogReport per MetricProduer
for (const auto& producer : mAllMetricProducers) {
if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) {
uint64_t token =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, protoOutput);
uint64_t token = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, str_set,
protoOutput);
protoOutput->end(token);
} else {
producer->clearPastBuckets(dumpTimeStampNs);
@@ -213,6 +216,7 @@ void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs,
protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ANNOTATIONS_INT32, annotation.second);
protoOutput->end(token);
}
mLastReportTimeNs = dumpTimeStampNs;
mLastReportWallClockNs = getWallClockNs();
VLOG("=========================Metric Reports End==========================");

View File

@@ -92,6 +92,7 @@ public:
virtual void onDumpReport(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput);
// Computes the total byte size of all metrics managed by a single config source.

View File

@@ -48,19 +48,26 @@ namespace statsd {
// for StatsLogReport
const int FIELD_ID_ID = 1;
const int FIELD_ID_VALUE_METRICS = 7;
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
// for ValueMetricDataWrapper
const int FIELD_ID_DATA = 1;
const int FIELD_ID_SKIPPED = 2;
const int FIELD_ID_SKIPPED_START = 1;
const int FIELD_ID_SKIPPED_END = 2;
const int FIELD_ID_SKIPPED_START_MILLIS = 3;
const int FIELD_ID_SKIPPED_END_MILLIS = 4;
// for ValueMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
// for ValueBucketInfo
const int FIELD_ID_START_BUCKET_NANOS = 1;
const int FIELD_ID_END_BUCKET_NANOS = 2;
const int FIELD_ID_VALUE = 3;
const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
@@ -113,6 +120,8 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric
mField = mValueField.child(0).field();
}
mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
HasPositionALL(metric.dimensions_in_condition());
// Kicks off the puller immediately.
flushIfNeededLocked(startTimestampNs);
@@ -159,6 +168,7 @@ void ValueMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
VLOG("metric %lld dump report now...", (long long)mMetricId);
if (include_current_partial_bucket) {
@@ -170,13 +180,33 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
return;
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
// Fills the dimension path if not slicing by ALL.
if (!mSliceByPositionALL) {
if (!mDimensionsInWhat.empty()) {
uint64_t dimenPathToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
if (!mDimensionsInCondition.empty()) {
uint64_t dimenPathToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
protoOutput->end(dimenPathToken);
}
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
for (const auto& pair : mSkippedBuckets) {
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START, (long long)pair.first);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END, (long long)pair.second);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS,
(long long)(NanoToMillis(pair.first)));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS,
(long long)(NanoToMillis(pair.second)));
protoOutput->end(wrapperToken);
}
mSkippedBuckets.clear();
@@ -188,25 +218,43 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
uint64_t dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput);
protoOutput->end(dimensionToken);
if (dimensionKey.hasDimensionKeyInCondition()) {
uint64_t dimensionInConditionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput);
protoOutput->end(dimensionInConditionToken);
if (mSliceByPositionALL) {
uint64_t dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
if (dimensionKey.hasDimensionKeyInCondition()) {
uint64_t dimensionInConditionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(),
str_set, protoOutput);
protoOutput->end(dimensionInConditionToken);
}
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
if (dimensionKey.hasDimensionKeyInCondition()) {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
FIELD_ID_DIMENSION_LEAF_IN_CONDITION,
str_set, protoOutput);
}
}
// Then fill bucket_info (ValueBucketInfo).
for (const auto& bucket : pair.second) {
uint64_t bucketInfoToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
(long long)bucket.mBucketStartNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
(long long)bucket.mBucketEndNs);
if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketStartNs));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketEndNs));
} else {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
(long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE, (long long)bucket.mValue);
protoOutput->end(bucketInfoToken);
VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,

View File

@@ -88,6 +88,7 @@ protected:
private:
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) override;
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;

View File

@@ -16,6 +16,7 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "hash.h"
#include "stats_log_util.h"
#include "guardrail/StatsdStats.h"
#include "packages/UidMap.h"
@@ -34,6 +35,7 @@ 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_UINT64;
using android::util::FIELD_TYPE_MESSAGE;
using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
@@ -46,6 +48,7 @@ const int FIELD_ID_SNAPSHOT_PACKAGE_NAME = 1;
const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION = 2;
const int FIELD_ID_SNAPSHOT_PACKAGE_UID = 3;
const int FIELD_ID_SNAPSHOT_PACKAGE_DELETED = 4;
const int FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH = 5;
const int FIELD_ID_SNAPSHOT_TIMESTAMP = 1;
const int FIELD_ID_SNAPSHOT_PACKAGE_INFO = 2;
const int FIELD_ID_SNAPSHOTS = 1;
@@ -56,6 +59,7 @@ const int FIELD_ID_CHANGE_PACKAGE = 3;
const int FIELD_ID_CHANGE_UID = 4;
const int FIELD_ID_CHANGE_NEW_VERSION = 5;
const int FIELD_ID_CHANGE_PREV_VERSION = 6;
const int FIELD_ID_CHANGE_PACKAGE_HASH = 7;
UidMap::UidMap() : mBytesUsed(0) {}
@@ -312,7 +316,7 @@ size_t UidMap::getBytesUsed() const {
}
void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key,
ProtoOutputStream* proto) {
std::set<string> *str_set, ProtoOutputStream* proto) {
lock_guard<mutex> lock(mMutex); // Lock for updates
for (const ChangeRecord& record : mChanges) {
@@ -322,7 +326,14 @@ void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key,
proto->write(FIELD_TYPE_BOOL | FIELD_ID_CHANGE_DELETION, (bool)record.deletion);
proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_TIMESTAMP,
(long long)record.timestampNs);
proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package);
if (str_set != nullptr) {
str_set->insert(record.package);
proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_PACKAGE_HASH,
(long long)Hash64(record.package));
} else {
proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package);
}
proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid);
proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_NEW_VERSION, (long long)record.version);
proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_PREV_VERSION,
@@ -338,7 +349,15 @@ void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key,
for (const auto& kv : mMap) {
uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
FIELD_ID_SNAPSHOT_PACKAGE_INFO);
proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
if (str_set != nullptr) {
str_set->insert(kv.first.second);
proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH,
(long long)Hash64(kv.first.second));
} else {
proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
}
proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
(long long)kv.second.versionCode);
proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first);

View File

@@ -128,7 +128,7 @@ public:
// If every config key has received a change or snapshot record, then this
// record is deleted.
void appendUidMap(const int64_t& timestamp, const ConfigKey& key,
util::ProtoOutputStream* proto);
std::set<string> *str_set, util::ProtoOutputStream* proto);
// Forces the output to be cleared. We still generate a snapshot based on the current state.
// This results in extra data uploaded but helps us reconstruct the uid mapping on the server

View File

@@ -33,6 +33,7 @@ message DimensionsValue {
bool value_bool = 5;
float value_float = 6;
DimensionsValueTuple value_tuple = 7;
uint64 value_str_hash = 8;
}
}
@@ -54,6 +55,12 @@ message CountBucketInfo {
optional int64 end_bucket_elapsed_nanos = 2;
optional int64 count = 3;
optional int64 bucket_num = 4;
optional int64 start_bucket_elapsed_millis = 5;
optional int64 end_bucket_elapsed_millis = 6;
}
message CountMetricData {
@@ -62,6 +69,10 @@ message CountMetricData {
optional DimensionsValue dimensions_in_condition = 2;
repeated CountBucketInfo bucket_info = 3;
repeated DimensionsValue dimension_leaf_values_in_what = 4;
repeated DimensionsValue dimension_leaf_values_in_condition = 5;
}
message DurationBucketInfo {
@@ -70,6 +81,12 @@ message DurationBucketInfo {
optional int64 end_bucket_elapsed_nanos = 2;
optional int64 duration_nanos = 3;
optional int64 bucket_num = 4;
optional int64 start_bucket_elapsed_millis = 5;
optional int64 end_bucket_elapsed_millis = 6;
}
message DurationMetricData {
@@ -78,6 +95,10 @@ message DurationMetricData {
optional DimensionsValue dimensions_in_condition = 2;
repeated DurationBucketInfo bucket_info = 3;
repeated DimensionsValue dimension_leaf_values_in_what = 4;
repeated DimensionsValue dimension_leaf_values_in_condition = 5;
}
message ValueBucketInfo {
@@ -86,6 +107,12 @@ message ValueBucketInfo {
optional int64 end_bucket_elapsed_nanos = 2;
optional int64 value = 3;
optional int64 bucket_num = 4;
optional int64 start_bucket_elapsed_millis = 5;
optional int64 end_bucket_elapsed_millis = 6;
}
message ValueMetricData {
@@ -94,6 +121,10 @@ message ValueMetricData {
optional DimensionsValue dimensions_in_condition = 2;
repeated ValueBucketInfo bucket_info = 3;
repeated DimensionsValue dimension_leaf_values_in_what = 4;
repeated DimensionsValue dimension_leaf_values_in_condition = 5;
}
message GaugeBucketInfo {
@@ -106,6 +137,12 @@ message GaugeBucketInfo {
repeated int64 elapsed_timestamp_nanos = 4;
repeated int64 wall_clock_timestamp_nanos = 5;
optional int64 bucket_num = 6;
optional int64 start_bucket_elapsed_millis = 7;
optional int64 end_bucket_elapsed_millis = 8;
}
message GaugeMetricData {
@@ -114,6 +151,10 @@ message GaugeMetricData {
optional DimensionsValue dimensions_in_condition = 2;
repeated GaugeBucketInfo bucket_info = 3;
repeated DimensionsValue dimension_leaf_values_in_what = 4;
repeated DimensionsValue dimension_leaf_values_in_condition = 5;
}
message StatsLogReport {
@@ -122,8 +163,10 @@ message StatsLogReport {
// Fields 2 and 3 are reserved.
message SkippedBuckets {
optional int64 start_elapsed_nanos = 1;
optional int64 end_elapsed_nanos = 2;
optional int64 start_bucket_elapsed_nanos = 1;
optional int64 end_bucket_elapsed_nanos = 2;
optional int64 start_bucket_elapsed_millis = 3;
optional int64 end_bucket_elapsed_millis = 4;
}
message EventMetricDataWrapper {
@@ -152,6 +195,14 @@ message StatsLogReport {
ValueMetricDataWrapper value_metrics = 7;
GaugeMetricDataWrapper gauge_metrics = 8;
}
optional int64 time_base_elapsed_nano_seconds = 9;
optional int64 bucket_size_nano_seconds = 10;
optional DimensionsValue dimensions_path_in_what = 11;
optional DimensionsValue dimensions_path_in_condition = 12;
}
message UidMapping {
@@ -164,6 +215,8 @@ message UidMapping {
optional int32 uid = 3;
optional bool deleted = 4;
optional uint64 name_hash = 5;
}
optional int64 elapsed_timestamp_nanos = 1;
@@ -180,6 +233,7 @@ message UidMapping {
optional int64 new_version = 5;
optional int64 prev_version = 6;
optional uint64 app_hash = 7;
}
repeated Change changes = 2;
}
@@ -197,6 +251,12 @@ message ConfigMetricsReport {
optional int64 current_report_wall_clock_nanos = 6;
message Annotation {
optional int64 field_int64 = 1;
optional int32 field_int32 = 2;
}
repeated Annotation annotation = 7;
enum DumpReportReason {
DEVICE_SHUTDOWN = 1;
CONFIG_UPDATED = 2;
@@ -208,11 +268,7 @@ message ConfigMetricsReport {
}
optional DumpReportReason dump_report_reason = 8;
message Annotation {
optional int64 field_int64 = 1;
optional int32 field_int32 = 2;
}
repeated Annotation annotation = 7;
repeated string strings = 9;
}
message ConfigMetricsReportList {

View File

@@ -14,6 +14,7 @@
* limitations under the License.
*/
#include "hash.h"
#include "stats_log_util.h"
#include <logd/LogEvent.h>
@@ -29,6 +30,8 @@ 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_UINT64;
using android::util::FIELD_TYPE_FIXED64;
using android::util::FIELD_TYPE_MESSAGE;
using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
@@ -45,6 +48,7 @@ const int DIMENSIONS_VALUE_VALUE_LONG = 4;
// const int DIMENSIONS_VALUE_VALUE_BOOL = 5; // logd doesn't have bool data type.
const int DIMENSIONS_VALUE_VALUE_FLOAT = 6;
const int DIMENSIONS_VALUE_VALUE_TUPLE = 7;
const int DIMENSIONS_VALUE_VALUE_STR_HASH = 8;
const int DIMENSIONS_VALUE_TUPLE_VALUE = 1;
@@ -54,10 +58,12 @@ 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<FieldValue>& dims, size_t* index, int depth,
int prefix, ProtoOutputStream* protoOutput) {
int prefix, std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
size_t count = dims.size();
while (*index < count) {
const auto& dim = dims[*index];
@@ -87,8 +93,15 @@ void writeDimensionToProtoHelper(const std::vector<FieldValue>& dims, size_t* in
dim.mValue.float_value);
break;
case STRING:
protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
dim.mValue.str_value);
if (str_set == nullptr) {
protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
dim.mValue.str_value);
} else {
str_set->insert(dim.mValue.str_value);
protoOutput->write(
FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH,
(long long)Hash64(dim.mValue.str_value));
}
break;
default:
break;
@@ -105,7 +118,107 @@ void writeDimensionToProtoHelper(const std::vector<FieldValue>& dims, size_t* in
uint64_t tupleToken =
protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
writeDimensionToProtoHelper(dims, index, valueDepth, dim.mField.getPrefix(valueDepth),
protoOutput);
str_set, protoOutput);
protoOutput->end(tupleToken);
protoOutput->end(dimensionToken);
} else {
// Done with the prev sub tree
return;
}
}
}
void writeDimensionLeafToProtoHelper(const std::vector<FieldValue>& dims,
const int dimensionLeafField,
size_t* index, int depth,
int prefix, std::set<string> *str_set,
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);
if (valueDepth > 2) {
ALOGE("Depth > 2 not supported");
return;
}
if (depth == valueDepth && valuePrefix == prefix) {
uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
dimensionLeafField);
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:
if (str_set == nullptr) {
protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
dim.mValue.str_value);
} else {
str_set->insert(dim.mValue.str_value);
protoOutput->write(
FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH,
(long long)Hash64(dim.mValue.str_value));
}
break;
default:
break;
}
if (token != 0) {
protoOutput->end(token);
}
(*index)++;
} else if (valueDepth > depth && valuePrefix == prefix) {
writeDimensionLeafToProtoHelper(dims, dimensionLeafField,
index, valueDepth, dim.mField.getPrefix(valueDepth),
str_set, protoOutput);
} else {
// Done with the prev sub tree
return;
}
}
}
void writeDimensionPathToProtoHelper(const std::vector<Matcher>& fieldMatchers,
size_t* index, int depth, int prefix,
ProtoOutputStream* protoOutput) {
size_t count = fieldMatchers.size();
while (*index < count) {
const Field& field = fieldMatchers[*index].mMatcher;
const int valueDepth = field.getDepth();
const int valuePrefix = field.getPrefix(depth);
const int fieldNum = field.getPosAtDepth(depth);
if (valueDepth > 2) {
ALOGE("Depth > 2 not supported");
return;
}
if (depth == valueDepth && valuePrefix == prefix) {
uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
DIMENSIONS_VALUE_TUPLE_VALUE);
protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
if (token != 0) {
protoOutput->end(token);
}
(*index)++;
} else if (valueDepth > depth && valuePrefix == prefix) {
// Writing the sub tree
uint64_t dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE);
protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
uint64_t tupleToken =
protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
writeDimensionPathToProtoHelper(fieldMatchers, index, valueDepth,
field.getPrefix(valueDepth), protoOutput);
protoOutput->end(tupleToken);
protoOutput->end(dimensionToken);
} else {
@@ -117,7 +230,8 @@ void writeDimensionToProtoHelper(const std::vector<FieldValue>& dims, size_t* in
} // namespace
void writeDimensionToProto(const HashableDimensionKey& dimension, ProtoOutputStream* protoOutput) {
void writeDimensionToProto(const HashableDimensionKey& dimension, std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
if (dimension.getValues().size() == 0) {
return;
}
@@ -125,7 +239,32 @@ void writeDimensionToProto(const HashableDimensionKey& dimension, ProtoOutputStr
dimension.getValues()[0].mField.getTag());
uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
size_t index = 0;
writeDimensionToProtoHelper(dimension.getValues(), &index, 0, 0, protoOutput);
writeDimensionToProtoHelper(dimension.getValues(), &index, 0, 0, str_set, protoOutput);
protoOutput->end(topToken);
}
void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension,
const int dimensionLeafFieldId,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
if (dimension.getValues().size() == 0) {
return;
}
size_t index = 0;
writeDimensionLeafToProtoHelper(dimension.getValues(), dimensionLeafFieldId,
&index, 0, 0, str_set, protoOutput);
}
void writeDimensionPathToProto(const std::vector<Matcher>& fieldMatchers,
ProtoOutputStream* protoOutput) {
if (fieldMatchers.size() == 0) {
return;
}
protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD,
fieldMatchers[0].mMatcher.getTag());
uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
size_t index = 0;
writeDimensionPathToProtoHelper(fieldMatchers, &index, 0, 0, protoOutput);
protoOutput->end(topToken);
}
@@ -297,6 +436,14 @@ int64_t truncateTimestampNsToFiveMinutes(int64_t timestampNs) {
return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
}
int64_t NanoToMillis(const int64_t nano) {
return nano / 1000000;
}
int64_t MillisToNano(const int64_t millis) {
return millis * 1000000;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -28,9 +28,17 @@ namespace statsd {
void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values,
util::ProtoOutputStream* protoOutput);
void writeDimensionToProto(const HashableDimensionKey& dimension,
void writeDimensionToProto(const HashableDimensionKey& dimension, std::set<string> *str_set,
util::ProtoOutputStream* protoOutput);
void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension,
const int dimensionLeafFieldId,
std::set<string> *str_set,
util::ProtoOutputStream* protoOutput);
void writeDimensionPathToProto(const std::vector<Matcher>& fieldMatchers,
util::ProtoOutputStream* protoOutput);
// Convert the TimeUnit enum to the bucket size in millis with a guardrail on
// bucket size.
int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit);
@@ -56,6 +64,10 @@ int64_t getWallClockMillis();
// Gets the wall clock timestamp in seconds.
int64_t getWallClockSec();
int64_t NanoToMillis(const int64_t nano);
int64_t MillisToNano(const int64_t millis);
// Helper function to write PulledAtomStats to ProtoOutputStream
void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
util::ProtoOutputStream* protoOutput);

View File

@@ -226,6 +226,68 @@ TEST(AtomMatcherTest, TestMetric2ConditionLink) {
EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag());
}
TEST(AtomMatcherTest, TestWriteDimensionPath) {
for (auto position : {Position::ANY, Position::ALL, Position::FIRST, Position::LAST}) {
FieldMatcher matcher1;
matcher1.set_field(10);
FieldMatcher* child = matcher1.add_child();
child->set_field(2);
child->set_position(position);
child->add_child()->set_field(1);
child->add_child()->set_field(3);
child = matcher1.add_child();
child->set_field(4);
child = matcher1.add_child();
child->set_field(6);
child->add_child()->set_field(2);
vector<Matcher> matchers;
translateFieldMatcher(matcher1, &matchers);
android::util::ProtoOutputStream protoOut;
writeDimensionPathToProto(matchers, &protoOut);
vector<uint8_t> outData;
outData.resize(protoOut.size());
size_t pos = 0;
auto iter = protoOut.data();
while (iter.readBuffer() != NULL) {
size_t toRead = iter.currentToRead();
std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
pos += toRead;
iter.rp()->move(toRead);
}
DimensionsValue result;
EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
EXPECT_EQ(10, result.field());
EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, result.value_case());
EXPECT_EQ(3, result.value_tuple().dimensions_value_size());
const auto& dim1 = result.value_tuple().dimensions_value(0);
EXPECT_EQ(2, dim1.field());
EXPECT_EQ(2, dim1.value_tuple().dimensions_value_size());
const auto& dim11 = dim1.value_tuple().dimensions_value(0);
EXPECT_EQ(1, dim11.field());
const auto& dim12 = dim1.value_tuple().dimensions_value(1);
EXPECT_EQ(3, dim12.field());
const auto& dim2 = result.value_tuple().dimensions_value(1);
EXPECT_EQ(4, dim2.field());
const auto& dim3 = result.value_tuple().dimensions_value(2);
EXPECT_EQ(6, dim3.field());
EXPECT_EQ(1, dim3.value_tuple().dimensions_value_size());
const auto& dim31 = dim3.value_tuple().dimensions_value(0);
EXPECT_EQ(2, dim31.field());
}
}
TEST(AtomMatcherTest, TestSubscriberDimensionWrite) {
HashableDimensionKey dim;
@@ -275,7 +337,7 @@ TEST(AtomMatcherTest, TestWriteDimensionToProto) {
dim.addValue(FieldValue(field4, value4));
android::util::ProtoOutputStream protoOut;
writeDimensionToProto(dim, &protoOut);
writeDimensionToProto(dim, nullptr /* include strings */, &protoOut);
vector<uint8_t> outData;
outData.resize(protoOut.size());
@@ -315,6 +377,62 @@ TEST(AtomMatcherTest, TestWriteDimensionToProto) {
EXPECT_EQ(99999, dim2.value_int());
}
TEST(AtomMatcherTest, TestWriteDimensionLeafNodesToProto) {
HashableDimensionKey dim;
int pos1[] = {1, 1, 1};
int pos2[] = {1, 1, 2};
int pos3[] = {1, 1, 3};
int pos4[] = {2, 0, 0};
Field field1(10, pos1, 2);
Field field2(10, pos2, 2);
Field field3(10, pos3, 2);
Field field4(10, pos4, 0);
Value value1((int32_t)10025);
Value value2("tag");
Value value3((int32_t)987654);
Value value4((int64_t)99999);
dim.addValue(FieldValue(field1, value1));
dim.addValue(FieldValue(field2, value2));
dim.addValue(FieldValue(field3, value3));
dim.addValue(FieldValue(field4, value4));
android::util::ProtoOutputStream protoOut;
writeDimensionLeafNodesToProto(dim, 1, nullptr /* include strings */, &protoOut);
vector<uint8_t> outData;
outData.resize(protoOut.size());
size_t pos = 0;
auto iter = protoOut.data();
while (iter.readBuffer() != NULL) {
size_t toRead = iter.currentToRead();
std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
pos += toRead;
iter.rp()->move(toRead);
}
DimensionsValueTuple result;
EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
EXPECT_EQ(4, result.dimensions_value_size());
const auto& dim1 = result.dimensions_value(0);
EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim1.value_case());
EXPECT_EQ(10025, dim1.value_int());
const auto& dim2 = result.dimensions_value(1);
EXPECT_EQ(DimensionsValue::ValueCase::kValueStr, dim2.value_case());
EXPECT_EQ("tag", dim2.value_str());
const auto& dim3 = result.dimensions_value(2);
EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim3.value_case());
EXPECT_EQ(987654, dim3.value_int());
const auto& dim4 = result.dimensions_value(3);
EXPECT_EQ(DimensionsValue::ValueCase::kValueLong, dim4.value_case());
EXPECT_EQ(99999, dim4.value_long());
}
TEST(AtomMatcherTest, TestWriteAtomToProto) {
AttributionNodeInternal attribution_node1;
attribution_node1.set_uid(1111);

View File

@@ -139,7 +139,7 @@ TEST(StatsLogProcessorTest, TestUidMapHasSnapshot) {
// Expect to get no metrics, but snapshot specified above in uidmap.
vector<uint8_t> bytes;
p.onDumpReport(key, 1, false, ADB_DUMP, &bytes);
p.onDumpReport(key, 1, false, true, ADB_DUMP, &bytes);
ConfigMetricsReportList output;
output.ParseFromArray(bytes.data(), bytes.size());
@@ -167,7 +167,7 @@ TEST(StatsLogProcessorTest, TestReportIncludesSubConfig) {
// Expect to get no metrics, but snapshot specified above in uidmap.
vector<uint8_t> bytes;
p.onDumpReport(key, 1, false, ADB_DUMP, &bytes);
p.onDumpReport(key, 1, false, true, ADB_DUMP, &bytes);
ConfigMetricsReportList output;
output.ParseFromArray(bytes.data(), bytes.size());

View File

@@ -17,6 +17,7 @@
#include "config/ConfigKey.h"
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
#include "hash.h"
#include "statslog.h"
#include "statsd_test_util.h"
@@ -192,7 +193,7 @@ TEST(UidMapTest, TestOutputIncludesAtLeastOneSnapshot) {
m.mLastUpdatePerConfigKey[config1] = 2;
ProtoOutputStream proto;
m.appendUidMap(3, config1, &proto);
m.appendUidMap(3, config1, nullptr, &proto);
// Check there's still a uidmap attached this one.
UidMapping results;
@@ -215,7 +216,7 @@ TEST(UidMapTest, TestRemovedAppRetained) {
m.removeApp(2, String16(kApp2.c_str()), 1000);
ProtoOutputStream proto;
m.appendUidMap(3, config1, &proto);
m.appendUidMap(3, config1, nullptr, &proto);
// Snapshot should still contain this item as deleted.
UidMapping results;
@@ -243,7 +244,7 @@ TEST(UidMapTest, TestRemovedAppOverGuardrail) {
// First, verify that we have the expected number of items.
UidMapping results;
ProtoOutputStream proto;
m.appendUidMap(3, config1, &proto);
m.appendUidMap(3, config1, nullptr, &proto);
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(maxDeletedApps + 10, results.snapshots(0).package_info_size());
@@ -254,7 +255,7 @@ TEST(UidMapTest, TestRemovedAppOverGuardrail) {
}
proto.clear();
m.appendUidMap(5, config1, &proto);
m.appendUidMap(5, config1, nullptr, &proto);
// Snapshot drops the first nine items.
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(maxDeletedApps, results.snapshots(0).package_info_size());
@@ -280,14 +281,14 @@ TEST(UidMapTest, TestClearingOutput) {
m.updateMap(1, uids, versions, apps);
ProtoOutputStream proto;
m.appendUidMap(2, config1, &proto);
m.appendUidMap(2, config1, nullptr, &proto);
UidMapping results;
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(1, results.snapshots_size());
// We have to keep at least one snapshot in memory at all times.
proto.clear();
m.appendUidMap(2, config1, &proto);
m.appendUidMap(2, config1, nullptr, &proto);
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(1, results.snapshots_size());
@@ -296,7 +297,7 @@ TEST(UidMapTest, TestClearingOutput) {
m.updateApp(5, String16(kApp1.c_str()), 1000, 40);
EXPECT_EQ(1U, m.mChanges.size());
proto.clear();
m.appendUidMap(6, config1, &proto);
m.appendUidMap(6, config1, nullptr, &proto);
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(1, results.snapshots_size());
EXPECT_EQ(1, results.changes_size());
@@ -308,14 +309,14 @@ TEST(UidMapTest, TestClearingOutput) {
// We still can't remove anything.
proto.clear();
m.appendUidMap(8, config1, &proto);
m.appendUidMap(8, config1, nullptr, &proto);
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(1, results.snapshots_size());
EXPECT_EQ(1, results.changes_size());
EXPECT_EQ(2U, m.mChanges.size());
proto.clear();
m.appendUidMap(9, config2, &proto);
m.appendUidMap(9, config2, nullptr, &proto);
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(1, results.snapshots_size());
EXPECT_EQ(2, results.changes_size());
@@ -342,10 +343,10 @@ TEST(UidMapTest, TestMemoryComputed) {
ProtoOutputStream proto;
vector<uint8_t> bytes;
m.appendUidMap(2, config1, &proto);
m.appendUidMap(2, config1, nullptr, &proto);
size_t prevBytes = m.mBytesUsed;
m.appendUidMap(4, config1, &proto);
m.appendUidMap(4, config1, nullptr, &proto);
EXPECT_TRUE(m.mBytesUsed < prevBytes);
}
@@ -376,6 +377,7 @@ TEST(UidMapTest, TestMemoryGuardrail) {
m.updateApp(5, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 4);
EXPECT_EQ(1U, m.mChanges.size());
}
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif

View File

@@ -144,10 +144,13 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid) {
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -287,10 +290,13 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain) {
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);

View File

@@ -172,10 +172,13 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondi
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
ADB_DUMP, &buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1,
false, true, ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -489,10 +492,13 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationConditi
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -733,10 +739,13 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_Combination
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);

View File

@@ -130,10 +130,13 @@ TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCon
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -343,10 +346,13 @@ TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondi
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -524,10 +530,13 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondit
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -723,10 +732,13 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationConditio
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);

View File

@@ -142,10 +142,13 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition) {
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -434,10 +437,13 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition) {
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -652,10 +658,13 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);

View File

@@ -122,10 +122,13 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) {
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(1, reports.reports_size());
EXPECT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
@@ -241,10 +244,13 @@ TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents) {
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(1, reports.reports_size());
EXPECT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
@@ -342,10 +348,13 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) {
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(1, reports.reports_size());
EXPECT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;

View File

@@ -149,10 +149,13 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) {
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(1, reports.reports_size());
EXPECT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;

View File

@@ -200,10 +200,13 @@ TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) {
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
@@ -316,10 +319,13 @@ TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2) {
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);

View File

@@ -46,7 +46,7 @@ ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestam
IPCThreadState* ipc = IPCThreadState::self();
ConfigKey configKey(ipc->getCallingUid(), kConfigKey);
processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/,
ADB_DUMP, &output);
true/* include strings*/, ADB_DUMP, &output);
ConfigMetricsReportList reports;
reports.ParseFromArray(output.data(), output.size());
EXPECT_EQ(1, reports.reports_size());
@@ -153,7 +153,12 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
backfillStartEndTimestamp(&report);
EXPECT_EQ(1, report.metrics_size());
EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
has_start_bucket_elapsed_nanos());
EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
has_end_bucket_elapsed_nanos());
EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
}
@@ -171,7 +176,12 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
backfillStartEndTimestamp(&report);
EXPECT_EQ(1, report.metrics_size());
EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
has_start_bucket_elapsed_nanos());
EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
has_end_bucket_elapsed_nanos());
EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
}
@@ -206,10 +216,13 @@ TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
ConfigMetricsReport report =
GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
backfillStartEndTimestamp(&report);
EXPECT_EQ(1, report.metrics_size());
EXPECT_EQ(1, report.metrics(0).value_metrics().skipped_size());
EXPECT_TRUE(report.metrics(0).value_metrics().skipped(0).has_start_bucket_elapsed_nanos());
// Can't test the start time since it will be based on the actual time when the pulling occurs.
EXPECT_EQ(endSkipped, report.metrics(0).value_metrics().skipped(0).end_elapsed_nanos());
EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos());
}
TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
@@ -243,10 +256,13 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
ConfigMetricsReport report =
GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
backfillStartEndTimestamp(&report);
EXPECT_EQ(1, report.metrics_size());
EXPECT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
// Can't test the start time since it will be based on the actual time when the pulling occurs.
EXPECT_EQ(endSkipped, report.metrics(0).gauge_metrics().skipped(0).end_elapsed_nanos());
EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos());
EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
report.metrics(0).gauge_metrics().skipped(0).end_bucket_elapsed_nanos());
}
#else

View File

@@ -117,10 +117,13 @@ TEST(ValueMetricE2eTest, TestPulledEvents) {
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(1, reports.reports_size());
EXPECT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::ValueMetricDataWrapper valueMetrics;
@@ -221,10 +224,13 @@ TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(1, reports.reports_size());
EXPECT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::ValueMetricDataWrapper valueMetrics;

View File

@@ -127,10 +127,13 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1)
FeedEvents(config, processor);
vector<uint8_t> buffer;
ConfigMetricsReportList reports;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -161,11 +164,13 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2)
FeedEvents(config, processor);
vector<uint8_t> buffer;
ConfigMetricsReportList reports;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
@@ -210,10 +215,13 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3)
processor->OnLogEvent(event.get());
}
processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
@@ -240,11 +248,14 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1)
FeedEvents(config, processor);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
@@ -266,10 +277,13 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2)
FeedEvents(config, processor);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
@@ -309,10 +323,13 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3)
processor->OnLogEvent(event.get());
}
processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, ADB_DUMP,
&buffer);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true,
ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);

View File

@@ -645,6 +645,183 @@ bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2) {
return LessThan(s1.dimInCondition, s2.dimInCondition);
}
void backfillStringInDimension(const std::map<uint64_t, string>& str_map,
DimensionsValue* dimension) {
if (dimension->has_value_str_hash()) {
auto it = str_map.find((uint64_t)(dimension->value_str_hash()));
if (it != str_map.end()) {
dimension->clear_value_str_hash();
dimension->set_value_str(it->second);
} else {
ALOGE("Can not find the string hash: %llu",
(unsigned long long)dimension->value_str_hash());
}
} else if (dimension->has_value_tuple()) {
auto value_tuple = dimension->mutable_value_tuple();
for (int i = 0; i < value_tuple->dimensions_value_size(); ++i) {
backfillStringInDimension(str_map, value_tuple->mutable_dimensions_value(i));
}
}
}
void backfillStringInReport(ConfigMetricsReport *config_report) {
std::map<uint64_t, string> str_map;
for (const auto& str : config_report->strings()) {
uint64_t hash = Hash64(str);
if (str_map.find(hash) != str_map.end()) {
ALOGE("String hash conflicts: %s %s", str.c_str(), str_map[hash].c_str());
}
str_map[hash] = str;
}
for (int i = 0; i < config_report->metrics_size(); ++i) {
auto metric_report = config_report->mutable_metrics(i);
if (metric_report->has_count_metrics()) {
backfillStringInDimension(str_map, metric_report->mutable_count_metrics());
} else if (metric_report->has_duration_metrics()) {
backfillStringInDimension(str_map, metric_report->mutable_duration_metrics());
} else if (metric_report->has_gauge_metrics()) {
backfillStringInDimension(str_map, metric_report->mutable_gauge_metrics());
} else if (metric_report->has_value_metrics()) {
backfillStringInDimension(str_map, metric_report->mutable_value_metrics());
}
}
// Backfill the package names.
for (int i = 0 ; i < config_report->uid_map().snapshots_size(); ++i) {
auto snapshot = config_report->mutable_uid_map()->mutable_snapshots(i);
for (int j = 0 ; j < snapshot->package_info_size(); ++j) {
auto package_info = snapshot->mutable_package_info(j);
if (package_info->has_name_hash()) {
auto it = str_map.find((uint64_t)(package_info->name_hash()));
if (it != str_map.end()) {
package_info->clear_name_hash();
package_info->set_name(it->second);
} else {
ALOGE("Can not find the string package name hash: %llu",
(unsigned long long)package_info->name_hash());
}
}
}
}
// Backfill the app name in app changes.
for (int i = 0 ; i < config_report->uid_map().changes_size(); ++i) {
auto change = config_report->mutable_uid_map()->mutable_changes(i);
if (change->has_app_hash()) {
auto it = str_map.find((uint64_t)(change->app_hash()));
if (it != str_map.end()) {
change->clear_app_hash();
change->set_app(it->second);
} else {
ALOGE("Can not find the string change app name hash: %llu",
(unsigned long long)change->app_hash());
}
}
}
}
void backfillStringInReport(ConfigMetricsReportList *config_report_list) {
for (int i = 0; i < config_report_list->reports_size(); ++i) {
backfillStringInReport(config_report_list->mutable_reports(i));
}
}
bool backfillDimensionPath(const DimensionsValue& path,
const google::protobuf::RepeatedPtrField<DimensionsValue>& leafValues,
int* leafIndex,
DimensionsValue* dimension) {
dimension->set_field(path.field());
if (path.has_value_tuple()) {
for (int i = 0; i < path.value_tuple().dimensions_value_size(); ++i) {
if (!backfillDimensionPath(
path.value_tuple().dimensions_value(i), leafValues, leafIndex,
dimension->mutable_value_tuple()->add_dimensions_value())) {
return false;
}
}
} else {
if (*leafIndex < 0 || *leafIndex >= leafValues.size()) {
return false;
}
dimension->MergeFrom(leafValues.Get(*leafIndex));
(*leafIndex)++;
}
return true;
}
bool backfillDimensionPath(const DimensionsValue& path,
const google::protobuf::RepeatedPtrField<DimensionsValue>& leafValues,
DimensionsValue* dimension) {
int leafIndex = 0;
return backfillDimensionPath(path, leafValues, &leafIndex, dimension);
}
void backfillDimensionPath(ConfigMetricsReportList *config_report_list) {
for (int i = 0; i < config_report_list->reports_size(); ++i) {
auto report = config_report_list->mutable_reports(i);
for (int j = 0; j < report->metrics_size(); ++j) {
auto metric_report = report->mutable_metrics(j);
if (metric_report->has_dimensions_path_in_what() ||
metric_report->has_dimensions_path_in_condition()) {
auto whatPath = metric_report->dimensions_path_in_what();
auto conditionPath = metric_report->dimensions_path_in_condition();
if (metric_report->has_count_metrics()) {
backfillDimensionPath(whatPath, conditionPath,
metric_report->mutable_count_metrics());
} else if (metric_report->has_duration_metrics()) {
backfillDimensionPath(whatPath, conditionPath,
metric_report->mutable_duration_metrics());
} else if (metric_report->has_gauge_metrics()) {
backfillDimensionPath(whatPath, conditionPath,
metric_report->mutable_gauge_metrics());
} else if (metric_report->has_value_metrics()) {
backfillDimensionPath(whatPath, conditionPath,
metric_report->mutable_value_metrics());
}
metric_report->clear_dimensions_path_in_what();
metric_report->clear_dimensions_path_in_condition();
}
}
}
}
void backfillStartEndTimestamp(StatsLogReport *report) {
const int64_t timeBaseNs = report->time_base_elapsed_nano_seconds();
const int64_t bucketSizeNs = report->bucket_size_nano_seconds();
if (report->has_count_metrics()) {
backfillStartEndTimestampForMetrics(
timeBaseNs, bucketSizeNs, report->mutable_count_metrics());
} else if (report->has_duration_metrics()) {
backfillStartEndTimestampForMetrics(
timeBaseNs, bucketSizeNs, report->mutable_duration_metrics());
} else if (report->has_gauge_metrics()) {
backfillStartEndTimestampForMetrics(
timeBaseNs, bucketSizeNs, report->mutable_gauge_metrics());
if (report->gauge_metrics().skipped_size() > 0) {
backfillStartEndTimestampForSkippedBuckets(
timeBaseNs, report->mutable_gauge_metrics());
}
} else if (report->has_value_metrics()) {
backfillStartEndTimestampForMetrics(
timeBaseNs, bucketSizeNs, report->mutable_value_metrics());
if (report->value_metrics().skipped_size() > 0) {
backfillStartEndTimestampForSkippedBuckets(
timeBaseNs, report->mutable_value_metrics());
}
}
}
void backfillStartEndTimestamp(ConfigMetricsReport *config_report) {
for (int j = 0; j < config_report->metrics_size(); ++j) {
backfillStartEndTimestamp(config_report->mutable_metrics(j));
}
}
void backfillStartEndTimestamp(ConfigMetricsReportList *config_report_list) {
for (int i = 0; i < config_report_list->reports_size(); ++i) {
backfillStartEndTimestamp(config_report_list->mutable_reports(i));
}
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -19,12 +19,16 @@
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "src/StatsLogProcessor.h"
#include "src/logd/LogEvent.h"
#include "src/hash.h"
#include "src/stats_log_util.h"
#include "statslog.h"
namespace android {
namespace os {
namespace statsd {
using google::protobuf::RepeatedPtrField;
// Create AtomMatcher proto to simply match a specific atom type.
AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
@@ -201,6 +205,53 @@ struct DimensionsPair {
bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2);
bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2);
void backfillStartEndTimestamp(ConfigMetricsReport *config_report);
void backfillStartEndTimestamp(ConfigMetricsReportList *config_report_list);
void backfillStringInReport(ConfigMetricsReportList *config_report_list);
void backfillStringInDimension(const std::map<uint64_t, string>& str_map,
DimensionsValue* dimension);
template <typename T>
void backfillStringInDimension(const std::map<uint64_t, string>& str_map,
T* metrics) {
for (int i = 0; i < metrics->data_size(); ++i) {
auto data = metrics->mutable_data(i);
if (data->has_dimensions_in_what()) {
backfillStringInDimension(str_map, data->mutable_dimensions_in_what());
}
if (data->has_dimensions_in_condition()) {
backfillStringInDimension(str_map, data->mutable_dimensions_in_condition());
}
}
}
void backfillDimensionPath(ConfigMetricsReportList* config_report_list);
bool backfillDimensionPath(const DimensionsValue& path,
const google::protobuf::RepeatedPtrField<DimensionsValue>& leafValues,
DimensionsValue* dimension);
template <typename T>
void backfillDimensionPath(const DimensionsValue& whatPath,
const DimensionsValue& conditionPath,
T* metricData) {
for (int i = 0; i < metricData->data_size(); ++i) {
auto data = metricData->mutable_data(i);
if (data->dimension_leaf_values_in_what_size() > 0) {
backfillDimensionPath(whatPath, data->dimension_leaf_values_in_what(),
data->mutable_dimensions_in_what());
data->clear_dimension_leaf_values_in_what();
}
if (data->dimension_leaf_values_in_condition_size() > 0) {
backfillDimensionPath(conditionPath, data->dimension_leaf_values_in_condition(),
data->mutable_dimensions_in_condition());
data->clear_dimension_leaf_values_in_condition();
}
}
}
struct DimensionCompare {
bool operator()(const DimensionsPair& s1, const DimensionsPair& s2) const {
return LessThan(s1, s2);
@@ -221,6 +272,51 @@ void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) {
}
}
template <typename T>
void backfillStartEndTimestampForFullBucket(
const int64_t timeBaseNs, const int64_t bucketSizeNs, T* bucket) {
bucket->set_start_bucket_elapsed_nanos(timeBaseNs + bucketSizeNs * bucket->bucket_num());
bucket->set_end_bucket_elapsed_nanos(
timeBaseNs + bucketSizeNs * bucket->bucket_num() + bucketSizeNs);
bucket->clear_bucket_num();
}
template <typename T>
void backfillStartEndTimestampForPartialBucket(const int64_t timeBaseNs, T* bucket) {
if (bucket->has_start_bucket_elapsed_millis()) {
bucket->set_start_bucket_elapsed_nanos(
MillisToNano(bucket->start_bucket_elapsed_millis()));
bucket->clear_start_bucket_elapsed_millis();
}
if (bucket->has_end_bucket_elapsed_millis()) {
bucket->set_end_bucket_elapsed_nanos(
MillisToNano(bucket->end_bucket_elapsed_millis()));
bucket->clear_end_bucket_elapsed_millis();
}
}
template <typename T>
void backfillStartEndTimestampForMetrics(const int64_t timeBaseNs, const int64_t bucketSizeNs,
T* metrics) {
for (int i = 0; i < metrics->data_size(); ++i) {
auto data = metrics->mutable_data(i);
for (int j = 0; j < data->bucket_info_size(); ++j) {
auto bucket = data->mutable_bucket_info(j);
if (bucket->has_bucket_num()) {
backfillStartEndTimestampForFullBucket(timeBaseNs, bucketSizeNs, bucket);
} else {
backfillStartEndTimestampForPartialBucket(timeBaseNs, bucket);
}
}
}
}
template <typename T>
void backfillStartEndTimestampForSkippedBuckets(const int64_t timeBaseNs, T* metrics) {
for (int i = 0; i < metrics->skipped_size(); ++i) {
backfillStartEndTimestampForPartialBucket(timeBaseNs, metrics->mutable_skipped(i));
}
}
} // namespace statsd
} // namespace os
} // namespace android