Gauge metric producer.
Test: manual tests passed for pushed device temperature.
Change-Id: I7592a4c04666606b745cdb41db8f9d8a96a966da
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index c5805fbb..86f353b 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -46,6 +46,7 @@
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 890f44b..02e6903 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -142,6 +142,9 @@
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 @@
// 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 @@
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 0000000..bd638b4
--- /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 0000000..bf8a86f
--- /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 521fcc3..9b708dd6 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -146,7 +146,8 @@
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 3ff2a77..a0fdcdc 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 @@
}
sp<MetricProducer> eventMetric = new EventMetricProducer(metric, conditionIndex, wizard);
-
allMetricProducers.push_back(eventMetric);
}
@@ -351,6 +351,46 @@
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 @@
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 9470372..898856b 100644
--- a/cmds/statsd/src/stats_events_copy.proto
+++ b/cmds/statsd/src/stats_events_copy.proto
@@ -61,6 +61,7 @@
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 @@
*/
/**
+ * 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 87884b33..f3e6894 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -230,7 +230,9 @@
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 e8e4d8b..7333785 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"