Merge "Add StatsdStats and guardrail."
This commit is contained in:
committed by
Android (Google) Code Review
commit
6fc14c85ee
@@ -57,7 +57,8 @@ statsd_common_src := \
|
||||
src/StatsLogProcessor.cpp \
|
||||
src/StatsService.cpp \
|
||||
src/stats_util.cpp \
|
||||
src/guardrail/MemoryLeakTrackUtil.cpp
|
||||
src/guardrail/MemoryLeakTrackUtil.cpp \
|
||||
src/guardrail/StatsdStats.cpp
|
||||
|
||||
statsd_common_c_includes := \
|
||||
$(LOCAL_PATH)/src \
|
||||
@@ -167,7 +168,8 @@ LOCAL_SRC_FILES := \
|
||||
tests/metrics/MaxDurationTracker_test.cpp \
|
||||
tests/metrics/CountMetricProducer_test.cpp \
|
||||
tests/metrics/EventMetricProducer_test.cpp \
|
||||
tests/metrics/ValueMetricProducer_test.cpp
|
||||
tests/metrics/ValueMetricProducer_test.cpp \
|
||||
tests/guardrail/StatsdStats_test.cpp
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libgmock
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define DEBUG true // STOPSHIP if true
|
||||
#include "Log.h"
|
||||
#include "statslog.h"
|
||||
|
||||
@@ -21,6 +22,7 @@
|
||||
#include <dirent.h>
|
||||
#include "StatsLogProcessor.h"
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "guardrail/StatsdStats.h"
|
||||
#include "metrics/CountMetricProducer.h"
|
||||
#include "stats_util.h"
|
||||
#include "storage/StorageManager.h"
|
||||
@@ -82,6 +84,7 @@ void StatsLogProcessor::onAnomalyAlarmFired(
|
||||
|
||||
// TODO: what if statsd service restarts? How do we know what logs are already processed before?
|
||||
void StatsLogProcessor::OnLogEvent(const LogEvent& msg) {
|
||||
StatsdStats::getInstance().noteAtomLogged(msg.GetTagId(), msg.GetTimestampNs() / NS_PER_SEC);
|
||||
// pass the event to metrics managers.
|
||||
for (auto& pair : mMetricsManagers) {
|
||||
pair.second->onLogEvent(msg);
|
||||
@@ -106,23 +109,26 @@ void StatsLogProcessor::OnLogEvent(const LogEvent& msg) {
|
||||
}
|
||||
|
||||
void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
|
||||
ALOGD("Updated configuration for key %s", key.ToString().c_str());
|
||||
unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(key, config);
|
||||
|
||||
auto it = mMetricsManagers.find(key);
|
||||
if (it != mMetricsManagers.end()) {
|
||||
it->second->finish();
|
||||
} else if (mMetricsManagers.size() > StatsdStats::kMaxConfigCount) {
|
||||
ALOGE("Can't accept more configs!");
|
||||
return;
|
||||
}
|
||||
|
||||
ALOGD("Updated configuration for key %s", key.ToString().c_str());
|
||||
|
||||
unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config);
|
||||
if (newMetricsManager->isConfigValid()) {
|
||||
mUidMap->OnConfigUpdated(key);
|
||||
newMetricsManager->setAnomalyMonitor(mAnomalyMonitor);
|
||||
mMetricsManagers[key] = std::move(newMetricsManager);
|
||||
// Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)});
|
||||
ALOGD("StatsdConfig valid");
|
||||
VLOG("StatsdConfig valid");
|
||||
} else {
|
||||
// If there is any error in the config, don't use it.
|
||||
ALOGD("StatsdConfig NOT valid");
|
||||
ALOGE("StatsdConfig NOT valid");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +210,7 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
|
||||
mMetricsManagers.erase(it);
|
||||
mUidMap->OnConfigRemoved(key);
|
||||
}
|
||||
StatsdStats::getInstance().noteConfigRemoved(key);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
|
||||
mLastBroadcastTimes.erase(key);
|
||||
@@ -223,12 +230,14 @@ void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs,
|
||||
}
|
||||
}
|
||||
mLastBroadcastTimes[key] = timestampNs;
|
||||
ALOGD("StatsD requesting broadcast for %s", key.ToString().c_str());
|
||||
VLOG("StatsD requesting broadcast for %s", key.ToString().c_str());
|
||||
mSendBroadcast(key);
|
||||
StatsdStats::getInstance().noteBroadcastSent(key);
|
||||
} else if (totalBytes > kMaxSerializedBytes) { // Too late. We need to start clearing data.
|
||||
// We ignore the return value so we force each metric producer to clear its contents.
|
||||
metricsManager->onDumpReport();
|
||||
ALOGD("StatsD had to toss out metrics for %s", key.ToString().c_str());
|
||||
StatsdStats::getInstance().noteDataDrop(key);
|
||||
VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "config/ConfigKey.h"
|
||||
#include "config/ConfigManager.h"
|
||||
#include "guardrail/MemoryLeakTrackUtil.h"
|
||||
#include "guardrail/StatsdStats.h"
|
||||
#include "storage/DropboxReader.h"
|
||||
#include "storage/StorageManager.h"
|
||||
|
||||
@@ -222,7 +223,7 @@ status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>&
|
||||
}
|
||||
|
||||
if (!args[0].compare(String8("print-stats"))) {
|
||||
return cmd_print_stats(out);
|
||||
return cmd_print_stats(out, args);
|
||||
}
|
||||
|
||||
if (!args[0].compare(String8("clear-config"))) {
|
||||
@@ -305,8 +306,9 @@ void StatsService::print_cmd_help(FILE* out) {
|
||||
fprintf(out, " NAME The name of the configuration\n");
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, "usage: adb shell cmd stats print-stats\n");
|
||||
fprintf(out, "usage: adb shell cmd stats print-stats [reset]\n");
|
||||
fprintf(out, " Prints some basic stats.\n");
|
||||
fprintf(out, " reset: 1 to reset the statsd stats. default=0.\n");
|
||||
}
|
||||
|
||||
status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) {
|
||||
@@ -474,12 +476,20 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String
|
||||
}
|
||||
}
|
||||
|
||||
status_t StatsService::cmd_print_stats(FILE* out) {
|
||||
status_t StatsService::cmd_print_stats(FILE* out, const Vector<String8>& args) {
|
||||
vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys();
|
||||
for (const ConfigKey& key : configs) {
|
||||
fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
|
||||
mProcessor->GetMetricsSize(key));
|
||||
}
|
||||
fprintf(out, "Detailed statsd stats in logcat...");
|
||||
StatsdStats& statsdStats = StatsdStats::getInstance();
|
||||
bool reset = false;
|
||||
if (args.size() > 1) {
|
||||
reset = strtol(args[1].string(), NULL, 10);
|
||||
}
|
||||
vector<int8_t> output;
|
||||
statsdStats.dumpStats(&output, reset);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ private:
|
||||
/**
|
||||
* Prints some basic stats to std out.
|
||||
*/
|
||||
status_t cmd_print_stats(FILE* out);
|
||||
status_t cmd_print_stats(FILE* out, const Vector<String8>& args);
|
||||
|
||||
/**
|
||||
* Print the event log.
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "Log.h"
|
||||
|
||||
#include "SimpleConditionTracker.h"
|
||||
#include "guardrail/StatsdStats.h"
|
||||
|
||||
#include <log/logprint.h>
|
||||
|
||||
@@ -32,9 +33,10 @@ using std::unordered_map;
|
||||
using std::vector;
|
||||
|
||||
SimpleConditionTracker::SimpleConditionTracker(
|
||||
const string& name, const int index, const SimpleCondition& simpleCondition,
|
||||
const ConfigKey& key, const string& name, const int index,
|
||||
const SimpleCondition& simpleCondition,
|
||||
const unordered_map<string, int>& trackerNameIndexMap)
|
||||
: ConditionTracker(name, index) {
|
||||
: ConditionTracker(name, index), mConfigKey(key) {
|
||||
VLOG("creating SimpleConditionTracker %s", mName.c_str());
|
||||
mCountNesting = simpleCondition.count_nesting();
|
||||
|
||||
@@ -126,6 +128,24 @@ void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditio
|
||||
conditionCache[mIndex] = ConditionState::kFalse;
|
||||
}
|
||||
|
||||
bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
|
||||
if (!mSliced || mSlicedConditionState.find(newKey) != mSlicedConditionState.end()) {
|
||||
// if the condition is not sliced or the key is not new, we are good!
|
||||
return false;
|
||||
}
|
||||
// 1. Report the tuple count if the tuple count > soft limit
|
||||
if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
|
||||
size_t newTupleCount = mSlicedConditionState.size() + 1;
|
||||
StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mName, newTupleCount);
|
||||
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
|
||||
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
|
||||
ALOGE("Condition %s dropping data for dimension key %s", mName.c_str(), newKey.c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
|
||||
bool matchStart,
|
||||
std::vector<ConditionState>& conditionCache,
|
||||
@@ -133,6 +153,12 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou
|
||||
bool changed = false;
|
||||
auto outputIt = mSlicedConditionState.find(outputKey);
|
||||
ConditionState newCondition;
|
||||
if (hitGuardRail(outputKey)) {
|
||||
conditionChangedCache[mIndex] = false;
|
||||
// Tells the caller it's evaluated.
|
||||
conditionCache[mIndex] = ConditionState::kUnknown;
|
||||
return;
|
||||
}
|
||||
if (outputIt == mSlicedConditionState.end()) {
|
||||
// We get a new output key.
|
||||
newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <gtest/gtest_prod.h>
|
||||
#include "ConditionTracker.h"
|
||||
#include "config/ConfigKey.h"
|
||||
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
|
||||
#include "stats_util.h"
|
||||
|
||||
@@ -28,7 +29,7 @@ namespace statsd {
|
||||
|
||||
class SimpleConditionTracker : public virtual ConditionTracker {
|
||||
public:
|
||||
SimpleConditionTracker(const std::string& name, const int index,
|
||||
SimpleConditionTracker(const ConfigKey& key, const std::string& name, const int index,
|
||||
const SimpleCondition& simpleCondition,
|
||||
const std::unordered_map<std::string, int>& trackerNameIndexMap);
|
||||
|
||||
@@ -50,6 +51,7 @@ public:
|
||||
std::vector<ConditionState>& conditionCache) const override;
|
||||
|
||||
private:
|
||||
const ConfigKey mConfigKey;
|
||||
// The index of the LogEventMatcher which defines the start.
|
||||
int mStartLogMatcherIndex;
|
||||
|
||||
@@ -75,6 +77,8 @@ private:
|
||||
std::vector<ConditionState>& conditionCache,
|
||||
std::vector<bool>& changedCache);
|
||||
|
||||
bool hitGuardRail(const HashableDimensionKey& newKey);
|
||||
|
||||
FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
|
||||
FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
|
||||
FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
|
||||
|
||||
342
cmds/statsd/src/guardrail/StatsdStats.cpp
Normal file
342
cmds/statsd/src/guardrail/StatsdStats.cpp
Normal file
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
#define DEBUG true // STOPSHIP if true
|
||||
#include "Log.h"
|
||||
|
||||
#include "StatsdStats.h"
|
||||
|
||||
#include <android/util/ProtoOutputStream.h>
|
||||
#include "statslog.h"
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
using android::util::FIELD_COUNT_REPEATED;
|
||||
using android::util::FIELD_TYPE_BOOL;
|
||||
using android::util::FIELD_TYPE_FLOAT;
|
||||
using android::util::FIELD_TYPE_INT32;
|
||||
using android::util::FIELD_TYPE_INT64;
|
||||
using android::util::FIELD_TYPE_MESSAGE;
|
||||
using android::util::FIELD_TYPE_STRING;
|
||||
using android::util::ProtoOutputStream;
|
||||
using std::lock_guard;
|
||||
using std::map;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
const int FIELD_ID_BEGIN_TIME = 1;
|
||||
const int FIELD_ID_END_TIME = 2;
|
||||
const int FIELD_ID_CONFIG_STATS = 3;
|
||||
const int FIELD_ID_MATCHER_STATS = 4;
|
||||
const int FIELD_ID_CONDITION_STATS = 5;
|
||||
const int FIELD_ID_METRIC_STATS = 6;
|
||||
const int FIELD_ID_ATOM_STATS = 7;
|
||||
|
||||
const int FIELD_ID_MATCHER_STATS_NAME = 1;
|
||||
const int FIELD_ID_MATCHER_STATS_COUNT = 2;
|
||||
|
||||
const int FIELD_ID_CONDITION_STATS_NAME = 1;
|
||||
const int FIELD_ID_CONDITION_STATS_COUNT = 2;
|
||||
|
||||
const int FIELD_ID_METRIC_STATS_NAME = 1;
|
||||
const int FIELD_ID_METRIC_STATS_COUNT = 2;
|
||||
|
||||
const int FIELD_ID_ATOM_STATS_TAG = 1;
|
||||
const int FIELD_ID_ATOM_STATS_COUNT = 2;
|
||||
|
||||
// TODO: add stats for pulled atoms.
|
||||
StatsdStats::StatsdStats() {
|
||||
mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1);
|
||||
mStartTime = time(nullptr);
|
||||
}
|
||||
|
||||
StatsdStats& StatsdStats::getInstance() {
|
||||
static StatsdStats statsInstance;
|
||||
return statsInstance;
|
||||
}
|
||||
|
||||
void StatsdStats::noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount,
|
||||
int matchersCount, int alertsCount, bool isValid) {
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
int32_t nowTimeSec = time(nullptr);
|
||||
|
||||
// If there is an existing config for the same key, icebox the old config.
|
||||
noteConfigRemovedInternalLocked(key);
|
||||
|
||||
StatsdStatsReport_ConfigStats configStats;
|
||||
configStats.set_uid(key.GetUid());
|
||||
configStats.set_name(key.GetName());
|
||||
configStats.set_creation_time_sec(nowTimeSec);
|
||||
configStats.set_metric_count(metricsCount);
|
||||
configStats.set_condition_count(conditionsCount);
|
||||
configStats.set_matcher_count(matchersCount);
|
||||
configStats.set_alert_count(alertsCount);
|
||||
configStats.set_is_valid(isValid);
|
||||
|
||||
if (isValid) {
|
||||
mConfigStats[key] = configStats;
|
||||
} else {
|
||||
configStats.set_deletion_time_sec(nowTimeSec);
|
||||
mIceBox.push_back(configStats);
|
||||
}
|
||||
}
|
||||
|
||||
void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) {
|
||||
auto it = mConfigStats.find(key);
|
||||
if (it != mConfigStats.end()) {
|
||||
int32_t nowTimeSec = time(nullptr);
|
||||
it->second.set_deletion_time_sec(nowTimeSec);
|
||||
// Add condition stats, metrics stats, matcher stats
|
||||
addSubStatsToConfig(key, it->second);
|
||||
// Remove them after they are added to the config stats.
|
||||
mMatcherStats.erase(key);
|
||||
mMetricsStats.erase(key);
|
||||
mConditionStats.erase(key);
|
||||
mIceBox.push_back(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
void StatsdStats::noteConfigRemoved(const ConfigKey& key) {
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
noteConfigRemovedInternalLocked(key);
|
||||
}
|
||||
|
||||
void StatsdStats::noteBroadcastSent(const ConfigKey& key) {
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
auto it = mConfigStats.find(key);
|
||||
if (it == mConfigStats.end()) {
|
||||
ALOGE("Config key %s not found!", key.ToString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
it->second.add_broadcast_sent_time_sec(time(nullptr));
|
||||
}
|
||||
|
||||
void StatsdStats::noteDataDrop(const ConfigKey& key) {
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
auto it = mConfigStats.find(key);
|
||||
if (it == mConfigStats.end()) {
|
||||
ALOGE("Config key %s not found!", key.ToString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
it->second.add_data_drop_time_sec(time(nullptr));
|
||||
}
|
||||
|
||||
void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const string& name, int size) {
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
// if name doesn't exist before, it will create the key with count 0.
|
||||
auto& conditionSizeMap = mConditionStats[key];
|
||||
if (size > conditionSizeMap[name]) {
|
||||
conditionSizeMap[name] = size;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const string& name, int size) {
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
// if name doesn't exist before, it will create the key with count 0.
|
||||
auto& metricsDimensionMap = mMetricsStats[key];
|
||||
if (size > metricsDimensionMap[name]) {
|
||||
metricsDimensionMap[name] = size;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsdStats::noteMatcherMatched(const ConfigKey& key, const string& name) {
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
auto& matcherStats = mMatcherStats[key];
|
||||
matcherStats[name]++;
|
||||
}
|
||||
|
||||
void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
|
||||
if (timeSec < mStartTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (atomId > android::util::kMaxPushedAtomId) {
|
||||
ALOGW("not interested in atom %d", atomId);
|
||||
return;
|
||||
}
|
||||
|
||||
mPushedAtomStats[atomId]++;
|
||||
}
|
||||
|
||||
void StatsdStats::reset() {
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
resetInternalLocked();
|
||||
}
|
||||
|
||||
void StatsdStats::resetInternalLocked() {
|
||||
// Reset the historical data, but keep the active ConfigStats
|
||||
mStartTime = time(nullptr);
|
||||
mIceBox.clear();
|
||||
mConditionStats.clear();
|
||||
mMetricsStats.clear();
|
||||
std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0);
|
||||
mMatcherStats.clear();
|
||||
}
|
||||
|
||||
void StatsdStats::addSubStatsToConfig(const ConfigKey& key,
|
||||
StatsdStatsReport_ConfigStats& configStats) {
|
||||
// Add matcher stats
|
||||
if (mMatcherStats.find(key) != mMatcherStats.end()) {
|
||||
const auto& matcherStats = mMatcherStats[key];
|
||||
for (const auto& stats : matcherStats) {
|
||||
auto output = configStats.add_matcher_stats();
|
||||
output->set_name(stats.first);
|
||||
output->set_matched_times(stats.second);
|
||||
VLOG("matcher %s matched %d times", stats.first.c_str(), stats.second);
|
||||
}
|
||||
}
|
||||
// Add condition stats
|
||||
if (mConditionStats.find(key) != mConditionStats.end()) {
|
||||
const auto& conditionStats = mConditionStats[key];
|
||||
for (const auto& stats : conditionStats) {
|
||||
auto output = configStats.add_condition_stats();
|
||||
output->set_name(stats.first);
|
||||
output->set_max_tuple_counts(stats.second);
|
||||
VLOG("condition %s max output tuple size %d", stats.first.c_str(), stats.second);
|
||||
}
|
||||
}
|
||||
// Add metrics stats
|
||||
if (mMetricsStats.find(key) != mMetricsStats.end()) {
|
||||
const auto& conditionStats = mMetricsStats[key];
|
||||
for (const auto& stats : conditionStats) {
|
||||
auto output = configStats.add_metric_stats();
|
||||
output->set_name(stats.first);
|
||||
output->set_max_tuple_counts(stats.second);
|
||||
VLOG("metrics %s max output tuple size %d", stats.first.c_str(), stats.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsdStats::dumpStats(std::vector<int8_t>* output, bool reset) {
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
|
||||
if (DEBUG) {
|
||||
time_t t = time(nullptr);
|
||||
struct tm* tm = localtime(&t);
|
||||
char timeBuffer[80];
|
||||
strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p", tm);
|
||||
VLOG("=================StatsdStats dump begins====================");
|
||||
VLOG("Stats collection start second: %s", timeBuffer);
|
||||
}
|
||||
ProtoOutputStream proto;
|
||||
proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTime);
|
||||
proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)time(nullptr));
|
||||
|
||||
VLOG("%lu Config in icebox: ", (unsigned long)mIceBox.size());
|
||||
for (const auto& configStats : mIceBox) {
|
||||
const int numBytes = configStats.ByteSize();
|
||||
vector<char> buffer(numBytes);
|
||||
configStats.SerializeToArray(&buffer[0], numBytes);
|
||||
proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0],
|
||||
buffer.size());
|
||||
|
||||
// surround the whole block with DEBUG, so that compiler can strip out the code
|
||||
// in production.
|
||||
if (DEBUG) {
|
||||
VLOG("*****ICEBOX*****");
|
||||
VLOG("Config {%d-%s}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
|
||||
"#matcher=%d, #alert=%d, #valid=%d",
|
||||
configStats.uid(), configStats.name().c_str(), configStats.creation_time_sec(),
|
||||
configStats.deletion_time_sec(), configStats.metric_count(),
|
||||
configStats.condition_count(), configStats.matcher_count(),
|
||||
configStats.alert_count(), configStats.is_valid());
|
||||
|
||||
for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
|
||||
VLOG("\tbroadcast time: %d", broadcastTime);
|
||||
}
|
||||
|
||||
for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
|
||||
VLOG("\tdata drop time: %d", dataDropTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& pair : mConfigStats) {
|
||||
auto& configStats = pair.second;
|
||||
if (DEBUG) {
|
||||
VLOG("********Active Configs***********");
|
||||
VLOG("Config {%d-%s}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
|
||||
"#matcher=%d, #alert=%d, #valid=%d",
|
||||
configStats.uid(), configStats.name().c_str(), configStats.creation_time_sec(),
|
||||
configStats.deletion_time_sec(), configStats.metric_count(),
|
||||
configStats.condition_count(), configStats.matcher_count(),
|
||||
configStats.alert_count(), configStats.is_valid());
|
||||
for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
|
||||
VLOG("\tbroadcast time: %d", broadcastTime);
|
||||
}
|
||||
|
||||
for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
|
||||
VLOG("\tdata drop time: %d", dataDropTime);
|
||||
}
|
||||
}
|
||||
|
||||
addSubStatsToConfig(pair.first, configStats);
|
||||
|
||||
const int numBytes = configStats.ByteSize();
|
||||
vector<char> buffer(numBytes);
|
||||
configStats.SerializeToArray(&buffer[0], numBytes);
|
||||
proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0],
|
||||
buffer.size());
|
||||
// reset the sub stats, the source of truth is in the individual map
|
||||
// they will be repopulated when dumpStats() is called again.
|
||||
configStats.clear_matcher_stats();
|
||||
configStats.clear_condition_stats();
|
||||
configStats.clear_metric_stats();
|
||||
}
|
||||
|
||||
VLOG("********Atom stats***********");
|
||||
const size_t atomCounts = mPushedAtomStats.size();
|
||||
for (size_t i = 2; i < atomCounts; i++) {
|
||||
if (mPushedAtomStats[i] > 0) {
|
||||
long long token =
|
||||
proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED);
|
||||
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i);
|
||||
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]);
|
||||
proto.end(token);
|
||||
|
||||
VLOG("Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
|
||||
}
|
||||
}
|
||||
|
||||
output->clear();
|
||||
size_t bufferSize = proto.size();
|
||||
output->resize(bufferSize);
|
||||
|
||||
size_t pos = 0;
|
||||
auto it = proto.data();
|
||||
while (it.readBuffer() != NULL) {
|
||||
size_t toRead = it.currentToRead();
|
||||
std::memcpy(&((*output)[pos]), it.readBuffer(), toRead);
|
||||
pos += toRead;
|
||||
it.rp()->move(toRead);
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
resetInternalLocked();
|
||||
}
|
||||
|
||||
VLOG("reset=%d, returned proto size %lu", reset, (unsigned long)bufferSize);
|
||||
VLOG("=================StatsdStats dump ends====================");
|
||||
}
|
||||
|
||||
} // namespace statsd
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
160
cmds/statsd/src/guardrail/StatsdStats.h
Normal file
160
cmds/statsd/src/guardrail/StatsdStats.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 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 "config/ConfigKey.h"
|
||||
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
// Keeps track of stats of statsd.
|
||||
// Single instance shared across the process. All methods are thread safe.
|
||||
class StatsdStats {
|
||||
public:
|
||||
static StatsdStats& getInstance();
|
||||
~StatsdStats(){};
|
||||
|
||||
// TODO: set different limit if the device is low ram.
|
||||
const static int kDimensionKeySizeSoftLimit = 300;
|
||||
const static int kDimensionKeySizeHardLimit = 500;
|
||||
|
||||
const static int kMaxConfigCount = 10;
|
||||
const static int kMaxConditionCountPerConfig = 200;
|
||||
const static int kMaxMetricCountPerConfig = 300;
|
||||
const static int kMaxMatcherCountPerConfig = 500;
|
||||
|
||||
/**
|
||||
* Report a new config has been received and report the static stats about the config.
|
||||
*
|
||||
* The static stats include: the count of metrics, conditions, matchers, and alerts.
|
||||
* If the config is not valid, this config stats will be put into icebox immediately.
|
||||
*/
|
||||
void noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount,
|
||||
int matchersCount, int alertCount, bool isValid);
|
||||
/**
|
||||
* Report a config has been removed.
|
||||
*/
|
||||
void noteConfigRemoved(const ConfigKey& key);
|
||||
|
||||
/**
|
||||
* Report a broadcast has been sent to a config owner to collect the data.
|
||||
*/
|
||||
void noteBroadcastSent(const ConfigKey& key);
|
||||
|
||||
/**
|
||||
* Report a config's metrics data has been dropped.
|
||||
*/
|
||||
void noteDataDrop(const ConfigKey& key);
|
||||
|
||||
/**
|
||||
* Report the size of output tuple of a condition.
|
||||
*
|
||||
* Note: only report when the condition has an output dimension, and the tuple
|
||||
* count > kDimensionKeySizeSoftLimit.
|
||||
*
|
||||
* [key]: The config key that this condition belongs to.
|
||||
* [name]: The name of the condition.
|
||||
* [size]: The output tuple size.
|
||||
*/
|
||||
void noteConditionDimensionSize(const ConfigKey& key, const std::string& name, int size);
|
||||
|
||||
/**
|
||||
* Report the size of output tuple of a metric.
|
||||
*
|
||||
* Note: only report when the metric has an output dimension, and the tuple
|
||||
* count > kDimensionKeySizeSoftLimit.
|
||||
*
|
||||
* [key]: The config key that this metric belongs to.
|
||||
* [name]: The name of the metric.
|
||||
* [size]: The output tuple size.
|
||||
*/
|
||||
void noteMetricDimensionSize(const ConfigKey& key, const std::string& name, int size);
|
||||
|
||||
/**
|
||||
* Report a matcher has been matched.
|
||||
*
|
||||
* [key]: The config key that this matcher belongs to.
|
||||
* [name]: The name of the matcher.
|
||||
*/
|
||||
void noteMatcherMatched(const ConfigKey& key, const std::string& name);
|
||||
|
||||
/**
|
||||
* Report an atom event has been logged.
|
||||
*/
|
||||
void noteAtomLogged(int atomId, int32_t timeSec);
|
||||
|
||||
/**
|
||||
* Reset the historical stats. Including all stats in icebox, and the tracked stats about
|
||||
* metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
|
||||
* to collect stats after reset() has been called.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Output the stats in protobuf binary format to [buffer].
|
||||
*
|
||||
* [reset]: whether to clear the historical stats after the call.
|
||||
*/
|
||||
void dumpStats(std::vector<int8_t>* buffer, bool reset);
|
||||
|
||||
private:
|
||||
StatsdStats();
|
||||
|
||||
mutable std::mutex mLock;
|
||||
|
||||
int32_t mStartTime;
|
||||
|
||||
// The stats about the configs that are still in use.
|
||||
std::map<const ConfigKey, StatsdStatsReport_ConfigStats> mConfigStats;
|
||||
|
||||
// Stores the stats for the configs that are no longer in use.
|
||||
std::vector<const StatsdStatsReport_ConfigStats> mIceBox;
|
||||
|
||||
// Stores the number of output tuple of condition trackers when it's bigger than
|
||||
// kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
|
||||
// it means some data has been dropped.
|
||||
std::map<const ConfigKey, std::map<const std::string, int>> mConditionStats;
|
||||
|
||||
// Stores the number of output tuple of metric producers when it's bigger than
|
||||
// kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
|
||||
// it means some data has been dropped.
|
||||
std::map<const ConfigKey, std::map<const std::string, int>> mMetricsStats;
|
||||
|
||||
// Stores the number of times a pushed atom is logged.
|
||||
// The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms
|
||||
// out of that range will be dropped (it's either pulled atoms or test atoms).
|
||||
// This is a vector, not a map because it will be accessed A LOT -- for each stats log.
|
||||
std::vector<int> mPushedAtomStats;
|
||||
|
||||
// Stores how many times a matcher have been matched.
|
||||
std::map<const ConfigKey, std::map<const std::string, int>> mMatcherStats;
|
||||
|
||||
void noteConfigRemovedInternalLocked(const ConfigKey& key);
|
||||
|
||||
void resetInternalLocked();
|
||||
|
||||
void addSubStatsToConfig(const ConfigKey& key, StatsdStatsReport_ConfigStats& configStats);
|
||||
};
|
||||
|
||||
} // namespace statsd
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
@@ -69,6 +69,10 @@ public:
|
||||
return mTagIds;
|
||||
}
|
||||
|
||||
const std::string& getName() const {
|
||||
return mName;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Name of this matching. We don't really need the name, but it makes log message easy to debug.
|
||||
const std::string mName;
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
|
||||
#include "SimpleLogMatchingTracker.h"
|
||||
|
||||
#include <log/logprint.h>
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "Log.h"
|
||||
|
||||
#include "CountMetricProducer.h"
|
||||
#include "guardrail/StatsdStats.h"
|
||||
#include "stats_util.h"
|
||||
|
||||
#include <limits.h>
|
||||
@@ -63,10 +64,11 @@ const int FIELD_ID_COUNT = 3;
|
||||
|
||||
// TODO: add back AnomalyTracker.
|
||||
|
||||
CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex,
|
||||
CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
|
||||
const int conditionIndex,
|
||||
const sp<ConditionWizard>& wizard,
|
||||
const uint64_t startTimeNs)
|
||||
: MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) {
|
||||
: MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) {
|
||||
// TODO: evaluate initial conditions. and set mConditionMet.
|
||||
if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
|
||||
mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
|
||||
@@ -180,6 +182,26 @@ void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint
|
||||
mCondition = conditionMet;
|
||||
}
|
||||
|
||||
bool CountMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
|
||||
if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) {
|
||||
return false;
|
||||
}
|
||||
// ===========GuardRail==============
|
||||
// 1. Report the tuple count if the tuple count > soft limit
|
||||
if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
|
||||
size_t newTupleCount = mCurrentSlicedCounter->size() + 1;
|
||||
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
|
||||
newTupleCount);
|
||||
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
|
||||
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
|
||||
ALOGE("CountMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
|
||||
newKey.c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
void CountMetricProducer::onMatchedLogEventInternal(
|
||||
const size_t matcherIndex, const HashableDimensionKey& eventKey,
|
||||
const map<string, HashableDimensionKey>& conditionKey, bool condition,
|
||||
@@ -195,6 +217,11 @@ void CountMetricProducer::onMatchedLogEventInternal(
|
||||
auto it = mCurrentSlicedCounter->find(eventKey);
|
||||
|
||||
if (it == mCurrentSlicedCounter->end()) {
|
||||
// ===========GuardRail==============
|
||||
if (hitGuardRail(eventKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create a counter for the new key
|
||||
(*mCurrentSlicedCounter)[eventKey] = 1;
|
||||
} else {
|
||||
|
||||
@@ -42,8 +42,9 @@ struct CountBucket {
|
||||
class CountMetricProducer : public MetricProducer {
|
||||
public:
|
||||
// TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
|
||||
CountMetricProducer(const CountMetric& countMetric, const int conditionIndex,
|
||||
const sp<ConditionWizard>& wizard, const uint64_t startTimeNs);
|
||||
CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric,
|
||||
const int conditionIndex, const sp<ConditionWizard>& wizard,
|
||||
const uint64_t startTimeNs);
|
||||
|
||||
virtual ~CountMetricProducer();
|
||||
|
||||
@@ -84,6 +85,8 @@ private:
|
||||
|
||||
static const size_t kBucketSize = sizeof(CountBucket{});
|
||||
|
||||
bool hitGuardRail(const HashableDimensionKey& newKey);
|
||||
|
||||
FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
|
||||
FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
|
||||
FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "Log.h"
|
||||
#include "DurationMetricProducer.h"
|
||||
#include "guardrail/StatsdStats.h"
|
||||
#include "stats_util.h"
|
||||
|
||||
#include <limits.h>
|
||||
@@ -60,14 +61,14 @@ const int FIELD_ID_START_BUCKET_NANOS = 1;
|
||||
const int FIELD_ID_END_BUCKET_NANOS = 2;
|
||||
const int FIELD_ID_DURATION = 3;
|
||||
|
||||
DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
|
||||
DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const DurationMetric& metric,
|
||||
const int conditionIndex, const size_t startIndex,
|
||||
const size_t stopIndex, const size_t stopAllIndex,
|
||||
const bool nesting,
|
||||
const sp<ConditionWizard>& wizard,
|
||||
const vector<KeyMatcher>& internalDimension,
|
||||
const uint64_t startTimeNs)
|
||||
: MetricProducer(startTimeNs, conditionIndex, wizard),
|
||||
: MetricProducer(key, startTimeNs, conditionIndex, wizard),
|
||||
mMetric(metric),
|
||||
mStartIndex(startIndex),
|
||||
mStopIndex(stopIndex),
|
||||
@@ -113,13 +114,13 @@ unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
|
||||
const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) {
|
||||
switch (mMetric.aggregation_type()) {
|
||||
case DurationMetric_AggregationType_SUM:
|
||||
return make_unique<OringDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
|
||||
mNested, mCurrentBucketStartTimeNs,
|
||||
mBucketSizeNs, mAnomalyTrackers, bucket);
|
||||
return make_unique<OringDurationTracker>(
|
||||
mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
|
||||
mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket);
|
||||
case DurationMetric_AggregationType_MAX_SPARSE:
|
||||
return make_unique<MaxDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
|
||||
mNested, mCurrentBucketStartTimeNs,
|
||||
mBucketSizeNs, mAnomalyTrackers, bucket);
|
||||
return make_unique<MaxDurationTracker>(
|
||||
mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
|
||||
mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +239,26 @@ void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) {
|
||||
mCurrentBucketNum += numBucketsForward;
|
||||
}
|
||||
|
||||
bool DurationMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
|
||||
// the key is not new, we are good.
|
||||
if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) {
|
||||
return false;
|
||||
}
|
||||
// 1. Report the tuple count if the tuple count > soft limit
|
||||
if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
|
||||
size_t newTupleCount = mCurrentSlicedDuration.size() + 1;
|
||||
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
|
||||
newTupleCount);
|
||||
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
|
||||
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
|
||||
ALOGE("DurationMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
|
||||
newKey.c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DurationMetricProducer::onMatchedLogEventInternal(
|
||||
const size_t matcherIndex, const HashableDimensionKey& eventKey,
|
||||
const map<string, HashableDimensionKey>& conditionKeys, bool condition,
|
||||
@@ -254,6 +275,9 @@ void DurationMetricProducer::onMatchedLogEventInternal(
|
||||
HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
|
||||
|
||||
if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
|
||||
if (hitGuardRail(eventKey)) {
|
||||
return;
|
||||
}
|
||||
mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@ namespace statsd {
|
||||
|
||||
class DurationMetricProducer : public MetricProducer {
|
||||
public:
|
||||
DurationMetricProducer(const DurationMetric& durationMetric, const int conditionIndex,
|
||||
const size_t startIndex, const size_t stopIndex,
|
||||
const size_t stopAllIndex, const bool nesting,
|
||||
DurationMetricProducer(const ConfigKey& key, const DurationMetric& durationMetric,
|
||||
const int conditionIndex, const size_t startIndex,
|
||||
const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
|
||||
const sp<ConditionWizard>& wizard,
|
||||
const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs);
|
||||
|
||||
@@ -98,6 +98,7 @@ private:
|
||||
|
||||
std::unique_ptr<DurationTracker> createDurationTracker(const HashableDimensionKey& eventKey,
|
||||
std::vector<DurationBucket>& bucket);
|
||||
bool hitGuardRail(const HashableDimensionKey& newKey);
|
||||
|
||||
static const size_t kBucketSize = sizeof(DurationBucket{});
|
||||
};
|
||||
|
||||
@@ -51,10 +51,11 @@ const int FIELD_ID_DATA = 1;
|
||||
const int FIELD_ID_TIMESTAMP_NANOS = 1;
|
||||
const int FIELD_ID_ATOMS = 2;
|
||||
|
||||
EventMetricProducer::EventMetricProducer(const EventMetric& metric, const int conditionIndex,
|
||||
EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric,
|
||||
const int conditionIndex,
|
||||
const sp<ConditionWizard>& wizard,
|
||||
const uint64_t startTimeNs)
|
||||
: MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) {
|
||||
: MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) {
|
||||
if (metric.links().size() > 0) {
|
||||
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
|
||||
metric.links().end());
|
||||
|
||||
@@ -34,8 +34,9 @@ namespace statsd {
|
||||
class EventMetricProducer : public MetricProducer {
|
||||
public:
|
||||
// TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
|
||||
EventMetricProducer(const EventMetric& eventMetric, const int conditionIndex,
|
||||
const sp<ConditionWizard>& wizard, const uint64_t startTimeNs);
|
||||
EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric,
|
||||
const int conditionIndex, const sp<ConditionWizard>& wizard,
|
||||
const uint64_t startTimeNs);
|
||||
|
||||
virtual ~EventMetricProducer();
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "Log.h"
|
||||
|
||||
#include "GaugeMetricProducer.h"
|
||||
#include "guardrail/StatsdStats.h"
|
||||
#include "stats_util.h"
|
||||
|
||||
#include <cutils/log.h>
|
||||
@@ -62,10 +63,11 @@ const int FIELD_ID_START_BUCKET_NANOS = 1;
|
||||
const int FIELD_ID_END_BUCKET_NANOS = 2;
|
||||
const int FIELD_ID_GAUGE = 3;
|
||||
|
||||
GaugeMetricProducer::GaugeMetricProducer(const GaugeMetric& metric, const int conditionIndex,
|
||||
GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
|
||||
const int conditionIndex,
|
||||
const sp<ConditionWizard>& wizard, const int pullTagId,
|
||||
const int64_t startTimeNs)
|
||||
: MetricProducer(startTimeNs, conditionIndex, wizard),
|
||||
: MetricProducer(key, startTimeNs, conditionIndex, wizard),
|
||||
mMetric(metric),
|
||||
mPullTagId(pullTagId) {
|
||||
if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
|
||||
@@ -225,6 +227,26 @@ void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven
|
||||
}
|
||||
}
|
||||
|
||||
bool GaugeMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
|
||||
if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) {
|
||||
return false;
|
||||
}
|
||||
// 1. Report the tuple count if the tuple count > soft limit
|
||||
if (mCurrentSlicedBucket->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
|
||||
size_t newTupleCount = mCurrentSlicedBucket->size() + 1;
|
||||
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
|
||||
newTupleCount);
|
||||
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
|
||||
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
|
||||
ALOGE("GaugeMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
|
||||
newKey.c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GaugeMetricProducer::onMatchedLogEventInternal(
|
||||
const size_t matcherIndex, const HashableDimensionKey& eventKey,
|
||||
const map<string, HashableDimensionKey>& conditionKey, bool condition,
|
||||
@@ -244,12 +266,15 @@ void GaugeMetricProducer::onMatchedLogEventInternal(
|
||||
flushIfNeeded(eventTimeNs);
|
||||
}
|
||||
|
||||
// For gauge metric, we just simply use the first guage in the given bucket.
|
||||
// For gauge metric, we just simply use the first gauge in the given bucket.
|
||||
if (!mCurrentSlicedBucket->empty()) {
|
||||
return;
|
||||
}
|
||||
const long gauge = getGauge(event);
|
||||
if (gauge >= 0) {
|
||||
if (hitGuardRail(eventKey)) {
|
||||
return;
|
||||
}
|
||||
(*mCurrentSlicedBucket)[eventKey] = gauge;
|
||||
}
|
||||
for (auto& tracker : mAnomalyTrackers) {
|
||||
|
||||
@@ -47,9 +47,9 @@ class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDa
|
||||
public:
|
||||
// TODO: Pass in the start time from MetricsManager, it should be consistent
|
||||
// for all metrics.
|
||||
GaugeMetricProducer(const GaugeMetric& countMetric, const int conditionIndex,
|
||||
const sp<ConditionWizard>& wizard, const int pullTagId,
|
||||
const int64_t startTimeNs);
|
||||
GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& countMetric,
|
||||
const int conditionIndex, const sp<ConditionWizard>& wizard,
|
||||
const int pullTagId, const int64_t startTimeNs);
|
||||
|
||||
virtual ~GaugeMetricProducer();
|
||||
|
||||
@@ -100,6 +100,8 @@ private:
|
||||
|
||||
int64_t getGauge(const LogEvent& event);
|
||||
|
||||
bool hitGuardRail(const HashableDimensionKey& newKey);
|
||||
|
||||
static const size_t kBucketSize = sizeof(GaugeBucket{});
|
||||
|
||||
FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "anomaly/AnomalyTracker.h"
|
||||
#include "condition/ConditionWizard.h"
|
||||
#include "config/ConfigKey.h"
|
||||
#include "matchers/matcher_util.h"
|
||||
#include "packages/PackageInfoListener.h"
|
||||
|
||||
@@ -35,9 +36,10 @@ namespace statsd {
|
||||
// be a no-op.
|
||||
class MetricProducer : public virtual PackageInfoListener {
|
||||
public:
|
||||
MetricProducer(const int64_t startTimeNs, const int conditionIndex,
|
||||
MetricProducer(const ConfigKey& key, const int64_t startTimeNs, const int conditionIndex,
|
||||
const sp<ConditionWizard>& wizard)
|
||||
: mStartTimeNs(startTimeNs),
|
||||
: mConfigKey(key),
|
||||
mStartTimeNs(startTimeNs),
|
||||
mCurrentBucketStartTimeNs(startTimeNs),
|
||||
mCurrentBucketNum(0),
|
||||
mCondition(conditionIndex >= 0 ? false : true),
|
||||
@@ -83,6 +85,8 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
const ConfigKey mConfigKey;
|
||||
|
||||
const uint64_t mStartTimeNs;
|
||||
|
||||
uint64_t mCurrentBucketStartTimeNs;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "CountMetricProducer.h"
|
||||
#include "condition/CombinationConditionTracker.h"
|
||||
#include "condition/SimpleConditionTracker.h"
|
||||
#include "guardrail/StatsdStats.h"
|
||||
#include "matchers/CombinationLogMatchingTracker.h"
|
||||
#include "matchers/SimpleLogMatchingTracker.h"
|
||||
#include "metrics_manager_util.h"
|
||||
@@ -36,10 +37,24 @@ namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
MetricsManager::MetricsManager(const StatsdConfig& config) {
|
||||
mConfigValid = initStatsdConfig(config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers,
|
||||
mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
|
||||
mTrackerToMetricMap, mTrackerToConditionMap);
|
||||
MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config) : mConfigKey(key) {
|
||||
mConfigValid =
|
||||
initStatsdConfig(key, config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers,
|
||||
mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
|
||||
mTrackerToMetricMap, mTrackerToConditionMap);
|
||||
|
||||
// TODO: add alert size.
|
||||
// no matter whether this config is valid, log it in the stats.
|
||||
StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(),
|
||||
mAllConditionTrackers.size(),
|
||||
mAllLogEntryMatchers.size(), 0, mConfigValid);
|
||||
// Guardrail. Reject the config if it's too big.
|
||||
if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig ||
|
||||
mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig ||
|
||||
mAllLogEntryMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
|
||||
ALOGE("This config is too big! Reject!");
|
||||
mConfigValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
MetricsManager::~MetricsManager() {
|
||||
@@ -137,6 +152,8 @@ void MetricsManager::onLogEvent(const LogEvent& event) {
|
||||
// For matched LogEntryMatchers, tell relevant metrics that a matched event has come.
|
||||
for (size_t i = 0; i < mAllLogEntryMatchers.size(); i++) {
|
||||
if (matcherCache[i] == MatchingState::kMatched) {
|
||||
StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
|
||||
mAllLogEntryMatchers[i]->getName());
|
||||
auto pair = mTrackerToMetricMap.find(i);
|
||||
if (pair != mTrackerToMetricMap.end()) {
|
||||
auto& metricList = pair->second;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "anomaly/AnomalyMonitor.h"
|
||||
#include "anomaly/AnomalyTracker.h"
|
||||
#include "condition/ConditionTracker.h"
|
||||
#include "config/ConfigKey.h"
|
||||
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
|
||||
#include "logd/LogEvent.h"
|
||||
#include "matchers/LogMatchingTracker.h"
|
||||
@@ -33,7 +34,7 @@ namespace statsd {
|
||||
// A MetricsManager is responsible for managing metrics from one single config source.
|
||||
class MetricsManager {
|
||||
public:
|
||||
MetricsManager(const StatsdConfig& config);
|
||||
MetricsManager(const ConfigKey& configKey, const StatsdConfig& config);
|
||||
|
||||
~MetricsManager();
|
||||
|
||||
@@ -57,6 +58,8 @@ public:
|
||||
size_t byteSize();
|
||||
|
||||
private:
|
||||
const ConfigKey mConfigKey;
|
||||
|
||||
// All event tags that are interesting to my metrics.
|
||||
std::set<int> mTagIds;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "Log.h"
|
||||
|
||||
#include "ValueMetricProducer.h"
|
||||
#include "guardrail/StatsdStats.h"
|
||||
|
||||
#include <cutils/log.h>
|
||||
#include <limits.h>
|
||||
@@ -67,11 +68,12 @@ const int FIELD_ID_VALUE = 3;
|
||||
static const uint64_t kDefaultBucketSizeMillis = 60 * 60 * 1000L;
|
||||
|
||||
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
|
||||
ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
|
||||
ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
|
||||
const int conditionIndex,
|
||||
const sp<ConditionWizard>& wizard, const int pullTagId,
|
||||
const uint64_t startTimeNs,
|
||||
shared_ptr<StatsPullerManager> statsPullerManager)
|
||||
: MetricProducer(startTimeNs, conditionIndex, wizard),
|
||||
: MetricProducer(key, startTimeNs, conditionIndex, wizard),
|
||||
mMetric(metric),
|
||||
mStatsPullerManager(statsPullerManager),
|
||||
mPullTagId(pullTagId) {
|
||||
@@ -103,10 +105,11 @@ ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int co
|
||||
}
|
||||
|
||||
// for testing
|
||||
ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
|
||||
ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
|
||||
const int conditionIndex,
|
||||
const sp<ConditionWizard>& wizard, const int pullTagId,
|
||||
const uint64_t startTimeNs)
|
||||
: ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs,
|
||||
: ValueMetricProducer(key, metric, conditionIndex, wizard, pullTagId, startTimeNs,
|
||||
make_shared<StatsPullerManager>()) {
|
||||
}
|
||||
|
||||
@@ -238,6 +241,27 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven
|
||||
}
|
||||
}
|
||||
|
||||
bool ValueMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
|
||||
// ===========GuardRail==============
|
||||
// 1. Report the tuple count if the tuple count > soft limit
|
||||
if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
|
||||
return false;
|
||||
}
|
||||
if (mCurrentSlicedBucket.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
|
||||
size_t newTupleCount = mCurrentSlicedBucket.size() + 1;
|
||||
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
|
||||
newTupleCount);
|
||||
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
|
||||
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
|
||||
ALOGE("ValueMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
|
||||
newKey.c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ValueMetricProducer::onMatchedLogEventInternal(
|
||||
const size_t matcherIndex, const HashableDimensionKey& eventKey,
|
||||
const map<string, HashableDimensionKey>& conditionKey, bool condition,
|
||||
@@ -249,6 +273,9 @@ void ValueMetricProducer::onMatchedLogEventInternal(
|
||||
return;
|
||||
}
|
||||
|
||||
if (hitGuardRail(eventKey)) {
|
||||
return;
|
||||
}
|
||||
Interval& interval = mCurrentSlicedBucket[eventKey];
|
||||
|
||||
long value = get_value(event);
|
||||
|
||||
@@ -38,9 +38,9 @@ struct ValueBucket {
|
||||
|
||||
class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
|
||||
public:
|
||||
ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex,
|
||||
const sp<ConditionWizard>& wizard, const int pullTagId,
|
||||
const uint64_t startTimeNs);
|
||||
ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
|
||||
const int conditionIndex, const sp<ConditionWizard>& wizard,
|
||||
const int pullTagId, const uint64_t startTimeNs);
|
||||
|
||||
virtual ~ValueMetricProducer();
|
||||
|
||||
@@ -77,9 +77,9 @@ private:
|
||||
std::shared_ptr<StatsPullerManager> mStatsPullerManager;
|
||||
|
||||
// for testing
|
||||
ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex,
|
||||
const sp<ConditionWizard>& wizard, const int pullTagId,
|
||||
const uint64_t startTimeNs,
|
||||
ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
|
||||
const int conditionIndex, const sp<ConditionWizard>& wizard,
|
||||
const int pullTagId, const uint64_t startTimeNs,
|
||||
std::shared_ptr<StatsPullerManager> statsPullerManager);
|
||||
|
||||
Mutex mLock;
|
||||
@@ -104,6 +104,8 @@ private:
|
||||
|
||||
long get_value(const LogEvent& event);
|
||||
|
||||
bool hitGuardRail(const HashableDimensionKey& newKey);
|
||||
|
||||
static const size_t kBucketSize = sizeof(ValueBucket{});
|
||||
|
||||
FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "anomaly/AnomalyTracker.h"
|
||||
#include "condition/ConditionWizard.h"
|
||||
#include "config/ConfigKey.h"
|
||||
#include "stats_util.h"
|
||||
|
||||
namespace android {
|
||||
@@ -59,11 +60,14 @@ struct DurationBucket {
|
||||
|
||||
class DurationTracker {
|
||||
public:
|
||||
DurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
|
||||
int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
|
||||
uint64_t bucketSizeNs, const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
|
||||
DurationTracker(const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey,
|
||||
sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
|
||||
uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
|
||||
const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
|
||||
std::vector<DurationBucket>& bucket)
|
||||
: mEventKey(eventKey),
|
||||
: mConfigKey(key),
|
||||
mName(name),
|
||||
mEventKey(eventKey),
|
||||
mWizard(wizard),
|
||||
mConditionTrackerIndex(conditionIndex),
|
||||
mBucketSizeNs(bucketSizeNs),
|
||||
@@ -138,6 +142,10 @@ protected:
|
||||
}
|
||||
}
|
||||
}
|
||||
// A reference to the DurationMetricProducer's config key.
|
||||
const ConfigKey& mConfigKey;
|
||||
|
||||
const std::string mName;
|
||||
|
||||
HashableDimensionKey mEventKey;
|
||||
|
||||
|
||||
@@ -18,23 +18,50 @@
|
||||
|
||||
#include "Log.h"
|
||||
#include "MaxDurationTracker.h"
|
||||
#include "guardrail/StatsdStats.h"
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
MaxDurationTracker::MaxDurationTracker(const HashableDimensionKey& eventKey,
|
||||
MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const string& name,
|
||||
const HashableDimensionKey& eventKey,
|
||||
sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
|
||||
uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
|
||||
const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
|
||||
std::vector<DurationBucket>& bucket)
|
||||
: DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs,
|
||||
anomalyTrackers, bucket) {
|
||||
: DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
|
||||
bucketSizeNs, anomalyTrackers, bucket) {
|
||||
}
|
||||
|
||||
bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
|
||||
// ===========GuardRail==============
|
||||
if (mInfos.find(newKey) != mInfos.end()) {
|
||||
// if the key existed, we are good!
|
||||
return false;
|
||||
}
|
||||
// 1. Report the tuple count if the tuple count > soft limit
|
||||
if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
|
||||
size_t newTupleCount = mInfos.size() + 1;
|
||||
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey,
|
||||
newTupleCount);
|
||||
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
|
||||
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
|
||||
ALOGE("MaxDurTracker %s dropping data for dimension key %s", mName.c_str(),
|
||||
newKey.c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
|
||||
const uint64_t eventTime, const ConditionKey& conditionKey) {
|
||||
// this will construct a new DurationInfo if this key didn't exist.
|
||||
if (hitGuardRail(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DurationInfo& duration = mInfos[key];
|
||||
duration.conditionKeys = conditionKey;
|
||||
VLOG("MaxDuration: key %s start condition %d", key.c_str(), condition);
|
||||
|
||||
@@ -28,7 +28,8 @@ namespace statsd {
|
||||
// they stop or bucket expires.
|
||||
class MaxDurationTracker : public DurationTracker {
|
||||
public:
|
||||
MaxDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
|
||||
MaxDurationTracker(const ConfigKey& key, const string& name,
|
||||
const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
|
||||
int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
|
||||
uint64_t bucketSizeNs,
|
||||
const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
|
||||
@@ -53,6 +54,9 @@ private:
|
||||
void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
|
||||
const uint64_t timestamp);
|
||||
|
||||
// return true if we should not allow newKey to be tracked because we are above the threshold
|
||||
bool hitGuardRail(const HashableDimensionKey& newKey);
|
||||
|
||||
FRIEND_TEST(MaxDurationTrackerTest, TestSimpleMaxDuration);
|
||||
FRIEND_TEST(MaxDurationTrackerTest, TestCrossBucketBoundary);
|
||||
FRIEND_TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define DEBUG true
|
||||
#include "Log.h"
|
||||
#include "OringDurationTracker.h"
|
||||
#include "guardrail/StatsdStats.h"
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
@@ -23,21 +24,45 @@ namespace statsd {
|
||||
|
||||
using std::pair;
|
||||
|
||||
OringDurationTracker::OringDurationTracker(const HashableDimensionKey& eventKey,
|
||||
OringDurationTracker::OringDurationTracker(const ConfigKey& key, const string& name,
|
||||
const HashableDimensionKey& eventKey,
|
||||
sp<ConditionWizard> wizard, int conditionIndex,
|
||||
bool nesting, uint64_t currentBucketStartNs,
|
||||
uint64_t bucketSizeNs,
|
||||
const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
|
||||
std::vector<DurationBucket>& bucket)
|
||||
: DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs,
|
||||
anomalyTrackers, bucket),
|
||||
: DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
|
||||
bucketSizeNs, anomalyTrackers, bucket),
|
||||
mStarted(),
|
||||
mPaused() {
|
||||
mLastStartTime = 0;
|
||||
}
|
||||
|
||||
bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
|
||||
// ===========GuardRail==============
|
||||
// 1. Report the tuple count if the tuple count > soft limit
|
||||
if (mConditionKeyMap.find(newKey) != mConditionKeyMap.end()) {
|
||||
return false;
|
||||
}
|
||||
if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
|
||||
size_t newTupleCount = mConditionKeyMap.size() + 1;
|
||||
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey,
|
||||
newTupleCount);
|
||||
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
|
||||
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
|
||||
ALOGE("OringDurTracker %s dropping data for dimension key %s", mName.c_str(),
|
||||
newKey.c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
|
||||
const uint64_t eventTime, const ConditionKey& conditionKey) {
|
||||
if (hitGuardRail(key)) {
|
||||
return;
|
||||
}
|
||||
if (condition) {
|
||||
if (mStarted.size() == 0) {
|
||||
mLastStartTime = eventTime;
|
||||
|
||||
@@ -27,7 +27,8 @@ namespace statsd {
|
||||
// Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
|
||||
class OringDurationTracker : public DurationTracker {
|
||||
public:
|
||||
OringDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
|
||||
OringDurationTracker(const ConfigKey& key, const string& name,
|
||||
const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
|
||||
int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
|
||||
uint64_t bucketSizeNs,
|
||||
const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
|
||||
@@ -58,6 +59,9 @@ private:
|
||||
int64_t mLastStartTime;
|
||||
std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
|
||||
|
||||
// return true if we should not allow newKey to be tracked because we are above the threshold
|
||||
bool hitGuardRail(const HashableDimensionKey& newKey);
|
||||
|
||||
FRIEND_TEST(OringDurationTrackerTest, TestDurationOverlap);
|
||||
FRIEND_TEST(OringDurationTrackerTest, TestCrossBucketBoundary);
|
||||
FRIEND_TEST(OringDurationTrackerTest, TestDurationConditionChange);
|
||||
|
||||
@@ -134,7 +134,8 @@ bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& log
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initConditions(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
|
||||
bool initConditions(const ConfigKey& key, const StatsdConfig& config,
|
||||
const unordered_map<string, int>& logTrackerMap,
|
||||
unordered_map<string, int>& conditionTrackerMap,
|
||||
vector<sp<ConditionTracker>>& allConditionTrackers,
|
||||
unordered_map<int, std::vector<int>>& trackerToConditionMap) {
|
||||
@@ -149,7 +150,7 @@ bool initConditions(const StatsdConfig& config, const unordered_map<string, int>
|
||||
switch (condition.contents_case()) {
|
||||
case Condition::ContentsCase::kSimpleCondition: {
|
||||
allConditionTrackers.push_back(new SimpleConditionTracker(
|
||||
condition.name(), index, condition.simple_condition(), logTrackerMap));
|
||||
key, condition.name(), index, condition.simple_condition(), logTrackerMap));
|
||||
break;
|
||||
}
|
||||
case Condition::ContentsCase::kCombination: {
|
||||
@@ -184,7 +185,8 @@ bool initConditions(const StatsdConfig& config, const unordered_map<string, int>
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
|
||||
bool initMetrics(const ConfigKey& key, const StatsdConfig& config,
|
||||
const unordered_map<string, int>& logTrackerMap,
|
||||
const unordered_map<string, int>& conditionTrackerMap,
|
||||
const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
|
||||
vector<sp<ConditionTracker>>& allConditionTrackers,
|
||||
@@ -230,7 +232,7 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
|
||||
}
|
||||
|
||||
sp<MetricProducer> countProducer =
|
||||
new CountMetricProducer(metric, conditionIndex, wizard, startTimeNs);
|
||||
new CountMetricProducer(key, metric, conditionIndex, wizard, startTimeNs);
|
||||
allMetricProducers.push_back(countProducer);
|
||||
}
|
||||
|
||||
@@ -298,8 +300,8 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
|
||||
}
|
||||
|
||||
sp<MetricProducer> durationMetric = new DurationMetricProducer(
|
||||
metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2],
|
||||
nesting, wizard, internalDimension, startTimeNs);
|
||||
key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
|
||||
trackerIndices[2], nesting, wizard, internalDimension, startTimeNs);
|
||||
|
||||
allMetricProducers.push_back(durationMetric);
|
||||
}
|
||||
@@ -332,7 +334,7 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
|
||||
}
|
||||
|
||||
sp<MetricProducer> eventMetric =
|
||||
new EventMetricProducer(metric, conditionIndex, wizard, startTimeNs);
|
||||
new EventMetricProducer(key, metric, conditionIndex, wizard, startTimeNs);
|
||||
|
||||
allMetricProducers.push_back(eventMetric);
|
||||
}
|
||||
@@ -378,8 +380,8 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
|
||||
}
|
||||
}
|
||||
|
||||
sp<MetricProducer> valueProducer =
|
||||
new ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs);
|
||||
sp<MetricProducer> valueProducer = new ValueMetricProducer(key, metric, conditionIndex,
|
||||
wizard, pullTagId, startTimeNs);
|
||||
allMetricProducers.push_back(valueProducer);
|
||||
}
|
||||
|
||||
@@ -424,8 +426,8 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
|
||||
}
|
||||
}
|
||||
|
||||
sp<MetricProducer> gaugeProducer =
|
||||
new GaugeMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs);
|
||||
sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(key, metric, conditionIndex,
|
||||
wizard, pullTagId, startTimeNs);
|
||||
allMetricProducers.push_back(gaugeProducer);
|
||||
}
|
||||
return true;
|
||||
@@ -451,7 +453,7 @@ bool initAlerts(const StatsdConfig& config, const unordered_map<string, int>& me
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds,
|
||||
bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, set<int>& allTagIds,
|
||||
vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
|
||||
vector<sp<ConditionTracker>>& allConditionTrackers,
|
||||
vector<sp<MetricProducer>>& allMetricProducers,
|
||||
@@ -469,13 +471,13 @@ bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds,
|
||||
}
|
||||
ALOGD("initLogMatchingTrackers succeed...");
|
||||
|
||||
if (!initConditions(config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
|
||||
if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
|
||||
trackerToConditionMap)) {
|
||||
ALOGE("initConditionTrackers failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers,
|
||||
if (!initMetrics(key, config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers,
|
||||
allConditionTrackers, allMetricProducers, conditionToMetricMap,
|
||||
trackerToMetricMap, metricProducerMap)) {
|
||||
ALOGE("initMetricProducers failed");
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace statsd {
|
||||
|
||||
// Initialize the LogMatchingTrackers.
|
||||
// input:
|
||||
// [key]: the config key that this config belongs to
|
||||
// [config]: the input StatsdConfig
|
||||
// output:
|
||||
// [logTrackerMap]: this map should contain matcher name to index mapping
|
||||
@@ -48,6 +49,7 @@ bool initLogTrackers(const StatsdConfig& config,
|
||||
|
||||
// Initialize ConditionTrackers
|
||||
// input:
|
||||
// [key]: the config key that this config belongs to
|
||||
// [config]: the input config
|
||||
// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
|
||||
// output:
|
||||
@@ -55,7 +57,7 @@ bool initLogTrackers(const StatsdConfig& config,
|
||||
// [allConditionTrackers]: stores the sp to all the ConditionTrackers
|
||||
// [trackerToConditionMap]: contain the mapping from index of
|
||||
// log tracker to condition trackers that use the log tracker
|
||||
bool initConditions(const StatsdConfig& config,
|
||||
bool initConditions(const ConfigKey& key, const StatsdConfig& config,
|
||||
const std::unordered_map<std::string, int>& logTrackerMap,
|
||||
std::unordered_map<std::string, int>& conditionTrackerMap,
|
||||
std::vector<sp<ConditionTracker>>& allConditionTrackers,
|
||||
@@ -64,6 +66,7 @@ bool initConditions(const StatsdConfig& config,
|
||||
|
||||
// Initialize MetricProducers.
|
||||
// input:
|
||||
// [key]: the config key that this config belongs to
|
||||
// [config]: the input config
|
||||
// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
|
||||
// [conditionTrackerMap]: condition name to index mapping
|
||||
@@ -73,7 +76,8 @@ bool initConditions(const StatsdConfig& config,
|
||||
// the list of MetricProducer index
|
||||
// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
|
||||
bool initMetrics(
|
||||
const StatsdConfig& config, const std::unordered_map<std::string, int>& logTrackerMap,
|
||||
const ConfigKey& key, const StatsdConfig& config,
|
||||
const std::unordered_map<std::string, int>& logTrackerMap,
|
||||
const std::unordered_map<std::string, int>& conditionTrackerMap,
|
||||
const std::unordered_map<int, std::vector<EventConditionLink>>& eventConditionLinks,
|
||||
const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
|
||||
@@ -84,7 +88,7 @@ bool initMetrics(
|
||||
|
||||
// Initialize MetricsManager from StatsdConfig.
|
||||
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
|
||||
bool initStatsdConfig(const StatsdConfig& config, std::set<int>& allTagIds,
|
||||
bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, std::set<int>& allTagIds,
|
||||
std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
|
||||
std::vector<sp<ConditionTracker>>& allConditionTrackers,
|
||||
std::vector<sp<MetricProducer>>& allMetricProducers,
|
||||
|
||||
@@ -172,3 +172,51 @@ message ConfigMetricsReportList {
|
||||
|
||||
repeated ConfigMetricsReport reports = 2;
|
||||
}
|
||||
|
||||
message StatsdStatsReport {
|
||||
optional int32 stats_begin_time_sec = 1;
|
||||
|
||||
optional int32 stats_end_time_sec = 2;
|
||||
|
||||
message MatcherStats {
|
||||
optional string name = 1;
|
||||
optional int32 matched_times = 2;
|
||||
}
|
||||
|
||||
message ConditionStats {
|
||||
optional string name = 1;
|
||||
optional int32 max_tuple_counts = 2;
|
||||
}
|
||||
|
||||
message MetricStats {
|
||||
optional string name = 1;
|
||||
optional int32 max_tuple_counts = 2;
|
||||
}
|
||||
|
||||
message ConfigStats {
|
||||
optional int32 uid = 1;
|
||||
optional string name = 2;
|
||||
optional int32 creation_time_sec = 3;
|
||||
optional int32 deletion_time_sec = 4;
|
||||
optional int32 metric_count = 5;
|
||||
optional int32 condition_count = 6;
|
||||
optional int32 matcher_count = 7;
|
||||
optional int32 alert_count = 8;
|
||||
optional bool is_valid = 9;
|
||||
|
||||
repeated int32 broadcast_sent_time_sec = 10;
|
||||
repeated int32 data_drop_time_sec = 11;
|
||||
repeated MatcherStats matcher_stats = 12;
|
||||
repeated ConditionStats condition_stats = 13;
|
||||
repeated MetricStats metric_stats = 14;
|
||||
}
|
||||
|
||||
repeated ConfigStats config_stats = 3;
|
||||
|
||||
message AtomStats {
|
||||
optional int32 tag = 1;
|
||||
optional int32 count = 2;
|
||||
}
|
||||
|
||||
repeated AtomStats atom_stats = 7;
|
||||
}
|
||||
@@ -62,7 +62,8 @@ MATCHER_P(StatsdConfigEq, name, "") {
|
||||
}
|
||||
|
||||
TEST(ConfigManagerTest, TestFakeConfig) {
|
||||
auto metricsManager = std::make_unique<MetricsManager>(build_fake_config());
|
||||
auto metricsManager =
|
||||
std::make_unique<MetricsManager>(ConfigKey(0, "test"), build_fake_config());
|
||||
EXPECT_TRUE(metricsManager->isConfigValid());
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ using android::os::statsd::Condition;
|
||||
|
||||
// TODO: ADD MORE TEST CASES.
|
||||
|
||||
const ConfigKey kConfigKey(0, "test");
|
||||
|
||||
StatsdConfig buildGoodConfig() {
|
||||
StatsdConfig config;
|
||||
config.set_name("12345");
|
||||
@@ -254,9 +256,9 @@ TEST(MetricsManagerTest, TestGoodConfig) {
|
||||
unordered_map<int, std::vector<int>> trackerToMetricMap;
|
||||
unordered_map<int, std::vector<int>> trackerToConditionMap;
|
||||
|
||||
EXPECT_TRUE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
|
||||
allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
|
||||
trackerToMetricMap, trackerToConditionMap));
|
||||
EXPECT_TRUE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
|
||||
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
|
||||
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
|
||||
EXPECT_EQ(1u, allMetricProducers.size());
|
||||
EXPECT_EQ(1u, allAnomalyTrackers.size());
|
||||
}
|
||||
@@ -272,9 +274,9 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
|
||||
unordered_map<int, std::vector<int>> trackerToMetricMap;
|
||||
unordered_map<int, std::vector<int>> trackerToConditionMap;
|
||||
|
||||
EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
|
||||
allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
|
||||
trackerToMetricMap, trackerToConditionMap));
|
||||
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
|
||||
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
|
||||
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
|
||||
}
|
||||
|
||||
TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
|
||||
@@ -288,9 +290,9 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
|
||||
unordered_map<int, std::vector<int>> trackerToMetricMap;
|
||||
unordered_map<int, std::vector<int>> trackerToConditionMap;
|
||||
|
||||
EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
|
||||
allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
|
||||
trackerToMetricMap, trackerToConditionMap));
|
||||
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
|
||||
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
|
||||
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
|
||||
}
|
||||
|
||||
TEST(MetricsManagerTest, TestMissingMatchers) {
|
||||
@@ -303,9 +305,9 @@ TEST(MetricsManagerTest, TestMissingMatchers) {
|
||||
unordered_map<int, std::vector<int>> conditionToMetricMap;
|
||||
unordered_map<int, std::vector<int>> trackerToMetricMap;
|
||||
unordered_map<int, std::vector<int>> trackerToConditionMap;
|
||||
EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
|
||||
allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
|
||||
trackerToMetricMap, trackerToConditionMap));
|
||||
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
|
||||
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
|
||||
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
|
||||
}
|
||||
|
||||
TEST(MetricsManagerTest, TestCircleConditionDependency) {
|
||||
@@ -319,9 +321,9 @@ TEST(MetricsManagerTest, TestCircleConditionDependency) {
|
||||
unordered_map<int, std::vector<int>> trackerToMetricMap;
|
||||
unordered_map<int, std::vector<int>> trackerToConditionMap;
|
||||
|
||||
EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
|
||||
allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
|
||||
trackerToMetricMap, trackerToConditionMap));
|
||||
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
|
||||
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
|
||||
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
|
||||
}
|
||||
|
||||
TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
|
||||
@@ -335,9 +337,9 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
|
||||
unordered_map<int, std::vector<int>> trackerToMetricMap;
|
||||
unordered_map<int, std::vector<int>> trackerToConditionMap;
|
||||
|
||||
EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
|
||||
allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
|
||||
trackerToMetricMap, trackerToConditionMap));
|
||||
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
|
||||
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
|
||||
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
const ConfigKey kConfigKey(0, "test");
|
||||
|
||||
SimpleCondition getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
|
||||
bool outputSlicedUid) {
|
||||
SimpleCondition simpleCondition;
|
||||
@@ -76,8 +78,8 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) {
|
||||
trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
|
||||
trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
|
||||
|
||||
SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*tracker index*/, simpleCondition,
|
||||
trackerNameIndexMap);
|
||||
SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON", 0 /*tracker index*/,
|
||||
simpleCondition, trackerNameIndexMap);
|
||||
|
||||
LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
|
||||
|
||||
@@ -158,8 +160,9 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) {
|
||||
trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
|
||||
trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
|
||||
|
||||
SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*condition tracker index*/,
|
||||
simpleCondition, trackerNameIndexMap);
|
||||
SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON",
|
||||
0 /*condition tracker index*/, simpleCondition,
|
||||
trackerNameIndexMap);
|
||||
|
||||
LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
|
||||
|
||||
@@ -227,8 +230,9 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
|
||||
trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
|
||||
trackerNameIndexMap["RELEASE_ALL"] = 2;
|
||||
|
||||
SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
|
||||
simpleCondition, trackerNameIndexMap);
|
||||
SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
|
||||
0 /*condition tracker index*/, simpleCondition,
|
||||
trackerNameIndexMap);
|
||||
int uid = 111;
|
||||
|
||||
LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
|
||||
@@ -308,8 +312,9 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
|
||||
trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
|
||||
trackerNameIndexMap["RELEASE_ALL"] = 2;
|
||||
|
||||
SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
|
||||
simpleCondition, trackerNameIndexMap);
|
||||
SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
|
||||
0 /*condition tracker index*/, simpleCondition,
|
||||
trackerNameIndexMap);
|
||||
int uid1 = 111;
|
||||
string uid1_wl1 = "wl1_1";
|
||||
int uid2 = 222;
|
||||
@@ -392,8 +397,9 @@ TEST(SimpleConditionTrackerTest, TestStopAll) {
|
||||
trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
|
||||
trackerNameIndexMap["RELEASE_ALL"] = 2;
|
||||
|
||||
SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
|
||||
simpleCondition, trackerNameIndexMap);
|
||||
SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
|
||||
0 /*condition tracker index*/, simpleCondition,
|
||||
trackerNameIndexMap);
|
||||
int uid1 = 111;
|
||||
int uid2 = 222;
|
||||
|
||||
|
||||
50
cmds/statsd/tests/guardrail/StatsdStats_test.cpp
Normal file
50
cmds/statsd/tests/guardrail/StatsdStats_test.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// 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 "src/guardrail/StatsdStats.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
TEST(StatsdStatsTest, TestConfigAdd) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
TEST(StatsdStatsTest, TestConfigRemove) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
TEST(StatsdStatsTest, TestMatcherReport) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
TEST(StatsdStatsTest, TestConditionReport) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
TEST(StatsdStatsTest, TestAtomLog) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
} // namespace statsd
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
#endif
|
||||
@@ -32,6 +32,8 @@ namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
const ConfigKey kConfigKey(0, "test");
|
||||
|
||||
TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
|
||||
int64_t bucketStartTimeNs = 10000000000;
|
||||
int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
|
||||
@@ -48,7 +50,7 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
|
||||
|
||||
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
|
||||
|
||||
CountMetricProducer countProducer(metric, -1 /*-1 meaning no condition*/, wizard,
|
||||
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
|
||||
bucketStartTimeNs);
|
||||
|
||||
// 2 events in bucket 1.
|
||||
@@ -106,7 +108,7 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
|
||||
|
||||
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
|
||||
|
||||
CountMetricProducer countProducer(metric, 1, wizard, bucketStartTimeNs);
|
||||
CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
|
||||
|
||||
countProducer.onConditionChanged(true, bucketStartTimeNs);
|
||||
countProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
|
||||
@@ -161,7 +163,7 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
|
||||
|
||||
EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
|
||||
|
||||
CountMetricProducer countProducer(metric, 1 /*condition tracker index*/, wizard,
|
||||
CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
|
||||
bucketStartTimeNs);
|
||||
|
||||
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
|
||||
@@ -201,7 +203,7 @@ TEST(CountMetricProducerTest, TestAnomalyDetection) {
|
||||
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
|
||||
|
||||
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
|
||||
CountMetricProducer countProducer(metric, -1 /*-1 meaning no condition*/, wizard,
|
||||
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
|
||||
bucketStartTimeNs);
|
||||
countProducer.addAnomalyTracker(anomalyTracker);
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
const ConfigKey kConfigKey(0, "test");
|
||||
|
||||
TEST(EventMetricProducerTest, TestNoCondition) {
|
||||
uint64_t bucketStartTimeNs = 10000000000;
|
||||
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
|
||||
@@ -45,7 +47,7 @@ TEST(EventMetricProducerTest, TestNoCondition) {
|
||||
|
||||
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
|
||||
|
||||
EventMetricProducer eventProducer(metric, -1 /*-1 meaning no condition*/, wizard,
|
||||
EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
|
||||
bucketStartTimeNs);
|
||||
|
||||
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
|
||||
@@ -69,7 +71,7 @@ TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) {
|
||||
|
||||
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
|
||||
|
||||
EventMetricProducer eventProducer(metric, 1, wizard, bucketStartTimeNs);
|
||||
EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
|
||||
|
||||
eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs);
|
||||
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
|
||||
@@ -111,7 +113,7 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) {
|
||||
|
||||
EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
|
||||
|
||||
EventMetricProducer eventProducer(metric, 1, wizard, bucketStartTimeNs);
|
||||
EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
|
||||
|
||||
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
|
||||
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
|
||||
|
||||
@@ -36,6 +36,8 @@ namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
const ConfigKey kConfigKey(0, "test");
|
||||
|
||||
TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
|
||||
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
|
||||
|
||||
@@ -45,8 +47,8 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
|
||||
uint64_t bucketStartTimeNs = 10000000000;
|
||||
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
|
||||
|
||||
MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
|
||||
buckets);
|
||||
MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
|
||||
bucketSizeNs, {}, buckets);
|
||||
|
||||
tracker.noteStart("1", true, bucketStartTimeNs, key1);
|
||||
// Event starts again. This would not change anything as it already starts.
|
||||
@@ -72,8 +74,8 @@ TEST(MaxDurationTrackerTest, TestStopAll) {
|
||||
uint64_t bucketStartTimeNs = 10000000000;
|
||||
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
|
||||
|
||||
MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
|
||||
buckets);
|
||||
MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
|
||||
bucketSizeNs, {}, buckets);
|
||||
|
||||
tracker.noteStart("1", true, bucketStartTimeNs + 1, key1);
|
||||
|
||||
@@ -100,8 +102,8 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
|
||||
uint64_t bucketStartTimeNs = 10000000000;
|
||||
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
|
||||
|
||||
MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
|
||||
buckets);
|
||||
MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
|
||||
bucketSizeNs, {}, buckets);
|
||||
|
||||
// The event starts.
|
||||
tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
|
||||
@@ -127,8 +129,8 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
|
||||
uint64_t bucketStartTimeNs = 10000000000;
|
||||
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
|
||||
|
||||
MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs, {},
|
||||
buckets);
|
||||
MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs,
|
||||
bucketSizeNs, {}, buckets);
|
||||
|
||||
// 2 starts
|
||||
tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
|
||||
@@ -168,8 +170,8 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
|
||||
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
|
||||
int64_t durationTimeNs = 2 * 1000;
|
||||
|
||||
MaxDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
|
||||
buckets);
|
||||
MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
|
||||
bucketSizeNs, {}, buckets);
|
||||
EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
|
||||
|
||||
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
|
||||
@@ -200,8 +202,8 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
|
||||
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
|
||||
|
||||
sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
|
||||
MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs,
|
||||
{anomalyTracker}, buckets);
|
||||
MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs,
|
||||
bucketSizeNs, {anomalyTracker}, buckets);
|
||||
|
||||
tracker.noteStart("1", true, eventStartTimeNs, key1);
|
||||
tracker.noteStop("1", eventStartTimeNs + 10, false);
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
const ConfigKey kConfigKey(0, "test");
|
||||
|
||||
TEST(OringDurationTrackerTest, TestDurationOverlap) {
|
||||
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
|
||||
|
||||
@@ -47,8 +49,8 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) {
|
||||
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
|
||||
uint64_t durationTimeNs = 2 * 1000;
|
||||
|
||||
OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
|
||||
buckets);
|
||||
OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
|
||||
bucketSizeNs, {}, buckets);
|
||||
|
||||
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
|
||||
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
|
||||
@@ -73,8 +75,8 @@ TEST(OringDurationTrackerTest, TestDurationNested) {
|
||||
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
|
||||
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
|
||||
|
||||
OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
|
||||
buckets);
|
||||
OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
|
||||
bucketSizeNs, {}, buckets);
|
||||
|
||||
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
|
||||
tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1); // overlapping wl
|
||||
@@ -99,8 +101,8 @@ TEST(OringDurationTrackerTest, TestStopAll) {
|
||||
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
|
||||
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
|
||||
|
||||
OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
|
||||
buckets);
|
||||
OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
|
||||
bucketSizeNs, {}, buckets);
|
||||
|
||||
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
|
||||
tracker.noteStart("3:maps", true, eventStartTimeNs + 10, key1); // overlapping wl
|
||||
@@ -125,8 +127,8 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
|
||||
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
|
||||
uint64_t durationTimeNs = 2 * 1000;
|
||||
|
||||
OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
|
||||
buckets);
|
||||
OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
|
||||
bucketSizeNs, {}, buckets);
|
||||
|
||||
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
|
||||
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
|
||||
@@ -162,8 +164,8 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) {
|
||||
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
|
||||
uint64_t durationTimeNs = 2 * 1000;
|
||||
|
||||
OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
|
||||
buckets);
|
||||
OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
|
||||
bucketSizeNs, {}, buckets);
|
||||
|
||||
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
|
||||
|
||||
@@ -194,8 +196,8 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
|
||||
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
|
||||
uint64_t durationTimeNs = 2 * 1000;
|
||||
|
||||
OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
|
||||
buckets);
|
||||
OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
|
||||
bucketSizeNs, {}, buckets);
|
||||
|
||||
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
|
||||
// condition to false; record duration 5n
|
||||
@@ -225,8 +227,8 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
|
||||
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
|
||||
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
|
||||
|
||||
OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
|
||||
buckets);
|
||||
OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
|
||||
bucketSizeNs, {}, buckets);
|
||||
|
||||
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
|
||||
tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1);
|
||||
@@ -259,8 +261,8 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
|
||||
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
|
||||
|
||||
sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
|
||||
OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs,
|
||||
{anomalyTracker}, buckets);
|
||||
OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
|
||||
bucketSizeNs, {anomalyTracker}, buckets);
|
||||
|
||||
// Nothing in the past bucket.
|
||||
tracker.noteStart("", true, eventStartTimeNs, key1);
|
||||
@@ -319,8 +321,8 @@ TEST(OringDurationTrackerTest, TestAnomalyDetection) {
|
||||
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
|
||||
|
||||
sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
|
||||
OringDurationTracker tracker("event", wizard, 1, true /*nesting*/, bucketStartTimeNs,
|
||||
bucketSizeNs, {anomalyTracker}, buckets);
|
||||
OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true /*nesting*/,
|
||||
bucketStartTimeNs, bucketSizeNs, {anomalyTracker}, buckets);
|
||||
|
||||
tracker.noteStart("", true, eventStartTimeNs, key1);
|
||||
tracker.noteStop("", eventStartTimeNs + 10, false);
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "metrics_test_helper.h"
|
||||
#include "src/metrics/ValueMetricProducer.h"
|
||||
#include "metrics_test_helper.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
@@ -22,11 +22,11 @@
|
||||
|
||||
using namespace testing;
|
||||
using android::sp;
|
||||
using std::make_shared;
|
||||
using std::set;
|
||||
using std::shared_ptr;
|
||||
using std::unordered_map;
|
||||
using std::vector;
|
||||
using std::shared_ptr;
|
||||
using std::make_shared;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
const ConfigKey kConfigKey(0, "test");
|
||||
/*
|
||||
* Tests pulled atoms with no conditions
|
||||
*/
|
||||
@@ -42,7 +43,7 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
|
||||
int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
|
||||
|
||||
int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
|
||||
int64_t bucket3StartTimeNs = bucketStartTimeNs + 2*bucketSizeNs;
|
||||
int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
|
||||
|
||||
ValueMetric metric;
|
||||
metric.set_name("1");
|
||||
@@ -54,12 +55,13 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
|
||||
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
|
||||
// TODO: pending refactor of StatsPullerManager
|
||||
// For now we still need this so that it doesn't do real pulling.
|
||||
shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
|
||||
shared_ptr<MockStatsPullerManager> pullerManager =
|
||||
make_shared<StrictMock<MockStatsPullerManager>>();
|
||||
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
|
||||
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
|
||||
|
||||
ValueMetricProducer valueProducer(metric, -1 /*-1 meaning no condition*/, wizard,tagId,
|
||||
bucketStartTimeNs, pullerManager);
|
||||
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
|
||||
tagId, bucketStartTimeNs, pullerManager);
|
||||
|
||||
vector<shared_ptr<LogEvent>> allData;
|
||||
allData.clear();
|
||||
@@ -144,41 +146,43 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
|
||||
int tagId = 1;
|
||||
|
||||
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
|
||||
shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
|
||||
shared_ptr<MockStatsPullerManager> pullerManager =
|
||||
make_shared<StrictMock<MockStatsPullerManager>>();
|
||||
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
|
||||
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
|
||||
|
||||
EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Invoke([] (int tagId, vector<std::shared_ptr<LogEvent>>* data) {
|
||||
int64_t bucketStartTimeNs = 10000000000;
|
||||
int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
|
||||
EXPECT_CALL(*pullerManager, Pull(tagId, _))
|
||||
.WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
|
||||
int64_t bucketStartTimeNs = 10000000000;
|
||||
int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
|
||||
|
||||
int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
|
||||
int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
|
||||
data->clear();
|
||||
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
|
||||
event->write(1);
|
||||
event->write(100);
|
||||
event->init();
|
||||
data->push_back(event);
|
||||
return true;
|
||||
}))
|
||||
.WillOnce(Invoke([] (int tagId, vector<std::shared_ptr<LogEvent>>* data) {
|
||||
int64_t bucketStartTimeNs = 10000000000;
|
||||
int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
|
||||
int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
|
||||
int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
|
||||
data->clear();
|
||||
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
|
||||
event->write(1);
|
||||
event->write(100);
|
||||
event->init();
|
||||
data->push_back(event);
|
||||
return true;
|
||||
}))
|
||||
.WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
|
||||
int64_t bucketStartTimeNs = 10000000000;
|
||||
int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
|
||||
|
||||
int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
|
||||
int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
|
||||
data->clear();
|
||||
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
|
||||
event->write(1);
|
||||
event->write(120);
|
||||
event->init();
|
||||
data->push_back(event);
|
||||
return true;
|
||||
}));
|
||||
int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
|
||||
int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
|
||||
data->clear();
|
||||
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
|
||||
event->write(1);
|
||||
event->write(120);
|
||||
event->init();
|
||||
data->push_back(event);
|
||||
return true;
|
||||
}));
|
||||
|
||||
ValueMetricProducer valueProducer(metric, 1, wizard,tagId,
|
||||
bucketStartTimeNs, pullerManager);
|
||||
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
|
||||
pullerManager);
|
||||
|
||||
valueProducer.onConditionChanged(true, bucketStartTimeNs + 10);
|
||||
|
||||
@@ -241,10 +245,11 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
|
||||
int tagId = 1;
|
||||
|
||||
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
|
||||
shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
|
||||
shared_ptr<MockStatsPullerManager> pullerManager =
|
||||
make_shared<StrictMock<MockStatsPullerManager>>();
|
||||
|
||||
ValueMetricProducer valueProducer(metric, -1, wizard,-1,
|
||||
bucketStartTimeNs, pullerManager);
|
||||
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
|
||||
pullerManager);
|
||||
|
||||
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
|
||||
event1->write(1);
|
||||
|
||||
@@ -18,6 +18,10 @@ using namespace std;
|
||||
namespace android {
|
||||
namespace stats_log_api_gen {
|
||||
|
||||
const int PULL_ATOM_START_ID = 1000;
|
||||
|
||||
int maxPushedAtomId = 2;
|
||||
|
||||
using android::os::statsd::Atom;
|
||||
|
||||
// TODO: Support WorkSources
|
||||
@@ -195,12 +199,18 @@ write_stats_log_header(FILE* out, const Atoms& atoms)
|
||||
fprintf(out, " */\n");
|
||||
char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
|
||||
fprintf(out, " %s = %d%s\n", constant.c_str(), atom->code, comma);
|
||||
if (atom->code < PULL_ATOM_START_ID && atom->code > maxPushedAtomId) {
|
||||
maxPushedAtomId = atom->code;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, "};\n");
|
||||
fprintf(out, "\n");
|
||||
|
||||
fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n",
|
||||
maxPushedAtomId);
|
||||
|
||||
// Print write methods
|
||||
fprintf(out, "//\n");
|
||||
fprintf(out, "// Write methods\n");
|
||||
|
||||
Reference in New Issue
Block a user