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