diff options
author | Chiawei Wang <chiaweiwang@google.com> | 2021-03-09 22:01:47 +0800 |
---|---|---|
committer | Chiawei Wang <chiaweiwang@google.com> | 2021-03-25 02:28:12 +0800 |
commit | 588e3281e9d60dafa25ba10a95a61401c38f1286 (patch) | |
tree | 61f18118a13f06b8223a9778a93211ce1316faff /pixelstats/MmMetricsReporter.cpp | |
parent | 5db67e6a7e91ccfc9e7d08b2b9bbf810fc59f6bb (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.cpp | 328 |
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_); } } |