Merge "Persist active metric status across system server" into qt-dev am: 7ee86cdb65

am: ba7ec1deab

Change-Id: I26645d03246862d11f00585f206a7ae4a4ab0287
This commit is contained in:
Tej Singh
2019-05-16 04:04:56 -07:00
committed by android-build-merger
13 changed files with 454 additions and 42 deletions

View File

@@ -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,

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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()
{

View File

@@ -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));