summaryrefslogtreecommitdiff
path: root/pixelstats/MmMetricsReporter.cpp
diff options
context:
space:
mode:
authorChiawei Wang <chiaweiwang@google.com>2021-03-18 21:11:56 +0800
committerChiawei Wang <chiaweiwang@google.com>2021-03-24 09:35:52 +0800
commit5db67e6a7e91ccfc9e7d08b2b9bbf810fc59f6bb (patch)
treed94238db942d9222c2aecacd512a54c41d92221b /pixelstats/MmMetricsReporter.cpp
parent4dd35a92d41cf15055db4b4760bba3027f5a5c3c (diff)
pixelstats: move MM Metrics atom reporting to MmMetricsReporter
Create MmMetricsReporter to report MM Metrics atoms Bug: 173101018 Test: adb shell cmd stats print-stats | grep 10501[56] Test: adb shell cmd stats print-logs adb logcat | grep 10501[56] Change-Id: I5f64fb680cb093325bc241894f3b240e9c41d40c
Diffstat (limited to 'pixelstats/MmMetricsReporter.cpp')
-rw-r--r--pixelstats/MmMetricsReporter.cpp251
1 files changed, 251 insertions, 0 deletions
diff --git a/pixelstats/MmMetricsReporter.cpp b/pixelstats/MmMetricsReporter.cpp
new file mode 100644
index 0000000..4088621
--- /dev/null
+++ b/pixelstats/MmMetricsReporter.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2021 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 LOG_TAG "pixelstats: MmMetrics"
+
+#include <aidl/android/frameworks/stats/IStats.h>
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android/binder_manager.h>
+#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
+#include <pixelstats/MmMetricsReporter.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+
+using aidl::android::frameworks::stats::IStats;
+using aidl::android::frameworks::stats::VendorAtom;
+using aidl::android::frameworks::stats::VendorAtomValue;
+using android::base::ReadFileToString;
+using android::hardware::google::pixel::PixelAtoms::PixelMmMetricsPerDay;
+using android::hardware::google::pixel::PixelAtoms::PixelMmMetricsPerHour;
+
+const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kMmMetricsPerHourInfo = {
+ {"nr_free_pages", PixelMmMetricsPerHour::kFreePagesFieldNumber, false},
+ {"nr_anon_pages", PixelMmMetricsPerHour::kAnonPagesFieldNumber, false},
+ {"nr_file_pages", PixelMmMetricsPerHour::kFilePagesFieldNumber, false},
+ {"nr_slab_reclaimable", PixelMmMetricsPerHour::kSlabReclaimableFieldNumber, false},
+ {"nr_zspages", PixelMmMetricsPerHour::kZspagesFieldNumber, false},
+ {"nr_unevictable", PixelMmMetricsPerHour::kUnevictableFieldNumber, false},
+};
+
+const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kMmMetricsPerDayInfo = {
+ {"workingset_refault", PixelMmMetricsPerDay::kWorkingsetRefaultFieldNumber, true},
+ {"workingset_refault_file", PixelMmMetricsPerDay::kWorkingsetRefaultFieldNumber, true},
+ {"pswpin", PixelMmMetricsPerDay::kPswpinFieldNumber, true},
+ {"pswpout", PixelMmMetricsPerDay::kPswpoutFieldNumber, true},
+ {"allocstall_dma", PixelMmMetricsPerDay::kAllocstallDmaFieldNumber, true},
+ {"allocstall_dma32", PixelMmMetricsPerDay::kAllocstallDma32FieldNumber, true},
+ {"allocstall_normal", PixelMmMetricsPerDay::kAllocstallNormalFieldNumber, true},
+ {"allocstall_movable", PixelMmMetricsPerDay::kAllocstallMovableFieldNumber, true},
+ {"pgalloc_dma", PixelMmMetricsPerDay::kPgallocDmaFieldNumber, true},
+ {"pgalloc_dma32", PixelMmMetricsPerDay::kPgallocDma32FieldNumber, true},
+ {"pgalloc_normal", PixelMmMetricsPerDay::kPgallocNormalFieldNumber, true},
+ {"pgalloc_movable", PixelMmMetricsPerDay::kPgallocMovableFieldNumber, true},
+ {"pgsteal_kswapd", PixelMmMetricsPerDay::kPgstealKswapdFieldNumber, true},
+ {"pgsteal_direct", PixelMmMetricsPerDay::kPgstealDirectFieldNumber, true},
+ {"pgscan_kswapd", PixelMmMetricsPerDay::kPgscanKswapdFieldNumber, true},
+ {"pgscan_direct", PixelMmMetricsPerDay::kPgscanDirectFieldNumber, true},
+ {"oom_kill", PixelMmMetricsPerDay::kOomKillFieldNumber, true},
+};
+
+MmMetricsReporter::MmMetricsReporter()
+ : kVmstatPath("/proc/vmstat"),
+ kIonTotalPoolsPath("/sys/kernel/dma_heap/total_pools_kb"),
+ kIonTotalPoolsPathForLegacy("/sys/kernel/ion/total_pools_kb") {}
+
+bool MmMetricsReporter::ReadFileToUint(const char *const path, uint64_t *val) {
+ std::string file_contents;
+
+ if (!ReadFileToString(path, &file_contents)) {
+ ALOGI("Unable to read %s - %s", path, strerror(errno));
+ return false;
+ } else {
+ file_contents = android::base::Trim(file_contents);
+ if (!android::base::ParseUint(file_contents, val)) {
+ ALOGI("Unable to convert %s to uint - %s", path, strerror(errno));
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Parse the output of /proc/vmstat or the sysfs having the same output format.
+ * The map containing pairs of {field_string, data} will be returned.
+ */
+std::map<std::string, uint64_t> MmMetricsReporter::readVmStat(const char *path) {
+ std::string file_contents;
+ std::map<std::string, uint64_t> vmstat_data;
+
+ if (path == nullptr) {
+ ALOGI("vmstat path is not specified");
+ return vmstat_data;
+ }
+
+ if (!ReadFileToString(path, &file_contents)) {
+ ALOGE("Unable to read vmstat from %s, err: %s", path, strerror(errno));
+ return vmstat_data;
+ }
+
+ std::istringstream data(file_contents);
+ std::string line;
+ while (std::getline(data, line)) {
+ std::vector<std::string> words = android::base::Split(line, " ");
+ if (words.size() != 2)
+ continue;
+
+ uint64_t i;
+ if (!android::base::ParseUint(words[1], &i))
+ continue;
+
+ vmstat_data[words[0]] = i;
+ }
+ return vmstat_data;
+}
+
+uint64_t MmMetricsReporter::getIonTotalPools() {
+ uint64_t res;
+
+ if (!ReadFileToUint(kIonTotalPoolsPathForLegacy, &res) || (res == 0)) {
+ if (!ReadFileToUint(kIonTotalPoolsPath, &res)) {
+ return 0;
+ }
+ }
+
+ return res;
+}
+
+/**
+ * fillAtomValues() is used to copy Mm metrics to values
+ * metrics_info: This is a vector of MmMetricsInfo {field_string, atom_key, update_diff}
+ * field_string is used to get the data from mm_metrics.
+ * atom_key is the position where the data should be put into values.
+ * update_diff will be true if this is an accumulated data.
+ * metrics_info may have multiple entries with the same atom_key,
+ * e.g. workingset_refault and workingset_refault_file.
+ * mm_metrics: This map contains pairs of {field_string, cur_value} collected
+ * from /proc/vmstat or the sysfs for the pixel specific metrics.
+ * e.g. {"nr_free_pages", 200000}
+ * Some data in mm_metrics are accumulated, e.g. pswpin.
+ * We upload the difference instead of the accumulated value
+ * when update_diff of the field is true.
+ * prev_mm_metrics: The pointer to the metrics we collected last time.
+ * atom_values: The atom values that will be reported later.
+ */
+void MmMetricsReporter::fillAtomValues(const std::vector<MmMetricsInfo> &metrics_info,
+ const std::map<std::string, uint64_t> &mm_metrics,
+ std::map<std::string, uint64_t> *prev_mm_metrics,
+ std::vector<VendorAtomValue> *atom_values) {
+ VendorAtomValue tmp;
+ tmp.set<VendorAtomValue::longValue>(0);
+ // resize atom_values to add all fields defined in metrics_info
+ int max_idx = 0;
+ for (auto &entry : metrics_info) {
+ if (max_idx < entry.atom_key)
+ max_idx = entry.atom_key;
+ }
+ int size = max_idx - kVendorAtomOffset + 1;
+ if (atom_values->size() < size)
+ atom_values->resize(size, tmp);
+
+ for (auto &entry : metrics_info) {
+ int atom_idx = entry.atom_key - kVendorAtomOffset;
+
+ auto data = mm_metrics.find(entry.name);
+ if (data == mm_metrics.end())
+ continue;
+
+ uint64_t cur_value = data->second;
+ uint64_t prev_value = 0;
+ if (prev_mm_metrics->size() != 0) {
+ auto prev_data = prev_mm_metrics->find(entry.name);
+ if (prev_data != prev_mm_metrics->end())
+ prev_value = prev_data->second;
+ }
+
+ if (entry.update_diff) {
+ tmp.set<VendorAtomValue::longValue>(cur_value - prev_value);
+ } else {
+ tmp.set<VendorAtomValue::longValue>(cur_value);
+ }
+ (*atom_values)[atom_idx] = tmp;
+ }
+ (*prev_mm_metrics) = mm_metrics;
+}
+
+void MmMetricsReporter::logPixelMmMetricsPerHour(const std::shared_ptr<IStats> &stats_client) {
+ std::map<std::string, uint64_t> vmstat = readVmStat(kVmstatPath);
+ if (vmstat.size() == 0)
+ return;
+
+ uint64_t ion_total_pools = getIonTotalPools();
+
+ std::vector<VendorAtomValue> values;
+ bool is_first_atom = (prev_hour_vmstat_.size() == 0) ? true : false;
+ fillAtomValues(kMmMetricsPerHourInfo, vmstat, &prev_hour_vmstat_, &values);
+
+ // resize values to add the following fields
+ VendorAtomValue tmp;
+ tmp.set<VendorAtomValue::longValue>(0);
+ int size = PixelMmMetricsPerHour::kIonTotalPoolsFieldNumber - kVendorAtomOffset + 1;
+ if (values.size() < size) {
+ values.resize(size, tmp);
+ }
+ tmp.set<VendorAtomValue::longValue>(ion_total_pools);
+ values[PixelMmMetricsPerHour::kIonTotalPoolsFieldNumber - kVendorAtomOffset] = tmp;
+
+ // Don't report the first atom to avoid big spike in accumulated values.
+ if (!is_first_atom) {
+ // Send vendor atom to IStats HAL
+ VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
+ .atomId = PixelAtoms::Ids::PIXEL_MM_METRICS_PER_HOUR,
+ .values = std::move(values)};
+ const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+ if (!ret.isOk())
+ ALOGE("Unable to report PixelMmMetricsPerHour to Stats service");
+ }
+}
+
+void MmMetricsReporter::logPixelMmMetricsPerDay(const std::shared_ptr<IStats> &stats_client) {
+ std::map<std::string, uint64_t> vmstat = readVmStat(kVmstatPath);
+ if (vmstat.size() == 0)
+ return;
+
+ std::vector<VendorAtomValue> values;
+ bool is_first_atom = (prev_day_vmstat_.size() == 0) ? true : false;
+ fillAtomValues(kMmMetricsPerDayInfo, vmstat, &prev_day_vmstat_, &values);
+
+ // Don't report the first atom to avoid big spike in accumulated values.
+ if (!is_first_atom) {
+ // Send vendor atom to IStats HAL
+ VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
+ .atomId = PixelAtoms::Ids::PIXEL_MM_METRICS_PER_DAY,
+ .values = std::move(values)};
+ const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+ if (!ret.isOk())
+ ALOGE("Unable to report MEMORY_MANAGEMENT_INFO to Stats service");
+ }
+}
+
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android