1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "pixelstats: MmMetrics"
18 
19 #include <aidl/android/frameworks/stats/IStats.h>
20 #include <android-base/file.h>
21 #include <android-base/parseint.h>
22 #include <android-base/properties.h>
23 #include <android-base/stringprintf.h>
24 #include <android-base/strings.h>
25 #include <android/binder_manager.h>
26 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
27 #include <pixelstats/MmMetricsReporter.h>
28 #include <utils/Log.h>
29 
30 #define SZ_4K 0x00001000
31 #define SZ_2M 0x00200000
32 
33 namespace android {
34 namespace hardware {
35 namespace google {
36 namespace pixel {
37 
38 using aidl::android::frameworks::stats::IStats;
39 using aidl::android::frameworks::stats::VendorAtom;
40 using aidl::android::frameworks::stats::VendorAtomValue;
41 using android::base::ReadFileToString;
42 using android::base::StartsWith;
43 using android::hardware::google::pixel::PixelAtoms::CmaStatus;
44 using android::hardware::google::pixel::PixelAtoms::CmaStatusExt;
45 using android::hardware::google::pixel::PixelAtoms::PixelMmMetricsPerDay;
46 using android::hardware::google::pixel::PixelAtoms::PixelMmMetricsPerHour;
47 
48 const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kMmMetricsPerHourInfo = {
49         {"nr_free_pages", PixelMmMetricsPerHour::kFreePagesFieldNumber, false},
50         {"nr_anon_pages", PixelMmMetricsPerHour::kAnonPagesFieldNumber, false},
51         {"nr_file_pages", PixelMmMetricsPerHour::kFilePagesFieldNumber, false},
52         {"nr_slab_reclaimable", PixelMmMetricsPerHour::kSlabReclaimableFieldNumber, false},
53         {"nr_zspages", PixelMmMetricsPerHour::kZspagesFieldNumber, false},
54         {"nr_unevictable", PixelMmMetricsPerHour::kUnevictableFieldNumber, false},
55 };
56 
57 const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kMmMetricsPerDayInfo = {
58         {"workingset_refault", PixelMmMetricsPerDay::kWorkingsetRefaultFieldNumber, true},
59         {"workingset_refault_file", PixelMmMetricsPerDay::kWorkingsetRefaultFieldNumber, true},
60         {"pswpin", PixelMmMetricsPerDay::kPswpinFieldNumber, true},
61         {"pswpout", PixelMmMetricsPerDay::kPswpoutFieldNumber, true},
62         {"allocstall_dma", PixelMmMetricsPerDay::kAllocstallDmaFieldNumber, true},
63         {"allocstall_dma32", PixelMmMetricsPerDay::kAllocstallDma32FieldNumber, true},
64         {"allocstall_normal", PixelMmMetricsPerDay::kAllocstallNormalFieldNumber, true},
65         {"allocstall_movable", PixelMmMetricsPerDay::kAllocstallMovableFieldNumber, true},
66         {"pgalloc_dma", PixelMmMetricsPerDay::kPgallocDmaFieldNumber, true},
67         {"pgalloc_dma32", PixelMmMetricsPerDay::kPgallocDma32FieldNumber, true},
68         {"pgalloc_normal", PixelMmMetricsPerDay::kPgallocNormalFieldNumber, true},
69         {"pgalloc_movable", PixelMmMetricsPerDay::kPgallocMovableFieldNumber, true},
70         {"pgsteal_kswapd", PixelMmMetricsPerDay::kPgstealKswapdFieldNumber, true},
71         {"pgsteal_direct", PixelMmMetricsPerDay::kPgstealDirectFieldNumber, true},
72         {"pgscan_kswapd", PixelMmMetricsPerDay::kPgscanKswapdFieldNumber, true},
73         {"pgscan_direct", PixelMmMetricsPerDay::kPgscanDirectFieldNumber, true},
74         {"oom_kill", PixelMmMetricsPerDay::kOomKillFieldNumber, true},
75         {"pgalloc_costly_order", PixelMmMetricsPerDay::kPgallocHighFieldNumber, true},
76         {"pgcache_hit", PixelMmMetricsPerDay::kPgcacheHitFieldNumber, true},
77         {"pgcache_miss", PixelMmMetricsPerDay::kPgcacheMissFieldNumber, true},
78 };
79 
80 const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kCmaStatusInfo = {
81         {"alloc_pages_attempts", CmaStatus::kCmaAllocPagesAttemptsFieldNumber, true},
82         {"alloc_pages_failfast_attempts", CmaStatus::kCmaAllocPagesSoftAttemptsFieldNumber, true},
83         {"fail_pages", CmaStatus::kCmaFailPagesFieldNumber, true},
84         {"fail_failfast_pages", CmaStatus::kCmaFailSoftPagesFieldNumber, true},
85         {"migrated_pages", CmaStatus::kMigratedPagesFieldNumber, true},
86 };
87 
88 const std::vector<MmMetricsReporter::MmMetricsInfo> MmMetricsReporter::kCmaStatusExtInfo = {
89         {"latency_low", CmaStatusExt::kCmaAllocLatencyLowFieldNumber, false},
90         {"latency_mid", CmaStatusExt::kCmaAllocLatencyMidFieldNumber, false},
91         {"latency_high", CmaStatusExt::kCmaAllocLatencyHighFieldNumber, false},
92 };
93 
94 const std::map<std::string, MmMetricsReporter::CmaType> MmMetricsReporter::kCmaTypeInfo = {
95         {"farawimg", MmMetricsReporter::FARAWIMG},  {"faimg", MmMetricsReporter::FAIMG},
96         {"faceauth_tpu", MmMetricsReporter::FATPU}, {"faprev", MmMetricsReporter::FAPREV},
97         {"vframe", MmMetricsReporter::VFRAME},      {"vstream", MmMetricsReporter::VSTREAM},
98 };
99 
MmMetricsReporter()100 MmMetricsReporter::MmMetricsReporter()
101     : kVmstatPath("/proc/vmstat"),
102       kIonTotalPoolsPath("/sys/kernel/dma_heap/total_pools_kb"),
103       kIonTotalPoolsPathForLegacy("/sys/kernel/ion/total_pools_kb"),
104       kGpuTotalPages("/sys/kernel/pixel_stat/gpu/mem/total_page_count"),
105       kPixelStatMm("/sys/kernel/pixel_stat/mm") {}
106 
ReadFileToUint(const char * const path,uint64_t * val)107 bool MmMetricsReporter::ReadFileToUint(const char *const path, uint64_t *val) {
108     std::string file_contents;
109 
110     if (!ReadFileToString(path, &file_contents)) {
111         ALOGI("Unable to read %s - %s", path, strerror(errno));
112         return false;
113     } else {
114         file_contents = android::base::Trim(file_contents);
115         if (!android::base::ParseUint(file_contents, val)) {
116             ALOGI("Unable to convert %s to uint - %s", path, strerror(errno));
117             return false;
118         }
119     }
120     return true;
121 }
122 
reportVendorAtom(const std::shared_ptr<IStats> & stats_client,int atom_id,const std::vector<VendorAtomValue> & values,const std::string & atom_name)123 bool MmMetricsReporter::reportVendorAtom(const std::shared_ptr<IStats> &stats_client, int atom_id,
124                                          const std::vector<VendorAtomValue> &values,
125                                          const std::string &atom_name) {
126     // Send vendor atom to IStats HAL
127     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
128                         .atomId = atom_id,
129                         .values = std::move(values)};
130     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
131     if (!ret.isOk()) {
132         ALOGE("Unable to report %s to Stats service", atom_name.c_str());
133         return false;
134     }
135     return true;
136 }
137 
138 /**
139  * Parse the output of /proc/vmstat or the sysfs having the same output format.
140  * The map containing pairs of {field_string, data} will be returned.
141  */
readVmStat(const char * path)142 std::map<std::string, uint64_t> MmMetricsReporter::readVmStat(const char *path) {
143     std::string file_contents;
144     std::map<std::string, uint64_t> vmstat_data;
145 
146     if (path == nullptr) {
147         ALOGI("vmstat path is not specified");
148         return vmstat_data;
149     }
150 
151     if (!ReadFileToString(path, &file_contents)) {
152         ALOGE("Unable to read vmstat from %s, err: %s", path, strerror(errno));
153         return vmstat_data;
154     }
155 
156     std::istringstream data(file_contents);
157     std::string line;
158     while (std::getline(data, line)) {
159         std::vector<std::string> words = android::base::Split(line, " ");
160         if (words.size() != 2)
161             continue;
162 
163         uint64_t i;
164         if (!android::base::ParseUint(words[1], &i))
165             continue;
166 
167         vmstat_data[words[0]] = i;
168     }
169     return vmstat_data;
170 }
171 
getIonTotalPools()172 uint64_t MmMetricsReporter::getIonTotalPools() {
173     uint64_t res;
174 
175     if (!ReadFileToUint(kIonTotalPoolsPathForLegacy, &res) || (res == 0)) {
176         if (!ReadFileToUint(kIonTotalPoolsPath, &res)) {
177             return 0;
178         }
179     }
180 
181     return res;
182 }
183 
184 /**
185  * Collect GPU memory from kGpuTotalPages and return the total number of 4K page.
186  */
getGpuMemory()187 uint64_t MmMetricsReporter::getGpuMemory() {
188     uint64_t gpu_size = 0;
189 
190     if (!ReadFileToUint(kGpuTotalPages, &gpu_size)) {
191         return 0;
192     }
193     return gpu_size;
194 }
195 
196 /**
197  * fillAtomValues() is used to copy Mm metrics to values
198  * metrics_info: This is a vector of MmMetricsInfo {field_string, atom_key, update_diff}
199  *               field_string is used to get the data from mm_metrics.
200  *               atom_key is the position where the data should be put into values.
201  *               update_diff will be true if this is an accumulated data.
202  *               metrics_info may have multiple entries with the same atom_key,
203  *               e.g. workingset_refault and workingset_refault_file.
204  * mm_metrics: This map contains pairs of {field_string, cur_value} collected
205  *             from /proc/vmstat or the sysfs for the pixel specific metrics.
206  *             e.g. {"nr_free_pages", 200000}
207  *             Some data in mm_metrics are accumulated, e.g. pswpin.
208  *             We upload the difference instead of the accumulated value
209  *             when update_diff of the field is true.
210  * prev_mm_metrics: The pointer to the metrics we collected last time.
211  * atom_values: The atom values that will be reported later.
212  */
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)213 void MmMetricsReporter::fillAtomValues(const std::vector<MmMetricsInfo> &metrics_info,
214                                        const std::map<std::string, uint64_t> &mm_metrics,
215                                        std::map<std::string, uint64_t> *prev_mm_metrics,
216                                        std::vector<VendorAtomValue> *atom_values) {
217     VendorAtomValue tmp;
218     tmp.set<VendorAtomValue::longValue>(0);
219     // resize atom_values to add all fields defined in metrics_info
220     int max_idx = 0;
221     for (auto &entry : metrics_info) {
222         if (max_idx < entry.atom_key)
223             max_idx = entry.atom_key;
224     }
225     int size = max_idx - kVendorAtomOffset + 1;
226     if (atom_values->size() < size)
227         atom_values->resize(size, tmp);
228 
229     for (auto &entry : metrics_info) {
230         int atom_idx = entry.atom_key - kVendorAtomOffset;
231 
232         auto data = mm_metrics.find(entry.name);
233         if (data == mm_metrics.end())
234             continue;
235 
236         uint64_t cur_value = data->second;
237         uint64_t prev_value = 0;
238         if (prev_mm_metrics->size() != 0) {
239             auto prev_data = prev_mm_metrics->find(entry.name);
240             if (prev_data != prev_mm_metrics->end())
241                 prev_value = prev_data->second;
242         }
243 
244         if (entry.update_diff) {
245             tmp.set<VendorAtomValue::longValue>(cur_value - prev_value);
246         } else {
247             tmp.set<VendorAtomValue::longValue>(cur_value);
248         }
249         (*atom_values)[atom_idx] = tmp;
250     }
251     (*prev_mm_metrics) = mm_metrics;
252 }
253 
logPixelMmMetricsPerHour(const std::shared_ptr<IStats> & stats_client)254 void MmMetricsReporter::logPixelMmMetricsPerHour(const std::shared_ptr<IStats> &stats_client) {
255     // Currently, we collect these metrics and report this atom only for userdebug_or_eng
256     // We only grant permissions to access sysfs for userdebug_or_eng.
257     // Add a check to avoid unnecessary access.
258     if (android::base::GetProperty("ro.build.type", "") == "user")
259         return;
260 
261     std::map<std::string, uint64_t> vmstat = readVmStat(kVmstatPath);
262     if (vmstat.size() == 0)
263         return;
264 
265     uint64_t ion_total_pools = getIonTotalPools();
266     uint64_t gpu_memory = getGpuMemory();
267 
268     std::vector<VendorAtomValue> values;
269     bool is_first_atom = (prev_hour_vmstat_.size() == 0) ? true : false;
270     fillAtomValues(kMmMetricsPerHourInfo, vmstat, &prev_hour_vmstat_, &values);
271 
272     // resize values to add the following fields
273     VendorAtomValue tmp;
274     tmp.set<VendorAtomValue::longValue>(0);
275     int size = PixelMmMetricsPerHour::kGpuMemoryFieldNumber - kVendorAtomOffset + 1;
276     if (values.size() < size) {
277         values.resize(size, tmp);
278     }
279     tmp.set<VendorAtomValue::longValue>(ion_total_pools);
280     values[PixelMmMetricsPerHour::kIonTotalPoolsFieldNumber - kVendorAtomOffset] = tmp;
281     tmp.set<VendorAtomValue::longValue>(gpu_memory);
282     values[PixelMmMetricsPerHour::kGpuMemoryFieldNumber - kVendorAtomOffset] = tmp;
283 
284     // Don't report the first atom to avoid big spike in accumulated values.
285     if (!is_first_atom) {
286         // Send vendor atom to IStats HAL
287         reportVendorAtom(stats_client, PixelAtoms::Atom::kPixelMmMetricsPerHour, values,
288                          "PixelMmMetricsPerHour");
289     }
290 }
291 
logPixelMmMetricsPerDay(const std::shared_ptr<IStats> & stats_client)292 void MmMetricsReporter::logPixelMmMetricsPerDay(const std::shared_ptr<IStats> &stats_client) {
293     // Currently, we collect these metrics and report this atom only for userdebug_or_eng
294     // We only grant permissions to access sysfs for userdebug_or_eng.
295     // Add a check to avoid unnecessary access.
296     if (android::base::GetProperty("ro.build.type", "") == "user")
297         return;
298 
299     std::map<std::string, uint64_t> vmstat = readVmStat(kVmstatPath);
300     if (vmstat.size() == 0)
301         return;
302 
303     std::vector<VendorAtomValue> values;
304     bool is_first_atom = (prev_day_vmstat_.size() == 0) ? true : false;
305     fillAtomValues(kMmMetricsPerDayInfo, vmstat, &prev_day_vmstat_, &values);
306 
307     std::map<std::string, uint64_t> pixel_vmstat =
308             readVmStat(android::base::StringPrintf("%s/vmstat", kPixelStatMm).c_str());
309     fillAtomValues(kMmMetricsPerDayInfo, pixel_vmstat, &prev_day_pixel_vmstat_, &values);
310     fillProcessStime(PixelMmMetricsPerDay::kKswapdStimeClksFieldNumber, "kswapd0", &kswapd_pid_,
311                      &prev_kswapd_stime_, &values);
312     fillProcessStime(PixelMmMetricsPerDay::kKcompactdStimeClksFieldNumber, "kcompactd0",
313                      &kcompactd_pid_, &prev_kcompactd_stime_, &values);
314 
315     // Don't report the first atom to avoid big spike in accumulated values.
316     if (!is_first_atom) {
317         // Send vendor atom to IStats HAL
318         reportVendorAtom(stats_client, PixelAtoms::Atom::kPixelMmMetricsPerDay, values,
319                          "PixelMmMetricsPerDay");
320     }
321 }
322 
323 /**
324  * Check if /proc/<pid>/comm is equal to name.
325  */
isValidPid(int pid,const char * name)326 bool MmMetricsReporter::isValidPid(int pid, const char *name) {
327     if (pid <= 0)
328         return false;
329 
330     std::string file_contents;
331     std::string path = android::base::StringPrintf("/proc/%d/comm", pid);
332     if (!ReadFileToString(path, &file_contents)) {
333         ALOGI("Unable to read %s, err: %s", path.c_str(), strerror(errno));
334         return false;
335     }
336 
337     file_contents = android::base::Trim(file_contents);
338     return !file_contents.compare(name);
339 }
340 
341 /**
342  * Return pid if /proc/<pid>/comm is equal to name, or -1 if not found.
343  */
findPidByProcessName(const char * name)344 int MmMetricsReporter::findPidByProcessName(const char *name) {
345     std::unique_ptr<DIR, int (*)(DIR *)> dir(opendir("/proc"), closedir);
346     if (!dir)
347         return -1;
348 
349     int pid;
350     while (struct dirent *dp = readdir(dir.get())) {
351         if (dp->d_type != DT_DIR)
352             continue;
353 
354         if (!android::base::ParseInt(dp->d_name, &pid))
355             continue;
356 
357         // Avoid avc denial since pixelstats-vendor doesn't have the permission to access /proc/1
358         if (pid == 1)
359             continue;
360 
361         std::string file_contents;
362         std::string path = android::base::StringPrintf("/proc/%s/comm", dp->d_name);
363         if (!ReadFileToString(path, &file_contents))
364             continue;
365 
366         file_contents = android::base::Trim(file_contents);
367         if (file_contents.compare(name))
368             continue;
369 
370         return pid;
371     }
372     return -1;
373 }
374 
375 /**
376  * Get stime of a process from /proc/<pid>/stat
377  * stime is the 15th field.
378  */
getStimeByPid(int pid)379 uint64_t MmMetricsReporter::getStimeByPid(int pid) {
380     const int stime_idx = 15;
381     uint64_t stime;
382     std::string file_contents;
383     std::string path = android::base::StringPrintf("/proc/%d/stat", pid);
384     if (!ReadFileToString(path, &file_contents)) {
385         ALOGI("Unable to read %s, err: %s", path.c_str(), strerror(errno));
386         return false;
387     }
388 
389     std::vector<std::string> data = android::base::Split(file_contents, " ");
390     if (data.size() < stime_idx) {
391         ALOGI("Unable to find stime from %s. size: %lu", path.c_str(), data.size());
392         return false;
393     }
394 
395     if (android::base::ParseUint(data[stime_idx - 1], &stime))
396         return stime;
397     else
398         return 0;
399 }
400 
401 /**
402  * Find stime of the process and copy it into atom_values
403  * atom_key: Currently, it can only be kKswapdTimeFieldNumber or kKcompactdTimeFieldNumber
404  * name: process name
405  * pid: The pid of the process. It would be the pid we found last time,
406  *      or -1 if not found.
407  * prev_stime: The stime of the process collected last time.
408  * atom_values: The atom we will report later.
409  */
fillProcessStime(int atom_key,const char * name,int * pid,uint64_t * prev_stime,std::vector<VendorAtomValue> * atom_values)410 void MmMetricsReporter::fillProcessStime(int atom_key, const char *name, int *pid,
411                                          uint64_t *prev_stime,
412                                          std::vector<VendorAtomValue> *atom_values) {
413     // resize atom_values if there is no space for this stime field.
414     int atom_idx = atom_key - kVendorAtomOffset;
415     int size = atom_idx + 1;
416     VendorAtomValue tmp;
417     tmp.set<VendorAtomValue::longValue>(0);
418     if (atom_values->size() < size)
419         atom_values->resize(size, tmp);
420 
421     if (!isValidPid(*pid, name)) {
422         (*pid) = findPidByProcessName(name);
423         if ((*pid) <= 0) {
424             ALOGI("Unable to find pid of %s, err: %s", name, strerror(errno));
425             return;
426         }
427     }
428 
429     uint64_t stime = getStimeByPid(*pid);
430     tmp.set<VendorAtomValue::longValue>(stime - *prev_stime);
431     (*atom_values)[atom_idx] = tmp;
432     (*prev_stime) = stime;
433 }
434 
435 /**
436  * Collect CMA metrics from kPixelStatMm/cma/<cma_type>/<metric>
437  * cma_type: CMA heap name
438  * metrics_info: This is a vector of MmMetricsInfo {metric, atom_key, update_diff}.
439  *               Currently, we only collect CMA metrics defined in metrics_info
440  */
readCmaStat(const std::string & cma_type,const std::vector<MmMetricsReporter::MmMetricsInfo> & metrics_info)441 std::map<std::string, uint64_t> MmMetricsReporter::readCmaStat(
442         const std::string &cma_type,
443         const std::vector<MmMetricsReporter::MmMetricsInfo> &metrics_info) {
444     uint64_t file_contents;
445     std::map<std::string, uint64_t> cma_stat;
446     for (auto &entry : metrics_info) {
447         std::string path = android::base::StringPrintf("%s/cma/%s/%s", kPixelStatMm,
448                                                        cma_type.c_str(), entry.name.c_str());
449         if (!ReadFileToUint(path.c_str(), &file_contents))
450             continue;
451         cma_stat[entry.name] = file_contents;
452     }
453     return cma_stat;
454 }
455 
456 /**
457  * This function is to collect CMA metrics and upload them.
458  * The CMA metrics are collected by readCmaStat(), copied into atom values
459  * by fillAtomValues(), and then uploaded by reportVendorAtom(). The collected
460  * metrics will be stored in prev_cma_stat_ and prev_cma_stat_ext_ according
461  * to its CmaType.
462  *
463  * stats_client: The Stats service
464  * atom_id: The id of atom. It can be PixelAtoms::Atom::kCmaStatus or kCmaStatusExt
465  * cma_type: The name of CMA heap. We only collect metrics from CMA heaps defined
466  *           in kCmaTypeInfo.
467  * type_idx: The id of the CMA heap. We add this id in atom values to identify
468  *           the CMA status data.
469  * metrics_info: This is a vector of MmMetricsInfo {metric, atom_key, update_diff}.
470  *               We only collect metrics defined in metrics_info from CMA heap path.
471  * all_prev_cma_stat: This is the CMA status collected last time.
472  *                    It is a map containing pairs of {type_idx, cma_stat}, and cma_stat is
473  *                    a map contains pairs of {metric, cur_value}.
474  *                    e.g. {CmaType::FARAWIMG, {"alloc_pages_attempts", 100000}, {...}, ....}
475  *                    is collected from kPixelStatMm/cma/farawimg/alloc_pages_attempts
476  */
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)477 void MmMetricsReporter::reportCmaStatusAtom(
478         const std::shared_ptr<IStats> &stats_client, int atom_id, const std::string &cma_type,
479         CmaType type_idx, const std::vector<MmMetricsInfo> &metrics_info,
480         std::map<CmaType, std::map<std::string, uint64_t>> *all_prev_cma_stat) {
481     std::map<std::string, uint64_t> cma_stat = readCmaStat(cma_type, metrics_info);
482     if (!cma_stat.empty()) {
483         std::vector<VendorAtomValue> values;
484         VendorAtomValue tmp;
485         tmp.set<VendorAtomValue::intValue>(type_idx);
486         values.push_back(tmp);
487 
488         std::map<std::string, uint64_t> prev_cma_stat;
489         auto entry = all_prev_cma_stat->find(type_idx);
490         if (entry != all_prev_cma_stat->end())
491             prev_cma_stat = entry->second;
492 
493         bool is_first_atom = (prev_cma_stat.size() == 0) ? true : false;
494         fillAtomValues(metrics_info, cma_stat, &prev_cma_stat, &values);
495         (*all_prev_cma_stat)[type_idx] = prev_cma_stat;
496         if (!is_first_atom)
497             reportVendorAtom(stats_client, atom_id, values, "CmaStatus");
498     }
499 }
500 
501 /**
502  * Find the CMA heap defined in kCmaTypeInfo, and then call reportCmaStatusAtom()
503  * to collect the CMA metrics from kPixelStatMm/cma/<cma_type> and upload them.
504  */
logCmaStatus(const std::shared_ptr<IStats> & stats_client)505 void MmMetricsReporter::logCmaStatus(const std::shared_ptr<IStats> &stats_client) {
506     std::string cma_root = android::base::StringPrintf("%s/cma", kPixelStatMm);
507     std::unique_ptr<DIR, int (*)(DIR *)> dir(opendir(cma_root.c_str()), closedir);
508     if (!dir)
509         return;
510 
511     while (struct dirent *dp = readdir(dir.get())) {
512         if (dp->d_type != DT_DIR)
513             continue;
514 
515         std::string cma_type(dp->d_name);
516         auto type = kCmaTypeInfo.find(cma_type);
517         if (type == kCmaTypeInfo.end())
518             continue;
519 
520         reportCmaStatusAtom(stats_client, PixelAtoms::Atom::kCmaStatus, cma_type, type->second,
521                             kCmaStatusInfo, &prev_cma_stat_);
522         reportCmaStatusAtom(stats_client, PixelAtoms::Atom::kCmaStatusExt, cma_type, type->second,
523                             kCmaStatusExtInfo, &prev_cma_stat_ext_);
524     }
525 }
526 
527 }  // namespace pixel
528 }  // namespace google
529 }  // namespace hardware
530 }  // namespace android
531