summaryrefslogtreecommitdiff
path: root/pixelstats/MmMetricsReporter.cpp
diff options
context:
space:
mode:
authorChiawei Wang <chiaweiwang@google.com>2021-03-09 22:01:47 +0800
committerChiawei Wang <chiaweiwang@google.com>2021-03-25 02:28:12 +0800
commit588e3281e9d60dafa25ba10a95a61401c38f1286 (patch)
tree61f18118a13f06b8223a9778a93211ce1316faff /pixelstats/MmMetricsReporter.cpp
parent5db67e6a7e91ccfc9e7d08b2b9bbf810fc59f6bb (diff)
pixelstats: upload pixel specifc metrics and gpu memory
Upload the metrics from the following file nodes: [stime of kswapd and kcompactd] /proc/<kswapd0 pid>/stat: stime /proc/<kcompactd0 pid>/stat: stime [Pixel specifc metrics] /sys/kernel/pixel_stat/mm/vmstat: pgalloc_costly_order pgcache_miss pgcache_hit [CMA metrics] /sys/kernel/pixel_stat/mm/<cma-heap type>: cma_alloc_pages_attempts cma_alloc_pages_soft_attempts cma_fail_pages cma_fail_soft_pages migrated_pages cma_alloc_latency_low cma_alloc_latency_mid cma_alloc_latency_high cma-heap type: farawimg faimg fatpu faprev vframe [GPU allocated pages] /sys/kernel/debug/physical-memory-group-manager/<group>: size lp_size Bug: 173101018 Test: adb shell cmd stats print-stats | grep 10501[56] Test: adb shell cmd stats print-stats | grep 10502[45] Test: adb shell cmd stats print-logs adb logcat | grep -e 10501[56] -e 10502[45] Signed-off-by: Chiawei Wang <chiaweiwang@google.com> Change-Id: Ie10a8f606b12988043d131ee545556af3fd55b74
Diffstat (limited to 'pixelstats/MmMetricsReporter.cpp')
-rw-r--r--pixelstats/MmMetricsReporter.cpp328
1 files changed, 314 insertions, 14 deletions
diff --git a/pixelstats/MmMetricsReporter.cpp b/pixelstats/MmMetricsReporter.cpp
index 4088621..cbedd73 100644
--- a/pixelstats/MmMetricsReporter.cpp
+++ b/pixelstats/MmMetricsReporter.cpp
@@ -19,12 +19,17 @@
#include <aidl/android/frameworks/stats/IStats.h>
#include <android-base/file.h>
#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.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>
+#define SZ_4K 0x00001000
+#define SZ_2M 0x00200000
+
namespace android {
namespace hardware {
namespace google {
@@ -34,6 +39,9 @@ using aidl::android::frameworks::stats::IStats;
using aidl::android::frameworks::stats::VendorAtom;
using aidl::android::frameworks::stats::VendorAtomValue;
using android::base::ReadFileToString;
+using android::base::StartsWith;
+using android::hardware::google::pixel::PixelAtoms::CmaStatus;
+using android::hardware::google::pixel::PixelAtoms::CmaStatusExt;
using android::hardware::google::pixel::PixelAtoms::PixelMmMetricsPerDay;
using android::hardware::google::pixel::PixelAtoms::PixelMmMetricsPerHour;
@@ -64,12 +72,37 @@ const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kMmMetric
{"pgscan_kswapd", PixelMmMetricsPerDay::kPgscanKswapdFieldNumber, true},
{"pgscan_direct", PixelMmMetricsPerDay::kPgscanDirectFieldNumber, true},
{"oom_kill", PixelMmMetricsPerDay::kOomKillFieldNumber, true},
+ {"pgalloc_costly_order", PixelMmMetricsPerDay::kPgallocHighFieldNumber, true},
+ {"pgcache_hit", PixelMmMetricsPerDay::kPgcacheHitFieldNumber, true},
+ {"pgcache_miss", PixelMmMetricsPerDay::kPgcacheMissFieldNumber, true},
+};
+
+const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kCmaStatusInfo = {
+ {"cma_alloc_pages_attempts", CmaStatus::kCmaAllocPagesAttemptsFieldNumber, true},
+ {"cma_alloc_pages_soft_attempts", CmaStatus::kCmaAllocPagesSoftAttemptsFieldNumber, true},
+ {"cma_fail_pages", CmaStatus::kCmaFailPagesFieldNumber, true},
+ {"cma_fail_soft_pages", CmaStatus::kCmaFailSoftPagesFieldNumber, true},
+ {"migrated_pages", CmaStatus::kMigratedPagesFieldNumber, true},
+};
+
+const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kCmaStatusExtInfo = {
+ {"cma_alloc_latency_low", CmaStatusExt::kCmaAllocLatencyLowFieldNumber, false},
+ {"cma_alloc_latency_mid", CmaStatusExt::kCmaAllocLatencyMidFieldNumber, false},
+ {"cma_alloc_latency_high", CmaStatusExt::kCmaAllocLatencyHighFieldNumber, false},
+};
+
+const std::map<std::string, MmMetricsReporter::CmaType> MmMetricsReporter::kCmaTypeInfo = {
+ {"farawimg", MmMetricsReporter::FARAWIMG}, {"faimg", MmMetricsReporter::FAIMG},
+ {"fatpu", MmMetricsReporter::FATPU}, {"faprev", MmMetricsReporter::FAPREV},
+ {"vframe", MmMetricsReporter::VFRAME},
};
MmMetricsReporter::MmMetricsReporter()
: kVmstatPath("/proc/vmstat"),
kIonTotalPoolsPath("/sys/kernel/dma_heap/total_pools_kb"),
- kIonTotalPoolsPathForLegacy("/sys/kernel/ion/total_pools_kb") {}
+ kIonTotalPoolsPathForLegacy("/sys/kernel/ion/total_pools_kb"),
+ kMgmDebugFs("/sys/kernel/debug/physical-memory-group-manager"),
+ kPixelStatMm("/sys/kernel/pixel_stat/mm") {}
bool MmMetricsReporter::ReadFileToUint(const char *const path, uint64_t *val) {
std::string file_contents;
@@ -87,6 +120,21 @@ bool MmMetricsReporter::ReadFileToUint(const char *const path, uint64_t *val) {
return true;
}
+bool MmMetricsReporter::reportVendorAtom(const std::shared_ptr<IStats> &stats_client, int atom_id,
+ const std::vector<VendorAtomValue> &values,
+ const std::string &atom_name) {
+ // Send vendor atom to IStats HAL
+ VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
+ .atomId = atom_id,
+ .values = std::move(values)};
+ const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
+ if (!ret.isOk()) {
+ ALOGE("Unable to report %s to Stats service", atom_name.c_str());
+ 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.
@@ -134,6 +182,40 @@ uint64_t MmMetricsReporter::getIonTotalPools() {
}
/**
+ * Collect GPU memory from kMgmDebugFs and return the total number of 4K page.
+ * <kMgmDebugFs>/<group>/size is the 4KB page number
+ * <kMgmDebugFs>/<group>/lp_size is the 2MB page number
+ */
+uint64_t MmMetricsReporter::getGpuMemory() {
+ uint64_t gpu_size = 0;
+
+ std::unique_ptr<DIR, int (*)(DIR *)> dir(opendir(kMgmDebugFs), closedir);
+ if (!dir) {
+ return 0;
+ }
+
+ while (struct dirent *dp = readdir(dir.get())) {
+ if (dp->d_type != DT_DIR)
+ continue;
+
+ if (!StartsWith(dp->d_name, "group"))
+ continue;
+
+ uint64_t size;
+ std::string path_4K = android::base::StringPrintf("%s/%s/size", kMgmDebugFs, dp->d_name);
+ std::string path_2M = android::base::StringPrintf("%s/%s/lp_size", kMgmDebugFs, dp->d_name);
+ if (ReadFileToUint(path_4K.c_str(), &size)) {
+ gpu_size += size;
+ }
+
+ if (ReadFileToUint(path_2M.c_str(), &size)) {
+ gpu_size += (size * SZ_2M / SZ_4K);
+ }
+ }
+ return gpu_size;
+}
+
+/**
* 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.
@@ -192,11 +274,18 @@ void MmMetricsReporter::fillAtomValues(const std::vector<MmMetricsInfo> &metrics
}
void MmMetricsReporter::logPixelMmMetricsPerHour(const std::shared_ptr<IStats> &stats_client) {
+ // Currently, we collect these metrics and report this atom only for userdebug_or_eng
+ // We only grant permissions to access sysfs for userdebug_or_eng.
+ // Add a check to avoid unnecessary access.
+ if (android::base::GetProperty("ro.build.type", "") == "user")
+ return;
+
std::map<std::string, uint64_t> vmstat = readVmStat(kVmstatPath);
if (vmstat.size() == 0)
return;
uint64_t ion_total_pools = getIonTotalPools();
+ uint64_t gpu_memory = getGpuMemory();
std::vector<VendorAtomValue> values;
bool is_first_atom = (prev_hour_vmstat_.size() == 0) ? true : false;
@@ -205,26 +294,30 @@ void MmMetricsReporter::logPixelMmMetricsPerHour(const std::shared_ptr<IStats> &
// resize values to add the following fields
VendorAtomValue tmp;
tmp.set<VendorAtomValue::longValue>(0);
- int size = PixelMmMetricsPerHour::kIonTotalPoolsFieldNumber - kVendorAtomOffset + 1;
+ int size = PixelMmMetricsPerHour::kGpuMemoryFieldNumber - kVendorAtomOffset + 1;
if (values.size() < size) {
values.resize(size, tmp);
}
tmp.set<VendorAtomValue::longValue>(ion_total_pools);
values[PixelMmMetricsPerHour::kIonTotalPoolsFieldNumber - kVendorAtomOffset] = tmp;
+ tmp.set<VendorAtomValue::longValue>(gpu_memory);
+ values[PixelMmMetricsPerHour::kGpuMemoryFieldNumber - 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");
+ reportVendorAtom(stats_client, PixelAtoms::Ids::PIXEL_MM_METRICS_PER_HOUR, values,
+ "PixelMmMetricsPerHour");
}
}
void MmMetricsReporter::logPixelMmMetricsPerDay(const std::shared_ptr<IStats> &stats_client) {
+ // Currently, we collect these metrics and report this atom only for userdebug_or_eng
+ // We only grant permissions to access sysfs for userdebug_or_eng.
+ // Add a check to avoid unnecessary access.
+ if (android::base::GetProperty("ro.build.type", "") == "user")
+ return;
+
std::map<std::string, uint64_t> vmstat = readVmStat(kVmstatPath);
if (vmstat.size() == 0)
return;
@@ -233,15 +326,222 @@ void MmMetricsReporter::logPixelMmMetricsPerDay(const std::shared_ptr<IStats> &s
bool is_first_atom = (prev_day_vmstat_.size() == 0) ? true : false;
fillAtomValues(kMmMetricsPerDayInfo, vmstat, &prev_day_vmstat_, &values);
+ std::map<std::string, uint64_t> pixel_vmstat =
+ readVmStat(android::base::StringPrintf("%s/vmstat", kPixelStatMm).c_str());
+ fillAtomValues(kMmMetricsPerDayInfo, pixel_vmstat, &prev_day_pixel_vmstat_, &values);
+ fillProcessStime(PixelMmMetricsPerDay::kKswapdTimeFieldNumber, "kswapd0", &kswapd_pid_,
+ &prev_kswapd_stime_, &values);
+ fillProcessStime(PixelMmMetricsPerDay::kKcompactdTimeFieldNumber, "kcompactd0", &kcompactd_pid_,
+ &prev_kcompactd_stime_, &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");
+ reportVendorAtom(stats_client, PixelAtoms::Ids::PIXEL_MM_METRICS_PER_DAY, values,
+ "PixelMmMetricsPerDay");
+ }
+}
+
+/**
+ * Check if /proc/<pid>/comm is equal to name.
+ */
+bool MmMetricsReporter::isValidPid(int pid, const char *name) {
+ if (pid <= 0)
+ return false;
+
+ std::string file_contents;
+ std::string path = android::base::StringPrintf("/proc/%d/comm", pid);
+ if (!ReadFileToString(path, &file_contents)) {
+ ALOGI("Unable to read %s, err: %s", path.c_str(), strerror(errno));
+ return false;
+ }
+
+ file_contents = android::base::Trim(file_contents);
+ return !file_contents.compare(name);
+}
+
+/**
+ * Return pid if /proc/<pid>/comm is equal to name, or -1 if not found.
+ */
+int MmMetricsReporter::findPidByProcessName(const char *name) {
+ std::unique_ptr<DIR, int (*)(DIR *)> dir(opendir("/proc"), closedir);
+ if (!dir)
+ return -1;
+
+ int pid;
+ while (struct dirent *dp = readdir(dir.get())) {
+ if (dp->d_type != DT_DIR)
+ continue;
+
+ if (!android::base::ParseInt(dp->d_name, &pid))
+ continue;
+
+ // Avoid avc denial since pixelstats-vendor doesn't have the permission to access /proc/1
+ if (pid == 1)
+ continue;
+
+ std::string file_contents;
+ std::string path = android::base::StringPrintf("/proc/%s/comm", dp->d_name);
+ if (!ReadFileToString(path, &file_contents))
+ continue;
+
+ file_contents = android::base::Trim(file_contents);
+ if (file_contents.compare(name))
+ continue;
+
+ return pid;
+ }
+ return -1;
+}
+
+/**
+ * Get stime of a process from /proc/<pid>/stat
+ * stime is the 15th field.
+ */
+uint64_t MmMetricsReporter::getStimeByPid(int pid) {
+ const int stime_idx = 15;
+ uint64_t stime;
+ std::string file_contents;
+ std::string path = android::base::StringPrintf("/proc/%d/stat", pid);
+ if (!ReadFileToString(path, &file_contents)) {
+ ALOGI("Unable to read %s, err: %s", path.c_str(), strerror(errno));
+ return false;
+ }
+
+ std::vector<std::string> data = android::base::Split(file_contents, " ");
+ if (data.size() < stime_idx) {
+ ALOGI("Unable to find stime from %s. size: %lu", path.c_str(), data.size());
+ return false;
+ }
+
+ if (android::base::ParseUint(data[stime_idx - 1], &stime))
+ return stime;
+ else
+ return 0;
+}
+
+/**
+ * Find stime of the process and copy it into atom_values
+ * atom_key: Currently, it can only be kKswapdTimeFieldNumber or kKcompactdTimeFieldNumber
+ * name: process name
+ * pid: The pid of the process. It would be the pid we found last time,
+ * or -1 if not found.
+ * prev_stime: The stime of the process collected last time.
+ * atom_values: The atom we will report later.
+ */
+void MmMetricsReporter::fillProcessStime(int atom_key, const char *name, int *pid,
+ uint64_t *prev_stime,
+ std::vector<VendorAtomValue> *atom_values) {
+ // resize atom_values if there is no space for this stime field.
+ int atom_idx = atom_key - kVendorAtomOffset;
+ int size = atom_idx + 1;
+ VendorAtomValue tmp;
+ tmp.set<VendorAtomValue::longValue>(0);
+ if (atom_values->size() < size)
+ atom_values->resize(size, tmp);
+
+ if (!isValidPid(*pid, name)) {
+ (*pid) = findPidByProcessName(name);
+ if ((*pid) <= 0) {
+ ALOGI("Unable to find pid of %s, err: %s", name, strerror(errno));
+ return;
+ }
+ }
+
+ uint64_t stime = getStimeByPid(*pid);
+ tmp.set<VendorAtomValue::longValue>(stime - *prev_stime);
+ (*atom_values)[atom_idx] = tmp;
+ (*prev_stime) = stime;
+}
+
+/**
+ * Collect CMA metrics from kPixelStatMm/<cma_type>/<metric>
+ * cma_type: CMA heap name
+ * metrics_info: This is a vector of MmMetricsInfo {metric, atom_key, update_diff}.
+ * Currently, we only collect CMA metrics defined in metrics_info
+ */
+std::map<std::string, uint64_t> MmMetricsReporter::readCmaStat(
+ const std::string &cma_type,
+ const std::vector<MmMetricsReporter::MmMetricsInfo> &metrics_info) {
+ uint64_t file_contents;
+ std::map<std::string, uint64_t> cma_stat;
+ for (auto &entry : metrics_info) {
+ std::string path = android::base::StringPrintf("%s/%s/%s", kPixelStatMm, cma_type.c_str(),
+ entry.name.c_str());
+ if (!ReadFileToUint(path.c_str(), &file_contents))
+ continue;
+ cma_stat[entry.name] = file_contents;
+ }
+ return cma_stat;
+}
+
+/**
+ * This function is to collect CMA metrics and upload them.
+ * The CMA metrics are collected by readCmaStat(), copied into atom values
+ * by fillAtomValues(), and then uploaded by reportVendorAtom(). The collected
+ * metrics will be stored in prev_cma_stat_ and prev_cma_stat_ext_ according
+ * to its CmaType.
+ *
+ * stats_client: The Stats service
+ * atom_id: The id of atom. It can be PixelAtoms::Ids::CMA_STATUS or CMA_STATUS_EXT
+ * cma_type: The name of CMA heap. We only collect metrics from CMA heaps defined
+ * in kCmaTypeInfo.
+ * type_idx: The id of the CMA heap. We add this id in atom values to identify
+ * the CMA status data.
+ * metrics_info: This is a vector of MmMetricsInfo {metric, atom_key, update_diff}.
+ * We only collect metrics defined in metrics_info from CMA heap path.
+ * all_prev_cma_stat: This is the CMA status collected last time.
+ * It is a map containing pairs of {type_idx, cma_stat}, and cma_stat is
+ * a map contains pairs of {metric, cur_value}.
+ * e.g. {CmaType::FARAWIMG, {"cma_alloc_pages_attempts", 100000}, {...}, ....}
+ * is collected from kPixelStatMm/farawimg/cma_alloc_pages_attempts
+ */
+void MmMetricsReporter::reportCmaStatusAtom(
+ const std::shared_ptr<IStats> &stats_client, int atom_id, const std::string &cma_type,
+ CmaType type_idx, const std::vector<MmMetricsInfo> &metrics_info,
+ std::map<CmaType, std::map<std::string, uint64_t>> *all_prev_cma_stat) {
+ std::map<std::string, uint64_t> cma_stat = readCmaStat(cma_type, metrics_info);
+ if (!cma_stat.empty()) {
+ std::vector<VendorAtomValue> values;
+ VendorAtomValue tmp;
+ tmp.set<VendorAtomValue::intValue>(type_idx);
+ values.push_back(tmp);
+
+ std::map<std::string, uint64_t> prev_cma_stat;
+ auto entry = all_prev_cma_stat->find(type_idx);
+ if (entry != all_prev_cma_stat->end())
+ prev_cma_stat = entry->second;
+
+ bool is_first_atom = (prev_cma_stat.size() == 0) ? true : false;
+ fillAtomValues(metrics_info, cma_stat, &prev_cma_stat, &values);
+ (*all_prev_cma_stat)[type_idx] = prev_cma_stat;
+ if (!is_first_atom)
+ reportVendorAtom(stats_client, atom_id, values, "CmaStatus");
+ }
+}
+
+/**
+ * Find the CMA heap defined in kCmaTypeInfo, and then call reportCmaStatusAtom()
+ * to collect the CMA metrics from kPixelStatMm/<cma_type> and upload them.
+ */
+void MmMetricsReporter::logCmaStatus(const std::shared_ptr<IStats> &stats_client) {
+ std::unique_ptr<DIR, int (*)(DIR *)> dir(opendir(kPixelStatMm), closedir);
+ if (!dir)
+ return;
+
+ while (struct dirent *dp = readdir(dir.get())) {
+ if (dp->d_type != DT_DIR)
+ continue;
+
+ std::string cma_type(dp->d_name);
+ auto type = kCmaTypeInfo.find(cma_type);
+ if (type == kCmaTypeInfo.end())
+ continue;
+
+ reportCmaStatusAtom(stats_client, PixelAtoms::Ids::CMA_STATUS, cma_type, type->second,
+ kCmaStatusInfo, &prev_cma_stat_);
+ reportCmaStatusAtom(stats_client, PixelAtoms::Ids::CMA_STATUS_EXT, cma_type, type->second,
+ kCmaStatusExtInfo, &prev_cma_stat_ext_);
}
}