Merge "Persist active metric status across system server" into qt-dev am: 7ee86cdb65
am: ba7ec1deab
Change-Id: I26645d03246862d11f00585f206a7ae4a4ab0287
This commit is contained in:
@@ -611,11 +611,8 @@ void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key,
|
||||
|
||||
void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) {
|
||||
std::lock_guard<std::mutex> lock(mMetricsMutex);
|
||||
|
||||
const int64_t timeNs = getElapsedRealtimeNs();
|
||||
// Do not write to disk if we already have in the last few seconds.
|
||||
// This is to avoid overwriting files that would have the same name if we
|
||||
// write twice in the same second.
|
||||
if (static_cast<unsigned long long> (timeNs) <
|
||||
mLastActiveMetricsWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) {
|
||||
ALOGI("Statsd skipping writing active metrics to disk. Already wrote data in last %d seconds",
|
||||
@@ -625,13 +622,7 @@ void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) {
|
||||
mLastActiveMetricsWriteNs = timeNs;
|
||||
|
||||
ProtoOutputStream proto;
|
||||
for (const auto& pair : mMetricsManagers) {
|
||||
const sp<MetricsManager>& metricsManager = pair.second;
|
||||
uint64_t configToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
|
||||
FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG);
|
||||
metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, &proto);
|
||||
proto.end(configToken);
|
||||
}
|
||||
WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, DEVICE_SHUTDOWN, &proto);
|
||||
|
||||
string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR);
|
||||
StorageManager::deleteFile(file_name.c_str());
|
||||
@@ -644,9 +635,24 @@ void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) {
|
||||
proto.flush(fd.get());
|
||||
}
|
||||
|
||||
void StatsLogProcessor::WriteActiveConfigsToProtoOutputStream(
|
||||
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
|
||||
std::lock_guard<std::mutex> lock(mMetricsMutex);
|
||||
WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, reason, proto);
|
||||
}
|
||||
|
||||
void StatsLogProcessor::WriteActiveConfigsToProtoOutputStreamLocked(
|
||||
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
|
||||
for (const auto& pair : mMetricsManagers) {
|
||||
const sp<MetricsManager>& metricsManager = pair.second;
|
||||
uint64_t configToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
|
||||
FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG);
|
||||
metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, reason, proto);
|
||||
proto->end(configToken);
|
||||
}
|
||||
}
|
||||
void StatsLogProcessor::LoadActiveConfigsFromDisk() {
|
||||
std::lock_guard<std::mutex> lock(mMetricsMutex);
|
||||
|
||||
string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR);
|
||||
int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (-1 == fd) {
|
||||
@@ -670,6 +676,19 @@ void StatsLogProcessor::LoadActiveConfigsFromDisk() {
|
||||
StorageManager::deleteFile(file_name.c_str());
|
||||
return;
|
||||
}
|
||||
// Passing in mTimeBaseNs only works as long as we only load from disk is when statsd starts.
|
||||
SetConfigsActiveStateLocked(activeConfigList, mTimeBaseNs);
|
||||
StorageManager::deleteFile(file_name.c_str());
|
||||
}
|
||||
|
||||
void StatsLogProcessor::SetConfigsActiveState(const ActiveConfigList& activeConfigList,
|
||||
int64_t currentTimeNs) {
|
||||
std::lock_guard<std::mutex> lock(mMetricsMutex);
|
||||
SetConfigsActiveStateLocked(activeConfigList, currentTimeNs);
|
||||
}
|
||||
|
||||
void StatsLogProcessor::SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList,
|
||||
int64_t currentTimeNs) {
|
||||
for (int i = 0; i < activeConfigList.config_size(); i++) {
|
||||
const auto& config = activeConfigList.config(i);
|
||||
ConfigKey key(config.uid(), config.id());
|
||||
@@ -679,11 +698,9 @@ void StatsLogProcessor::LoadActiveConfigsFromDisk() {
|
||||
continue;
|
||||
}
|
||||
VLOG("Setting active config %s", key.ToString().c_str());
|
||||
it->second->loadActiveConfig(config, mTimeBaseNs);
|
||||
it->second->loadActiveConfig(config, currentTimeNs);
|
||||
}
|
||||
VLOG("Successfully loaded %d active configs.", activeConfigList.config_size());
|
||||
|
||||
StorageManager::deleteFile(file_name.c_str());
|
||||
}
|
||||
|
||||
void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason,
|
||||
|
||||
@@ -31,17 +31,6 @@ namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
// Keep this in sync with DumpReportReason enum in stats_log.proto
|
||||
enum DumpReportReason {
|
||||
DEVICE_SHUTDOWN = 1,
|
||||
CONFIG_UPDATED = 2,
|
||||
CONFIG_REMOVED = 3,
|
||||
GET_DATA_CALLED = 4,
|
||||
ADB_DUMP = 5,
|
||||
CONFIG_RESET = 6,
|
||||
STATSCOMPANION_DIED = 7,
|
||||
TERMINATION_SIGNAL_RECEIVED = 8
|
||||
};
|
||||
|
||||
class StatsLogProcessor : public ConfigListener {
|
||||
public:
|
||||
@@ -92,9 +81,16 @@ public:
|
||||
/* Persist configs containing metrics with active activations to disk. */
|
||||
void SaveActiveConfigsToDisk(int64_t currentTimeNs);
|
||||
|
||||
/* Writes the current active status/ttl for all configs and metrics to ProtoOutputStream. */
|
||||
void WriteActiveConfigsToProtoOutputStream(
|
||||
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
|
||||
|
||||
/* Load configs containing metrics with active activations from disk. */
|
||||
void LoadActiveConfigsFromDisk();
|
||||
|
||||
/* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */
|
||||
void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs);
|
||||
|
||||
// Reset all configs.
|
||||
void resetConfigs();
|
||||
|
||||
@@ -158,6 +154,12 @@ private:
|
||||
|
||||
void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs);
|
||||
|
||||
void WriteActiveConfigsToProtoOutputStreamLocked(
|
||||
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
|
||||
|
||||
void SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList,
|
||||
int64_t currentTimeNs);
|
||||
|
||||
void WriteDataToDiskLocked(const DumpReportReason dumpReportReason,
|
||||
const DumpLatency dumpLatency);
|
||||
void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs,
|
||||
@@ -224,6 +226,7 @@ private:
|
||||
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
|
||||
FRIEND_TEST(StatsLogProcessorTest,
|
||||
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
|
||||
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
|
||||
|
||||
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1);
|
||||
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2);
|
||||
|
||||
@@ -1478,8 +1478,21 @@ void StatsService::binderDied(const wp <IBinder>& who) {
|
||||
StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
|
||||
if (mProcessor != nullptr) {
|
||||
ALOGW("Reset statsd upon system server restarts.");
|
||||
int64_t systemServerRestartNs = getElapsedRealtimeNs();
|
||||
ProtoOutputStream proto;
|
||||
mProcessor->WriteActiveConfigsToProtoOutputStream(systemServerRestartNs,
|
||||
STATSCOMPANION_DIED, &proto);
|
||||
|
||||
mProcessor->WriteDataToDisk(STATSCOMPANION_DIED, FAST);
|
||||
mProcessor->resetConfigs();
|
||||
|
||||
std::string serializedActiveConfigs;
|
||||
if (proto.serializeToString(&serializedActiveConfigs)) {
|
||||
ActiveConfigList activeConfigs;
|
||||
if (activeConfigs.ParseFromString(serializedActiveConfigs)) {
|
||||
mProcessor->SetConfigsActiveState(activeConfigs, systemServerRestartNs);
|
||||
}
|
||||
}
|
||||
}
|
||||
mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
|
||||
mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
|
||||
|
||||
@@ -427,6 +427,7 @@ private:
|
||||
|
||||
std::shared_ptr<LogEventQueue> mEventQueue;
|
||||
|
||||
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
|
||||
FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
|
||||
FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
|
||||
FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
|
||||
|
||||
@@ -26,7 +26,18 @@ message ActiveEventActivation {
|
||||
|
||||
// Time left in activation. When this proto is loaded after device boot,
|
||||
// the activation should be set to active for this duration.
|
||||
// This field will only be set when the state is ACTIVE
|
||||
optional int64 remaining_ttl_nanos = 2;
|
||||
|
||||
enum State {
|
||||
UNNKNOWN = 0;
|
||||
// This metric should activate for remaining_ttl_nanos when we load the activations.
|
||||
ACTIVE = 1;
|
||||
// When we load the activations, this metric should activate on next boot for the tll
|
||||
// specified in the config.
|
||||
ACTIVATE_ON_BOOT = 2;
|
||||
}
|
||||
optional State state = 3;
|
||||
}
|
||||
|
||||
message ActiveMetric {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "MetricProducer.h"
|
||||
|
||||
using android::util::FIELD_COUNT_REPEATED;
|
||||
using android::util::FIELD_TYPE_ENUM;
|
||||
using android::util::FIELD_TYPE_INT32;
|
||||
using android::util::FIELD_TYPE_INT64;
|
||||
using android::util::FIELD_TYPE_MESSAGE;
|
||||
@@ -37,6 +38,7 @@ const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2;
|
||||
// for ActiveEventActivation
|
||||
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1;
|
||||
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
|
||||
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
|
||||
|
||||
void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
|
||||
if (!mIsActive) {
|
||||
@@ -165,17 +167,21 @@ void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric,
|
||||
continue;
|
||||
}
|
||||
auto& activation = it->second;
|
||||
// We don't want to change the ttl for future activations, so we set the start_ns
|
||||
// such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
|
||||
activation->start_ns =
|
||||
currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
|
||||
activation->state = ActivationState::kActive;
|
||||
mIsActive = true;
|
||||
if (activeEventActivation.state() == ActiveEventActivation::ACTIVE) {
|
||||
// We don't want to change the ttl for future activations, so we set the start_ns
|
||||
// such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
|
||||
activation->start_ns =
|
||||
currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
|
||||
activation->state = ActivationState::kActive;
|
||||
mIsActive = true;
|
||||
} else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) {
|
||||
activation->state = ActivationState::kActiveOnBoot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetricProducer::writeActiveMetricToProtoOutputStream(
|
||||
int64_t currentTimeNs, ProtoOutputStream* proto) {
|
||||
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
|
||||
proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId);
|
||||
for (auto& it : mEventActivationMap) {
|
||||
const int atom_matcher_index = it.first;
|
||||
@@ -196,9 +202,22 @@ void MetricProducer::writeActiveMetricToProtoOutputStream(
|
||||
activation->start_ns + activation->ttl_ns - currentTimeNs;
|
||||
proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
|
||||
(long long)remainingTtlNs);
|
||||
proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
|
||||
ActiveEventActivation::ACTIVE);
|
||||
|
||||
} else if (ActivationState::kActiveOnBoot == activation->state) {
|
||||
proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
|
||||
(long long)activation->ttl_ns);
|
||||
if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) {
|
||||
proto->write(
|
||||
FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
|
||||
(long long)activation->ttl_ns);
|
||||
proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
|
||||
ActiveEventActivation::ACTIVE);
|
||||
} else if (reason == STATSCOMPANION_DIED) {
|
||||
// We are saving because of system server death, not due to a device shutdown.
|
||||
// Next time we load, we do not want to activate metrics that activate on boot.
|
||||
proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
|
||||
ActiveEventActivation::ACTIVATE_ON_BOOT);
|
||||
}
|
||||
}
|
||||
proto->end(activationToken);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,18 @@ namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
// Keep this in sync with DumpReportReason enum in stats_log.proto
|
||||
enum DumpReportReason {
|
||||
DEVICE_SHUTDOWN = 1,
|
||||
CONFIG_UPDATED = 2,
|
||||
CONFIG_REMOVED = 3,
|
||||
GET_DATA_CALLED = 4,
|
||||
ADB_DUMP = 5,
|
||||
CONFIG_RESET = 6,
|
||||
STATSCOMPANION_DIED = 7,
|
||||
TERMINATION_SIGNAL_RECEIVED = 8
|
||||
};
|
||||
|
||||
// If the metric has no activation requirement, it will be active once the metric producer is
|
||||
// created.
|
||||
// If the metric needs to be activated by atoms, the metric producer will start
|
||||
@@ -244,7 +256,7 @@ public:
|
||||
void flushIfExpire(int64_t elapsedTimestampNs);
|
||||
|
||||
void writeActiveMetricToProtoOutputStream(
|
||||
int64_t currentTimeNs, ProtoOutputStream* proto);
|
||||
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
|
||||
protected:
|
||||
virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
|
||||
virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
|
||||
@@ -268,8 +280,6 @@ protected:
|
||||
return mIsActive;
|
||||
}
|
||||
|
||||
void prepActiveForBootIfNecessaryLocked(int64_t currentTimeNs);
|
||||
|
||||
void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
|
||||
|
||||
virtual void prepareFirstBucketLocked() {};
|
||||
@@ -412,6 +422,7 @@ protected:
|
||||
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
|
||||
FRIEND_TEST(StatsLogProcessorTest,
|
||||
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
|
||||
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
|
||||
};
|
||||
|
||||
} // namespace statsd
|
||||
|
||||
@@ -528,14 +528,14 @@ void MetricsManager::loadActiveConfig(const ActiveConfig& config, int64_t curren
|
||||
}
|
||||
|
||||
void MetricsManager::writeActiveConfigToProtoOutputStream(
|
||||
int64_t currentTimeNs, ProtoOutputStream* proto) {
|
||||
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
|
||||
proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId());
|
||||
proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid());
|
||||
for (int metricIndex : mMetricIndexesWithActivation) {
|
||||
const auto& metric = mAllMetricProducers[metricIndex];
|
||||
const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
|
||||
FIELD_ID_ACTIVE_CONFIG_METRIC);
|
||||
metric->writeActiveMetricToProtoOutputStream(currentTimeNs, proto);
|
||||
metric->writeActiveMetricToProtoOutputStream(currentTimeNs, reason, proto);
|
||||
proto->end(metricToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ public:
|
||||
void loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs);
|
||||
|
||||
void writeActiveConfigToProtoOutputStream(
|
||||
int64_t currentTimeNs, ProtoOutputStream* proto);
|
||||
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
|
||||
|
||||
private:
|
||||
// For test only.
|
||||
@@ -290,6 +290,7 @@ private:
|
||||
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
|
||||
FRIEND_TEST(StatsLogProcessorTest,
|
||||
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
|
||||
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
|
||||
};
|
||||
|
||||
} // namespace statsd
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "StatsLogProcessor.h"
|
||||
#include "StatsService.h"
|
||||
#include "config/ConfigKey.h"
|
||||
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
|
||||
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
|
||||
#include "guardrail/StatsdStats.h"
|
||||
#include "logd/LogEvent.h"
|
||||
#include "packages/UidMap.h"
|
||||
#include "storage/StorageManager.h"
|
||||
#include "statslog.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
@@ -97,7 +99,8 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) {
|
||||
ConfigKey key(100, 12345);
|
||||
EXPECT_CALL(mockMetricsManager, byteSize())
|
||||
.Times(1)
|
||||
.WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95)));
|
||||
.WillRepeatedly(::testing::Return(int(
|
||||
StatsdStats::kMaxMetricsBytesPerConfig * .95)));
|
||||
|
||||
// Expect only one broadcast despite always returning a size that should trigger broadcast.
|
||||
p.flushIfNecessaryLocked(1, key, mockMetricsManager);
|
||||
@@ -128,7 +131,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) {
|
||||
ConfigKey key(100, 12345);
|
||||
EXPECT_CALL(mockMetricsManager, byteSize())
|
||||
.Times(1)
|
||||
.WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2)));
|
||||
.WillRepeatedly(::testing::Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2)));
|
||||
|
||||
EXPECT_CALL(mockMetricsManager, dropData(_)).Times(1);
|
||||
|
||||
@@ -1482,6 +1485,236 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
|
||||
// }}}---------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) {
|
||||
int uid = 9876;
|
||||
long configId = 12341;
|
||||
|
||||
// Create config with 3 metrics:
|
||||
// Metric 1: Activate on 2 activations, 1 on boot, 1 immediate.
|
||||
// Metric 2: Activate on 2 activations, 1 on boot, 1 immediate.
|
||||
// Metric 3: Always active
|
||||
StatsdConfig config1;
|
||||
config1.set_id(configId);
|
||||
config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
|
||||
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
|
||||
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
|
||||
auto jobStartMatcher = CreateStartScheduledJobAtomMatcher();
|
||||
auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher();
|
||||
*config1.add_atom_matcher() = wakelockAcquireMatcher;
|
||||
*config1.add_atom_matcher() = screenOnMatcher;
|
||||
*config1.add_atom_matcher() = jobStartMatcher;
|
||||
*config1.add_atom_matcher() = jobFinishMatcher;
|
||||
|
||||
long metricId1 = 1234561;
|
||||
long metricId2 = 1234562;
|
||||
long metricId3 = 1234563;
|
||||
|
||||
auto countMetric1 = config1.add_count_metric();
|
||||
countMetric1->set_id(metricId1);
|
||||
countMetric1->set_what(wakelockAcquireMatcher.id());
|
||||
countMetric1->set_bucket(FIVE_MINUTES);
|
||||
|
||||
auto countMetric2 = config1.add_count_metric();
|
||||
countMetric2->set_id(metricId2);
|
||||
countMetric2->set_what(wakelockAcquireMatcher.id());
|
||||
countMetric2->set_bucket(FIVE_MINUTES);
|
||||
|
||||
auto countMetric3 = config1.add_count_metric();
|
||||
countMetric3->set_id(metricId3);
|
||||
countMetric3->set_what(wakelockAcquireMatcher.id());
|
||||
countMetric3->set_bucket(FIVE_MINUTES);
|
||||
|
||||
// Metric 1 activates on boot for wakelock acquire, immediately for screen on.
|
||||
auto metric1Activation = config1.add_metric_activation();
|
||||
metric1Activation->set_metric_id(metricId1);
|
||||
auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
|
||||
metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
|
||||
metric1ActivationTrigger1->set_ttl_seconds(100);
|
||||
metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
|
||||
auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
|
||||
metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
|
||||
metric1ActivationTrigger2->set_ttl_seconds(200);
|
||||
metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
|
||||
|
||||
// Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish.
|
||||
auto metric2Activation = config1.add_metric_activation();
|
||||
metric2Activation->set_metric_id(metricId2);
|
||||
auto metric2ActivationTrigger1 = metric2Activation->add_event_activation();
|
||||
metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id());
|
||||
metric2ActivationTrigger1->set_ttl_seconds(100);
|
||||
metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
|
||||
auto metric2ActivationTrigger2 = metric2Activation->add_event_activation();
|
||||
metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id());
|
||||
metric2ActivationTrigger2->set_ttl_seconds(200);
|
||||
metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
|
||||
|
||||
// Send the config.
|
||||
StatsService service(nullptr, nullptr);
|
||||
string serialized = config1.SerializeAsString();
|
||||
service.addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
|
||||
|
||||
// Make sure the config is stored on disk. Otherwise, we will not reset on system server death.
|
||||
StatsdConfig tmpConfig;
|
||||
ConfigKey cfgKey1(uid, configId);
|
||||
EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig));
|
||||
|
||||
// Metric 1 is not active.
|
||||
// Metric 2 is not active.
|
||||
// Metric 3 is active.
|
||||
// {{{---------------------------------------------------------------------------
|
||||
sp<StatsLogProcessor> processor = service.mProcessor;
|
||||
EXPECT_EQ(1, processor->mMetricsManagers.size());
|
||||
auto it = processor->mMetricsManagers.find(cfgKey1);
|
||||
EXPECT_TRUE(it != processor->mMetricsManagers.end());
|
||||
auto& metricsManager1 = it->second;
|
||||
EXPECT_TRUE(metricsManager1->isActive());
|
||||
EXPECT_EQ(3, metricsManager1->mAllMetricProducers.size());
|
||||
|
||||
auto& metricProducer1 = metricsManager1->mAllMetricProducers[0];
|
||||
EXPECT_EQ(metricId1, metricProducer1->getMetricId());
|
||||
EXPECT_FALSE(metricProducer1->isActive());
|
||||
|
||||
auto& metricProducer2 = metricsManager1->mAllMetricProducers[1];
|
||||
EXPECT_EQ(metricId2, metricProducer2->getMetricId());
|
||||
EXPECT_FALSE(metricProducer2->isActive());
|
||||
|
||||
auto& metricProducer3 = metricsManager1->mAllMetricProducers[2];
|
||||
EXPECT_EQ(metricId3, metricProducer3->getMetricId());
|
||||
EXPECT_TRUE(metricProducer3->isActive());
|
||||
|
||||
// Check event activations.
|
||||
EXPECT_EQ(metricsManager1->mAllAtomMatchers.size(), 4);
|
||||
EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(),
|
||||
metric1ActivationTrigger1->atom_matcher_id());
|
||||
const auto& activation1 = metricProducer1->mEventActivationMap.at(0);
|
||||
EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
|
||||
EXPECT_EQ(0, activation1->start_ns);
|
||||
EXPECT_EQ(kNotActive, activation1->state);
|
||||
EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
|
||||
|
||||
EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(),
|
||||
metric1ActivationTrigger2->atom_matcher_id());
|
||||
const auto& activation2 = metricProducer1->mEventActivationMap.at(1);
|
||||
EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
|
||||
EXPECT_EQ(0, activation2->start_ns);
|
||||
EXPECT_EQ(kNotActive, activation2->state);
|
||||
EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
|
||||
|
||||
EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(),
|
||||
metric2ActivationTrigger1->atom_matcher_id());
|
||||
const auto& activation3 = metricProducer2->mEventActivationMap.at(2);
|
||||
EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns);
|
||||
EXPECT_EQ(0, activation3->start_ns);
|
||||
EXPECT_EQ(kNotActive, activation3->state);
|
||||
EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType);
|
||||
|
||||
EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(),
|
||||
metric2ActivationTrigger2->atom_matcher_id());
|
||||
const auto& activation4 = metricProducer2->mEventActivationMap.at(3);
|
||||
EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns);
|
||||
EXPECT_EQ(0, activation4->start_ns);
|
||||
EXPECT_EQ(kNotActive, activation4->state);
|
||||
EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType);
|
||||
// }}}------------------------------------------------------------------------------
|
||||
|
||||
// Trigger Activation 1 for Metric 1. Should activate on boot.
|
||||
// Trigger Activation 4 for Metric 2. Should activate immediately.
|
||||
long configAddedTimeNs = metricsManager1->mLastReportTimeNs;
|
||||
std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
|
||||
auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 1 + configAddedTimeNs);
|
||||
processor->OnLogEvent(event.get());
|
||||
|
||||
event = CreateFinishScheduledJobEvent(attributions1, "finish1", 2 + configAddedTimeNs);
|
||||
processor->OnLogEvent(event.get());
|
||||
|
||||
// Metric 1 is not active; Activation 1 set to kActiveOnBoot
|
||||
// Metric 2 is active. Activation 4 set to kActive
|
||||
// Metric 3 is active.
|
||||
// {{{---------------------------------------------------------------------------
|
||||
EXPECT_FALSE(metricProducer1->isActive());
|
||||
EXPECT_EQ(0, activation1->start_ns);
|
||||
EXPECT_EQ(kActiveOnBoot, activation1->state);
|
||||
EXPECT_EQ(0, activation2->start_ns);
|
||||
EXPECT_EQ(kNotActive, activation2->state);
|
||||
|
||||
EXPECT_TRUE(metricProducer2->isActive());
|
||||
EXPECT_EQ(0, activation3->start_ns);
|
||||
EXPECT_EQ(kNotActive, activation3->state);
|
||||
EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns);
|
||||
EXPECT_EQ(kActive, activation4->state);
|
||||
|
||||
EXPECT_TRUE(metricProducer3->isActive());
|
||||
// }}}-----------------------------------------------------------------------------
|
||||
|
||||
// Can't fake time with StatsService.
|
||||
// Lets get a time close to the system server death time and make sure it's sane.
|
||||
int64_t approximateSystemServerDeath = getElapsedRealtimeNs();
|
||||
EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs);
|
||||
EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs);
|
||||
|
||||
// System server dies.
|
||||
service.binderDied(nullptr);
|
||||
|
||||
// We should have a new metrics manager. Lets get it and ensure activation status is restored.
|
||||
// {{{---------------------------------------------------------------------------
|
||||
EXPECT_EQ(1, processor->mMetricsManagers.size());
|
||||
it = processor->mMetricsManagers.find(cfgKey1);
|
||||
EXPECT_TRUE(it != processor->mMetricsManagers.end());
|
||||
auto& metricsManager2 = it->second;
|
||||
EXPECT_TRUE(metricsManager2->isActive());
|
||||
EXPECT_EQ(3, metricsManager2->mAllMetricProducers.size());
|
||||
|
||||
auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0];
|
||||
EXPECT_EQ(metricId1, metricProducer1001->getMetricId());
|
||||
EXPECT_FALSE(metricProducer1001->isActive());
|
||||
|
||||
auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1];
|
||||
EXPECT_EQ(metricId2, metricProducer1002->getMetricId());
|
||||
EXPECT_TRUE(metricProducer1002->isActive());
|
||||
|
||||
auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2];
|
||||
EXPECT_EQ(metricId3, metricProducer1003->getMetricId());
|
||||
EXPECT_TRUE(metricProducer1003->isActive());
|
||||
|
||||
// Check event activations.
|
||||
// Activation 1 is kActiveOnBoot.
|
||||
// Activation 2 and 3 are not active.
|
||||
// Activation 4 is active.
|
||||
EXPECT_EQ(metricsManager2->mAllAtomMatchers.size(), 4);
|
||||
EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(),
|
||||
metric1ActivationTrigger1->atom_matcher_id());
|
||||
const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0);
|
||||
EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
|
||||
EXPECT_EQ(0, activation1001->start_ns);
|
||||
EXPECT_EQ(kActiveOnBoot, activation1001->state);
|
||||
EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType);
|
||||
|
||||
EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(),
|
||||
metric1ActivationTrigger2->atom_matcher_id());
|
||||
const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1);
|
||||
EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns);
|
||||
EXPECT_EQ(0, activation1002->start_ns);
|
||||
EXPECT_EQ(kNotActive, activation1002->state);
|
||||
EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType);
|
||||
|
||||
EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(),
|
||||
metric2ActivationTrigger1->atom_matcher_id());
|
||||
const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2);
|
||||
EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
|
||||
EXPECT_EQ(0, activation1003->start_ns);
|
||||
EXPECT_EQ(kNotActive, activation1003->state);
|
||||
EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType);
|
||||
|
||||
EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(),
|
||||
metric2ActivationTrigger2->atom_matcher_id());
|
||||
const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3);
|
||||
EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns);
|
||||
EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns);
|
||||
EXPECT_EQ(kActive, activation1004->state);
|
||||
EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType);
|
||||
// }}}------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
#endif
|
||||
|
||||
@@ -123,6 +123,7 @@ public:
|
||||
size_t size(); // Get the size of the serialized protobuf.
|
||||
sp<ProtoReader> data(); // Get the reader apis of the data.
|
||||
bool flush(int fd); // Flush data directly to a file descriptor.
|
||||
bool serializeToString(std::string* out); // Serializes the proto to a string.
|
||||
|
||||
/**
|
||||
* Clears the ProtoOutputStream so the buffer can be reused instead of deallocation/allocation again.
|
||||
|
||||
@@ -448,6 +448,23 @@ ProtoOutputStream::flush(int fd)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ProtoOutputStream::serializeToString(std::string* out)
|
||||
{
|
||||
if (out == nullptr) return false;
|
||||
if (!compact()) return false;
|
||||
|
||||
|
||||
sp<ProtoReader> reader = mBuffer->read();
|
||||
out->reserve(reader->size());
|
||||
while (reader->hasNext()) {
|
||||
out->append(static_cast<const char*>(static_cast<const void*>(reader->readBuffer())),
|
||||
reader->currentToRead());
|
||||
reader->move(reader->currentToRead());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
sp<ProtoReader>
|
||||
ProtoOutputStream::data()
|
||||
{
|
||||
|
||||
@@ -88,6 +88,50 @@ TEST(ProtoOutputStreamTest, Primitives) {
|
||||
EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO);
|
||||
}
|
||||
|
||||
TEST(ProtoOutputStreamTest, SerializeToStringPrimitives) {
|
||||
std::string s = "hello";
|
||||
const char b[5] = { 'a', 'p', 'p', 'l', 'e' };
|
||||
|
||||
ProtoOutputStream proto;
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | PrimitiveProto::kValInt32FieldNumber, 123));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_INT64 | PrimitiveProto::kValInt64FieldNumber, -1LL));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_FLOAT | PrimitiveProto::kValFloatFieldNumber, -23.5f));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_DOUBLE | PrimitiveProto::kValDoubleFieldNumber, 324.5));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_UINT32 | PrimitiveProto::kValUint32FieldNumber, 3424));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_UINT64 | PrimitiveProto::kValUint64FieldNumber, 57LL));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED32 | PrimitiveProto::kValFixed32FieldNumber, -20));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED64 | PrimitiveProto::kValFixed64FieldNumber, -37LL));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_BOOL | PrimitiveProto::kValBoolFieldNumber, true));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | PrimitiveProto::kValStringFieldNumber, s));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | PrimitiveProto::kValBytesFieldNumber, b, 5));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED32 | PrimitiveProto::kValSfixed32FieldNumber, 63));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED64 | PrimitiveProto::kValSfixed64FieldNumber, -54));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_SINT32 | PrimitiveProto::kValSint32FieldNumber, -533));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_SINT64 | PrimitiveProto::kValSint64FieldNumber, -61224762453LL));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_ENUM | PrimitiveProto::kValEnumFieldNumber, 2));
|
||||
|
||||
PrimitiveProto primitives;
|
||||
std::string serialized;
|
||||
ASSERT_TRUE(proto.serializeToString(&serialized));
|
||||
ASSERT_TRUE(primitives.ParseFromString(serialized));
|
||||
EXPECT_EQ(primitives.val_int32(), 123);
|
||||
EXPECT_EQ(primitives.val_int64(), -1);
|
||||
EXPECT_EQ(primitives.val_float(), -23.5f);
|
||||
EXPECT_EQ(primitives.val_double(), 324.5f);
|
||||
EXPECT_EQ(primitives.val_uint32(), 3424);
|
||||
EXPECT_EQ(primitives.val_uint64(), 57);
|
||||
EXPECT_EQ(primitives.val_fixed32(), -20);
|
||||
EXPECT_EQ(primitives.val_fixed64(), -37);
|
||||
EXPECT_EQ(primitives.val_bool(), true);
|
||||
EXPECT_THAT(primitives.val_string(), StrEq(s.c_str()));
|
||||
EXPECT_THAT(primitives.val_bytes(), StrEq("apple"));
|
||||
EXPECT_EQ(primitives.val_sfixed32(), 63);
|
||||
EXPECT_EQ(primitives.val_sfixed64(), -54);
|
||||
EXPECT_EQ(primitives.val_sint32(), -533);
|
||||
EXPECT_EQ(primitives.val_sint64(), -61224762453LL);
|
||||
EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO);
|
||||
}
|
||||
|
||||
TEST(ProtoOutputStreamTest, Complex) {
|
||||
std::string name1 = "cat";
|
||||
std::string name2 = "dog";
|
||||
@@ -127,6 +171,47 @@ TEST(ProtoOutputStreamTest, Complex) {
|
||||
EXPECT_THAT(log2.data(), StrEq("food"));
|
||||
}
|
||||
|
||||
TEST(ProtoOutputStreamTest, SerializeToStringComplex) {
|
||||
std::string name1 = "cat";
|
||||
std::string name2 = "dog";
|
||||
const char data1[6] = { 'f', 'u', 'n', 'n', 'y', '!' };
|
||||
const char data2[4] = { 'f', 'o', 'o', 'd' };
|
||||
|
||||
ProtoOutputStream proto;
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 23));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 101));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, -72));
|
||||
uint64_t token1 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 12));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name1));
|
||||
// specify the length to test the write(id, bytes, length) function.
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data1, 5));
|
||||
proto.end(token1);
|
||||
uint64_t token2 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 98));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name2));
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data2, 4));
|
||||
proto.end(token2);
|
||||
|
||||
ComplexProto complex;
|
||||
std::string serialized;
|
||||
ASSERT_TRUE(proto.serializeToString(&serialized));
|
||||
ASSERT_TRUE(complex.ParseFromString(serialized));
|
||||
EXPECT_EQ(complex.ints_size(), 3);
|
||||
EXPECT_EQ(complex.ints(0), 23);
|
||||
EXPECT_EQ(complex.ints(1), 101);
|
||||
EXPECT_EQ(complex.ints(2), -72);
|
||||
EXPECT_EQ(complex.logs_size(), 2);
|
||||
ComplexProto::Log log1 = complex.logs(0);
|
||||
EXPECT_EQ(log1.id(), 12);
|
||||
EXPECT_THAT(log1.name(), StrEq(name1.c_str()));
|
||||
EXPECT_THAT(log1.data(), StrEq("funny")); // should not contain '!'
|
||||
ComplexProto::Log log2 = complex.logs(1);
|
||||
EXPECT_EQ(log2.id(), 98);
|
||||
EXPECT_THAT(log2.name(), StrEq(name2.c_str()));
|
||||
EXPECT_THAT(log2.data(), StrEq("food"));
|
||||
}
|
||||
|
||||
TEST(ProtoOutputStreamTest, Reusability) {
|
||||
ProtoOutputStream proto;
|
||||
EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 32));
|
||||
|
||||
Reference in New Issue
Block a user