diff options
| -rw-r--r-- | cmds/statsd/Android.mk | 1 | ||||
| -rw-r--r-- | cmds/statsd/src/config/ConfigManager.cpp | 17 | ||||
| -rw-r--r-- | cmds/statsd/src/metrics/GaugeMetricProducer.cpp | 232 | ||||
| -rw-r--r-- | cmds/statsd/src/metrics/GaugeMetricProducer.h | 97 | ||||
| -rw-r--r-- | cmds/statsd/src/metrics/MetricsManager.cpp | 3 | ||||
| -rw-r--r-- | cmds/statsd/src/metrics/metrics_manager_util.cpp | 43 | ||||
| -rw-r--r-- | cmds/statsd/src/stats_events_copy.proto | 12 | ||||
| -rw-r--r-- | cmds/statsd/src/statsd_config.proto | 6 | ||||
| -rw-r--r-- | cmds/statsd/tests/MetricsManager_test.cpp | 1 |
9 files changed, 407 insertions, 5 deletions
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index c5805fbb2eca..86f353bc615c 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -46,6 +46,7 @@ statsd_common_src := \ src/metrics/duration_helper/OringDurationTracker.cpp \ src/metrics/duration_helper/MaxDurationTracker.cpp \ src/metrics/ValueMetricProducer.cpp \ + src/metrics/GaugeMetricProducer.cpp \ src/metrics/MetricsManager.cpp \ src/metrics/metrics_manager_util.cpp \ src/packages/UidMap.cpp \ diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index 890f44b689d6..02e6903e3260 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -142,6 +142,9 @@ static StatsdConfig build_fake_config() { int KERNEL_WAKELOCK_TAG_ID = 1004; int KERNEL_WAKELOCK_NAME_KEY = 4; + int DEVICE_TEMPERATURE_TAG_ID = 33; + int DEVICE_TEMPERATURE_KEY = 1; + // Count Screen ON events. CountMetric* metric = config.add_count_metric(); metric->set_metric_id(1); @@ -227,7 +230,7 @@ static StatsdConfig build_fake_config() { // Duration of screen on time. durationMetric = config.add_duration_metric(); durationMetric->set_metric_id(8); - durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); + durationMetric->mutable_bucket()->set_bucket_size_millis(10 * 1000L); durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM); durationMetric->set_what("SCREEN_IS_ON"); @@ -247,7 +250,19 @@ static StatsdConfig build_fake_config() { eventMetric->set_metric_id(9); eventMetric->set_what("SCREEN_TURNED_ON"); + // Add an GaugeMetric. + GaugeMetric* gaugeMetric = config.add_gauge_metric(); + gaugeMetric->set_metric_id(10); + gaugeMetric->set_what("DEVICE_TEMPERATURE"); + gaugeMetric->set_gauge_field(DEVICE_TEMPERATURE_KEY); + gaugeMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L); + // Event matchers............ + LogEntryMatcher* temperatureEntryMatcher = config.add_log_entry_matcher(); + temperatureEntryMatcher->set_name("DEVICE_TEMPERATURE"); + temperatureEntryMatcher->mutable_simple_log_entry_matcher()->set_tag( + DEVICE_TEMPERATURE_TAG_ID); + LogEntryMatcher* eventMatcher = config.add_log_entry_matcher(); eventMatcher->set_name("SCREEN_TURNED_ON"); SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp new file mode 100644 index 000000000000..bd638b45eccd --- /dev/null +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -0,0 +1,232 @@ +/* +* 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. +*/ + +#define DEBUG true // STOPSHIP if true +#include "Log.h" + +#include "GaugeMetricProducer.h" +#include "stats_util.h" + +#include <cutils/log.h> +#include <limits.h> +#include <stdlib.h> + +using std::map; +using std::string; +using std::unordered_map; +using std::vector; + +namespace android { +namespace os { +namespace statsd { + +GaugeMetricProducer::GaugeMetricProducer(const GaugeMetric& metric, const int conditionIndex, + const sp<ConditionWizard>& wizard, const int pullTagId) + : MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard), + mMetric(metric), + mPullTagId(pullTagId) { + if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) { + mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000; + } else { + mBucketSizeNs = kDefaultGaugemBucketSizeNs; + } + + // TODO: use UidMap if uid->pkg_name is required + mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end()); + + if (metric.links().size() > 0) { + mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), + metric.links().end()); + mConditionSliced = true; + } + + // Kicks off the puller immediately. + if (mPullTagId != -1) { + mStatsPullerManager.RegisterReceiver(mPullTagId, this, + metric.bucket().bucket_size_millis()); + } + + VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(), + (long long)mBucketSizeNs, (long long)mStartTimeNs); +} + +GaugeMetricProducer::~GaugeMetricProducer() { + VLOG("~GaugeMetricProducer() called"); +} + +void GaugeMetricProducer::finish() { +} + +static void addSlicedGaugeToReport(const vector<KeyValuePair>& key, + const vector<GaugeBucketInfo>& buckets, + StatsLogReport_GaugeMetricDataWrapper& wrapper) { + GaugeMetricData* data = wrapper.add_data(); + for (const auto& kv : key) { + data->add_dimension()->CopyFrom(kv); + } + for (const auto& bucket : buckets) { + data->add_bucket_info()->CopyFrom(bucket); + VLOG("\t bucket [%lld - %lld] gauge: %lld", bucket.start_bucket_nanos(), + bucket.end_bucket_nanos(), bucket.gauge()); + } +} + +StatsLogReport GaugeMetricProducer::onDumpReport() { + VLOG("gauge metric %lld dump report now...", mMetric.metric_id()); + + StatsLogReport report; + report.set_metric_id(mMetric.metric_id()); + report.set_start_report_nanos(mStartTimeNs); + + // Dump current bucket if it's stale. + // If current bucket is still on-going, don't force dump current bucket. + // In finish(), We can force dump current bucket. + flushGaugeIfNeededLocked(time(nullptr) * NANO_SECONDS_IN_A_SECOND); + report.set_end_report_nanos(mCurrentBucketStartTimeNs); + + StatsLogReport_GaugeMetricDataWrapper* wrapper = report.mutable_gauge_metrics(); + + for (const auto& pair : mPastBuckets) { + const HashableDimensionKey& hashableKey = pair.first; + auto it = mDimensionKeyMap.find(hashableKey); + if (it == mDimensionKeyMap.end()) { + ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str()); + continue; + } + + VLOG(" dimension key %s", hashableKey.c_str()); + addSlicedGaugeToReport(it->second, pair.second, *wrapper); + } + return report; + // TODO: Clear mPastBuckets, mDimensionKeyMap once the report is dumped. +} + +void GaugeMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) { + AutoMutex _l(mLock); + VLOG("Metric %lld onConditionChanged", mMetric.metric_id()); + mCondition = conditionMet; + + // Push mode. Nothing to do. + if (mPullTagId == -1) { + return; + } + // If (1) the condition is not met or (2) we already pulled the gauge metric in the current + // bucket, do not pull gauge again. + if (!mCondition || mCurrentSlicedBucket.size() > 0) { + return; + } + vector<std::shared_ptr<LogEvent>> allData; + if (!mStatsPullerManager.Pull(mPullTagId, &allData)) { + ALOGE("Stats puller failed for tag: %d", mPullTagId); + return; + } + for (const auto& data : allData) { + onMatchedLogEvent(0, *data, false /*scheduledPull*/); + } + flushGaugeIfNeededLocked(eventTime); +} + +void GaugeMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { + VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id()); +} + +long GaugeMetricProducer::getGauge(const LogEvent& event) { + status_t err = NO_ERROR; + long val = event.GetLong(mMetric.gauge_field(), &err); + if (err == NO_ERROR) { + return val; + } else { + VLOG("Can't find value in message."); + return -1; + } +} + +void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) { + AutoMutex mutex(mLock); + if (allData.size() == 0) { + return; + } + for (const auto& data : allData) { + onMatchedLogEvent(0, *data, true /*scheduledPull*/); + } + uint64_t eventTime = allData.at(0)->GetTimestampNs(); + flushGaugeIfNeededLocked(eventTime); +} + +void GaugeMetricProducer::onMatchedLogEventInternal( + const size_t matcherIndex, const HashableDimensionKey& eventKey, + const map<string, HashableDimensionKey>& conditionKey, bool condition, + const LogEvent& event, bool scheduledPull) { + if (condition == false) { + return; + } + uint64_t eventTimeNs = event.GetTimestampNs(); + if (eventTimeNs < mCurrentBucketStartTimeNs) { + VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, + (long long)mCurrentBucketStartTimeNs); + return; + } + + // For gauge metric, we just simply use the latest guage in the given bucket. + const long gauge = getGauge(event); + if (gauge < 0) { + VLOG("Invalid gauge at event Time: %lld", (long long)eventTimeNs); + return; + } + mCurrentSlicedBucket[eventKey] = gauge; + if (mPullTagId < 0) { + flushGaugeIfNeededLocked(eventTimeNs); + } +} + +// When a new matched event comes in, we check if event falls into the current +// bucket. If not, flush the old counter to past buckets and initialize the new +// bucket. +// if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside +// the GaugeMetricProducer while holding the lock. +void GaugeMetricProducer::flushGaugeIfNeededLocked(const uint64_t eventTimeNs) { + if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) { + VLOG("event time is %lld, less than next bucket start time %lld", (long long)eventTimeNs, + (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs)); + return; + } + + // Adjusts the bucket start time + int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs; + + GaugeBucketInfo info; + info.set_start_bucket_nanos(mCurrentBucketStartTimeNs); + info.set_end_bucket_nanos(mCurrentBucketStartTimeNs + mBucketSizeNs); + + for (const auto& slice : mCurrentSlicedBucket) { + info.set_gauge(slice.second); + auto& bucketList = mPastBuckets[slice.first]; + bucketList.push_back(info); + + VLOG("gauge metric %lld, dump key value: %s -> %ld", mMetric.metric_id(), + slice.first.c_str(), slice.second); + } + // Reset counters + mCurrentSlicedBucket.clear(); + + mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs; + VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(), + (long long)mCurrentBucketStartTimeNs); +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h new file mode 100644 index 000000000000..bf8a86f3794a --- /dev/null +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <unordered_map> + +#include "../condition/ConditionTracker.h" +#include "../external/PullDataReceiver.h" +#include "../external/StatsPullerManager.h" +#include "../matchers/matcher_util.h" +#include "MetricProducer.h" +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "stats_util.h" + +namespace android { +namespace os { +namespace statsd { + +// This gauge metric producer first register the puller to automatically pull the gauge at the +// beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise +// proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric +// producer always reports the guage at the earliest time of the bucket when the condition is met. +class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver { +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); + + virtual ~GaugeMetricProducer(); + + // Handles when the pulled data arrives. + void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override; + + void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override; + void onSlicedConditionMayChange(const uint64_t eventTime) override; + + void finish() override; + + StatsLogReport onDumpReport() override; + + // TODO: implements it when supporting proto stream. + size_t byteSize() override { + return 0; + }; + + // TODO: Implement this later. + virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{}; + // TODO: Implement this later. + virtual void notifyAppRemoved(const string& apk, const int uid) override{}; + +protected: + void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey, + const std::map<std::string, HashableDimensionKey>& conditionKey, + bool condition, const LogEvent& event, + bool scheduledPull) override; + +private: + // The default bucket size for gauge metric is 1 second. + static const uint64_t kDefaultGaugemBucketSizeNs = 1000 * 1000 * 1000; + const GaugeMetric mMetric; + + StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance(); + // tagId for pulled data. -1 if this is not pulled + const int mPullTagId; + + Mutex mLock; + + // Save the past buckets and we can clear when the StatsLogReport is dumped. + std::unordered_map<HashableDimensionKey, std::vector<GaugeBucketInfo>> mPastBuckets; + + // The current bucket. + std::unordered_map<HashableDimensionKey, long> mCurrentSlicedBucket; + + void flushGaugeIfNeededLocked(const uint64_t newEventTime); + + long getGauge(const LogEvent& event); +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 521fcc33ac47..9b708dd63275 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -146,7 +146,8 @@ void MetricsManager::onLogEvent(const LogEvent& event) { auto& metricList = pair->second; for (const int metricIndex : metricList) { // pushed metrics are never scheduled pulls - mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event, false); + mAllMetricProducers[metricIndex]->onMatchedLogEvent( + i, event, false /* schedulePull */); } } } diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 3ff2a773327e..a0fdcdca9c34 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -22,6 +22,7 @@ #include "CountMetricProducer.h" #include "DurationMetricProducer.h" #include "EventMetricProducer.h" +#include "GaugeMetricProducer.h" #include "ValueMetricProducer.h" #include "stats_util.h" @@ -308,7 +309,6 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l } sp<MetricProducer> eventMetric = new EventMetricProducer(metric, conditionIndex, wizard); - allMetricProducers.push_back(eventMetric); } @@ -351,6 +351,46 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l new ValueMetricProducer(metric, conditionIndex, wizard, pullTagId); allMetricProducers.push_back(valueProducer); } + + // Gauge metrics. + for (int i = 0; i < config.gauge_metric_size(); i++) { + const GaugeMetric& metric = config.gauge_metric(i); + if (!metric.has_what()) { + ALOGW("cannot find what in ValueMetric %lld", metric.metric_id()); + return false; + } + + int metricIndex = allMetricProducers.size(); + int trackerIndex; + if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0, + allLogEntryMatchers, logTrackerMap, trackerToMetricMap, + trackerIndex)) { + return false; + } + + sp<LogMatchingTracker> atomMatcher = allLogEntryMatchers.at(trackerIndex); + // If it is pulled atom, it should be simple matcher with one tagId. + int pullTagId = -1; + for (int tagId : atomMatcher->getTagIds()) { + if (statsPullerManager.PullerForMatcherExists(tagId)) { + if (atomMatcher->getTagIds().size() != 1) { + return false; + } + pullTagId = tagId; + } + } + + int conditionIndex = -1; + if (metric.has_condition()) { + handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, + metric.links(), allConditionTrackers, conditionIndex, + conditionToMetricMap); + } + + sp<MetricProducer> gaugeProducer = + new GaugeMetricProducer(metric, conditionIndex, wizard, pullTagId); + allMetricProducers.push_back(gaugeProducer); + } return true; } @@ -368,6 +408,7 @@ bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds, ALOGE("initLogMatchingTrackers failed"); return false; } + ALOGD("initLogMatchingTrackers succeed..."); if (!initConditions(config, logTrackerMap, conditionTrackerMap, allConditionTrackers, trackerToConditionMap)) { diff --git a/cmds/statsd/src/stats_events_copy.proto b/cmds/statsd/src/stats_events_copy.proto index 9470372005f8..898856b06ebc 100644 --- a/cmds/statsd/src/stats_events_copy.proto +++ b/cmds/statsd/src/stats_events_copy.proto @@ -61,6 +61,7 @@ message StatsEvent { UidProcessStateChanged uid_process_state_changed = 27; ProcessLifeCycleStateChanged process_life_cycle_state_changed = 28; ScreenStateChanged screen_state_changed = 29; + DeviceTemperatureReported device_temperature_reported = 33; // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15. } } @@ -101,6 +102,17 @@ message WorkSource { */ /** + * Logs the temperature of the device, in tenths of a degree Celsius. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message DeviceTemperatureReported { + // Temperature in tenths of a degree C. + optional int32 temperature = 1; +} + +/** * Logs when the screen state changes. * * Logged from: diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 87884b33cef0..f3e68946fead 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -230,7 +230,9 @@ message StatsdConfig { repeated DurationMetric duration_metric = 5; - repeated LogEntryMatcher log_entry_matcher = 6; + repeated GaugeMetric gauge_metric = 6; - repeated Condition condition = 7; + repeated LogEntryMatcher log_entry_matcher = 7; + + repeated Condition condition = 8; } diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index e8e4d8be1cd6..733378551806 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -17,6 +17,7 @@ #include "src/condition/ConditionTracker.h" #include "src/matchers/LogMatchingTracker.h" #include "src/metrics/CountMetricProducer.h" +#include "src/metrics/GaugeMetricProducer.h" #include "src/metrics/MetricProducer.h" #include "src/metrics/ValueMetricProducer.h" #include "src/metrics/metrics_manager_util.h" |