• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 #include <errno.h>
18 #include <fcntl.h>
19 #include <inttypes.h>
20 #include <linux/kernel-page-flags.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 
24 #include <atomic>
25 #include <fstream>
26 #include <iostream>
27 #include <memory>
28 #include <string>
29 #include <utility>
30 #include <vector>
31 
32 #include <android-base/file.h>
33 #include <android-base/logging.h>
34 #include <android-base/stringprintf.h>
35 #include <android-base/strings.h>
36 #include <android-base/unique_fd.h>
37 #include <procinfo/process_map.h>
38 
39 #include "meminfo_private.h"
40 
41 namespace android {
42 namespace meminfo {
43 
44 // List of VMA names that we don't want to process:
45 //   - On ARM32, [vectors] is a special VMA that is outside of pagemap range.
46 //   - On x86, [vsyscall] is a kernel memory that is outside of pagemap range.
47 static const std::vector<std::string> g_excluded_vmas = {
48     "[vectors]",
49 #ifdef __x86_64__
50     "[vsyscall]"
51 #endif
52 };
53 
add_mem_usage(MemUsage * to,const MemUsage & from)54 static void add_mem_usage(MemUsage* to, const MemUsage& from) {
55     to->vss += from.vss;
56     to->rss += from.rss;
57     to->pss += from.pss;
58     to->uss += from.uss;
59 
60     to->swap += from.swap;
61 
62     to->private_clean += from.private_clean;
63     to->private_dirty += from.private_dirty;
64 
65     to->shared_clean += from.shared_clean;
66     to->shared_dirty += from.shared_dirty;
67 }
68 
69 // Converts MemUsage stats from KB to B in case usage is expected in bytes.
convert_usage_kb_to_b(MemUsage & usage)70 static void convert_usage_kb_to_b(MemUsage& usage) {
71     // These stats are only populated if /proc/<pid>/smaps is read, so they are excluded:
72     // swap_pss, anon_huge_pages, shmem_pmdmapped, file_pmd_mapped, shared_hugetlb, private_hugetlb.
73     constexpr int conversion_factor = 1024;
74     usage.vss *= conversion_factor;
75     usage.rss *= conversion_factor;
76     usage.pss *= conversion_factor;
77     usage.uss *= conversion_factor;
78 
79     usage.swap *= conversion_factor;
80 
81     usage.private_clean *= conversion_factor;
82     usage.private_dirty *= conversion_factor;
83 
84     usage.shared_clean *= conversion_factor;
85     usage.shared_dirty *= conversion_factor;
86 
87     usage.thp *= conversion_factor;
88 }
89 
90 // Returns true if the line was valid smaps stats line false otherwise.
parse_smaps_field(const char * line,MemUsage * stats)91 static bool parse_smaps_field(const char* line, MemUsage* stats) {
92     const char *end = line;
93 
94     // https://lore.kernel.org/patchwork/patch/1088579/ introduced tabs. Handle this case as well.
95     while (*end && !isspace(*end)) end++;
96     if (*end && end > line && *(end - 1) == ':') {
97         const char* c = end;
98         while (isspace(*c)) c++;
99         switch (line[0]) {
100             case 'P':
101                 if (strncmp(line, "Pss:", 4) == 0) {
102                     stats->pss = strtoull(c, nullptr, 10);
103                 } else if (strncmp(line, "Private_Clean:", 14) == 0) {
104                     uint64_t prcl = strtoull(c, nullptr, 10);
105                     stats->private_clean = prcl;
106                     stats->uss += prcl;
107                 } else if (strncmp(line, "Private_Dirty:", 14) == 0) {
108                     uint64_t prdi = strtoull(c, nullptr, 10);
109                     stats->private_dirty = prdi;
110                     stats->uss += prdi;
111                 } else if (strncmp(line, "Private_Hugetlb:", 16) == 0) {
112                     stats->private_hugetlb = strtoull(c, nullptr, 10);
113                 }
114                 break;
115             case 'S':
116                 if (strncmp(line, "Size:", 5) == 0) {
117                     stats->vss = strtoull(c, nullptr, 10);
118                 } else if (strncmp(line, "Shared_Clean:", 13) == 0) {
119                     stats->shared_clean = strtoull(c, nullptr, 10);
120                 } else if (strncmp(line, "Shared_Dirty:", 13) == 0) {
121                     stats->shared_dirty = strtoull(c, nullptr, 10);
122                 } else if (strncmp(line, "Swap:", 5) == 0) {
123                     stats->swap = strtoull(c, nullptr, 10);
124                 } else if (strncmp(line, "SwapPss:", 8) == 0) {
125                     stats->swap_pss = strtoull(c, nullptr, 10);
126                 } else if (strncmp(line, "ShmemPmdMapped:", 15) == 0) {
127                     stats->shmem_pmd_mapped = strtoull(c, nullptr, 10);
128                 } else if (strncmp(line, "Shared_Hugetlb:", 15) == 0) {
129                     stats->shared_hugetlb = strtoull(c, nullptr, 10);
130                 }
131                 break;
132             case 'R':
133                 if (strncmp(line, "Rss:", 4) == 0) {
134                     stats->rss = strtoull(c, nullptr, 10);
135                 }
136                 break;
137             case 'A':
138                 if (strncmp(line, "AnonHugePages:", 14) == 0) {
139                     stats->anon_huge_pages = strtoull(c, nullptr, 10);
140                 }
141                 break;
142             case 'F':
143                 if (strncmp(line, "FilePmdMapped:", 14) == 0) {
144                     stats->file_pmd_mapped = strtoull(c, nullptr, 10);
145                 }
146                 break;
147             case 'L':
148                 if (strncmp(line, "Locked:", 7) == 0) {
149                     stats->locked = strtoull(c, nullptr, 10);
150                 }
151                 break;
152         }
153         return true;
154     }
155 
156     return false;
157 }
158 
ResetWorkingSet(pid_t pid)159 bool ProcMemInfo::ResetWorkingSet(pid_t pid) {
160     std::string clear_refs_path = ::android::base::StringPrintf("/proc/%d/clear_refs", pid);
161     if (!::android::base::WriteStringToFile("1\n", clear_refs_path)) {
162         PLOG(ERROR) << "Failed to write to " << clear_refs_path;
163         return false;
164     }
165 
166     return true;
167 }
168 
ProcMemInfo(pid_t pid,bool get_wss,uint64_t pgflags,uint64_t pgflags_mask)169 ProcMemInfo::ProcMemInfo(pid_t pid, bool get_wss, uint64_t pgflags, uint64_t pgflags_mask)
170     : pid_(pid), get_wss_(get_wss), pgflags_(pgflags), pgflags_mask_(pgflags_mask) {}
171 
Maps()172 const std::vector<Vma>& ProcMemInfo::Maps() {
173     if (maps_.empty() && !ReadMaps(get_wss_)) {
174         LOG(ERROR) << "Failed to read maps for Process " << pid_;
175     }
176 
177     return maps_;
178 }
179 
MapsWithPageIdle()180 const std::vector<Vma>& ProcMemInfo::MapsWithPageIdle() {
181     if (maps_.empty() && !ReadMaps(get_wss_, true)) {
182         LOG(ERROR) << "Failed to read maps with page idle for Process " << pid_;
183     }
184 
185     return maps_;
186 }
187 
MapsWithoutUsageStats()188 const std::vector<Vma>& ProcMemInfo::MapsWithoutUsageStats() {
189     if (maps_.empty() && !ReadMaps(get_wss_, false, false)) {
190         LOG(ERROR) << "Failed to read maps for Process " << pid_;
191     }
192 
193     return maps_;
194 }
195 
GetPagemapFd(pid_t pid)196 static int GetPagemapFd(pid_t pid) {
197     std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid);
198     int fd = TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC));
199     if (fd == -1) {
200         PLOG(ERROR) << "Failed to open " << pagemap_file;
201     }
202     return fd;
203 }
204 
Smaps(const std::string & path,bool collect_usage,bool collect_swap_offsets)205 const std::vector<Vma>& ProcMemInfo::Smaps(const std::string& path, bool collect_usage,
206                                            bool collect_swap_offsets) {
207     if (!maps_.empty()) {
208         return maps_;
209     }
210 
211     ::android::base::unique_fd pagemap_fd;
212     if (collect_swap_offsets) {
213         pagemap_fd = ::android::base::unique_fd(GetPagemapFd(pid_));
214         if (pagemap_fd == -1) {
215             LOG(ERROR) << "Failed to open pagemap for pid " << pid_ << " during Smaps()";
216             return maps_;
217         }
218     }
219 
220     auto collect_vmas = [&](Vma& vma) {
221         if (std::find(g_excluded_vmas.begin(), g_excluded_vmas.end(), vma.name) ==
222                 g_excluded_vmas.end()) {
223             maps_.emplace_back(vma);
224             if (collect_usage) {
225                 add_mem_usage(&usage_, vma.usage);
226             }
227             if (collect_swap_offsets &&
228                 !ReadVmaStats(pagemap_fd.get(), vma, false, false, false, false)) {
229                 LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start
230                            << "-" << vma.end << "]";
231                 return false;
232             }
233         }
234         return true;
235     };
236 
237     if (path.empty() && !ForEachVma(collect_vmas)) {
238         LOG(ERROR) << "Failed to read smaps for Process " << pid_;
239         maps_.clear();
240     }
241 
242     if (!path.empty() && !ForEachVmaFromFile(path, collect_vmas)) {
243         LOG(ERROR) << "Failed to read smaps from file " << path;
244         maps_.clear();
245     }
246 
247     return maps_;
248 }
249 
Usage()250 const MemUsage& ProcMemInfo::Usage() {
251     if (get_wss_) {
252         LOG(WARNING) << "Trying to read process memory usage for " << pid_
253                      << " using invalid object";
254         return usage_;
255     }
256 
257     if (maps_.empty() && !ReadMaps(get_wss_)) {
258         LOG(ERROR) << "Failed to get memory usage for Process " << pid_;
259     }
260 
261     return usage_;
262 }
263 
Wss()264 const MemUsage& ProcMemInfo::Wss() {
265     if (!get_wss_) {
266         LOG(WARNING) << "Trying to read process working set for " << pid_
267                      << " using invalid object";
268         return usage_;
269     }
270 
271     if (maps_.empty() && !ReadMaps(get_wss_)) {
272         LOG(ERROR) << "Failed to get working set for Process " << pid_;
273     }
274 
275     return usage_;
276 }
277 
ForEachVma(const VmaCallback & callback,bool use_smaps)278 bool ProcMemInfo::ForEachVma(const VmaCallback& callback, bool use_smaps) {
279     std::string path =
280             ::android::base::StringPrintf("/proc/%d/%s", pid_, use_smaps ? "smaps" : "maps");
281     return ForEachVmaFromFile(path, callback, use_smaps);
282 }
283 
ForEachExistingVma(const VmaCallback & callback)284 bool ProcMemInfo::ForEachExistingVma(const VmaCallback& callback) {
285     if (maps_.empty()) {
286         return false;
287     }
288     for (auto& vma : maps_) {
289         if (!callback(vma)) {
290             return false;
291         }
292     }
293     return true;
294 }
295 
ForEachVmaFromMaps(const VmaCallback & callback)296 bool ProcMemInfo::ForEachVmaFromMaps(const VmaCallback& callback) {
297     Vma vma;
298     auto vmaCollect = [&callback,&vma](const uint64_t start, uint64_t end, uint16_t flags,
299                             uint64_t pgoff, ino_t inode, const char* name, bool shared) {
300         vma.start = start;
301         vma.end = end;
302         vma.flags = flags;
303         vma.offset = pgoff;
304         vma.name = name;
305         vma.inode = inode;
306         vma.is_shared = shared;
307         callback(vma);
308     };
309 
310     bool success = ::android::procinfo::ReadProcessMaps(pid_, vmaCollect);
311 
312     return success;
313 }
314 
ForEachVmaFromMaps(const VmaCallback & callback,std::string & mapsBuffer)315 bool ProcMemInfo::ForEachVmaFromMaps(const VmaCallback& callback, std::string& mapsBuffer) {
316     Vma vma;
317     vma.name.reserve(256);
318     auto vmaCollect = [&callback,&vma](const uint64_t start, uint64_t end, uint16_t flags,
319                             uint64_t pgoff, ino_t inode, const char* name, bool shared) {
320         vma.start = start;
321         vma.end = end;
322         vma.flags = flags;
323         vma.offset = pgoff;
324         vma.name = name;
325         vma.inode = inode;
326         vma.is_shared = shared;
327         callback(vma);
328     };
329 
330     bool success = ::android::procinfo::ReadProcessMaps(pid_, vmaCollect, mapsBuffer);
331 
332     return success;
333 }
334 
SmapsOrRollup(MemUsage * stats) const335 bool ProcMemInfo::SmapsOrRollup(MemUsage* stats) const {
336     std::string path = ::android::base::StringPrintf(
337             "/proc/%d/%s", pid_, IsSmapsRollupSupported() ? "smaps_rollup" : "smaps");
338     return SmapsOrRollupFromFile(path, stats);
339 }
340 
SmapsOrRollupPss(uint64_t * pss) const341 bool ProcMemInfo::SmapsOrRollupPss(uint64_t* pss) const {
342     std::string path = ::android::base::StringPrintf(
343             "/proc/%d/%s", pid_, IsSmapsRollupSupported() ? "smaps_rollup" : "smaps");
344     return SmapsOrRollupPssFromFile(path, pss);
345 }
346 
StatusVmRSS(uint64_t * rss) const347 bool ProcMemInfo::StatusVmRSS(uint64_t* rss) const {
348     std::string path = ::android::base::StringPrintf("/proc/%d/status", pid_);
349     return StatusVmRSSFromFile(path, rss);
350 }
351 
SwapOffsets()352 const std::vector<uint64_t>& ProcMemInfo::SwapOffsets() {
353     if (get_wss_) {
354         LOG(WARNING) << "Trying to read process swap offsets for " << pid_
355                      << " using invalid object";
356         return swap_offsets_;
357     }
358 
359     if (maps_.empty() && !ReadMaps(get_wss_, false, true, false)) {
360         LOG(ERROR) << "Failed to get swap offsets for Process " << pid_;
361     }
362 
363     return swap_offsets_;
364 }
365 
PageMap(const Vma & vma,std::vector<uint64_t> * pagemap)366 bool ProcMemInfo::PageMap(const Vma& vma, std::vector<uint64_t>* pagemap) {
367     pagemap->clear();
368     std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
369     ::android::base::unique_fd pagemap_fd(
370             TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
371     if (pagemap_fd == -1) {
372         PLOG(ERROR) << "Failed to open " << pagemap_file;
373         return false;
374     }
375 
376     uint64_t nr_pages = (vma.end - vma.start) / getpagesize();
377     pagemap->resize(nr_pages);
378 
379     size_t bytes_to_read = sizeof(uint64_t) * nr_pages;
380     off64_t start_addr = (vma.start / getpagesize()) * sizeof(uint64_t);
381     ssize_t bytes_read = pread64(pagemap_fd, pagemap->data(), bytes_to_read, start_addr);
382     if (bytes_read == -1) {
383         PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
384         return false;
385     } else if (static_cast<size_t>(bytes_read) != bytes_to_read) {
386         LOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_
387                    << ": read bytes " << bytes_read << " expected bytes " << bytes_to_read;
388         return false;
389     }
390 
391     return true;
392 }
393 
ReadMaps(bool get_wss,bool use_pageidle,bool get_usage_stats,bool update_mem_usage)394 bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle, bool get_usage_stats,
395                            bool update_mem_usage) {
396     // Each object reads /proc/<pid>/maps only once. This is done to make sure programs that are
397     // running for the lifetime of the system can recycle the objects and don't have to
398     // unnecessarily retain and update this object in memory (which can get significantly large).
399     // E.g. A program that only needs to reset the working set will never all ->Maps(), ->Usage().
400     // E.g. A program that is monitoring smaps_rollup, may never call ->maps(), Usage(), so it
401     // doesn't make sense for us to parse and retain unnecessary memory accounting stats by default.
402     if (!maps_.empty()) return true;
403 
404     // parse and read /proc/<pid>/maps
405     std::string maps_file = ::android::base::StringPrintf("/proc/%d/maps", pid_);
406     if (!::android::procinfo::ReadMapFile(
407                 maps_file, [&](const android::procinfo::MapInfo& mapinfo) {
408                     if (std::find(g_excluded_vmas.begin(), g_excluded_vmas.end(), mapinfo.name) ==
409                             g_excluded_vmas.end()) {
410                       maps_.emplace_back(Vma(mapinfo.start, mapinfo.end,
411                                              mapinfo.pgoff, mapinfo.flags,
412                                              mapinfo.name,
413                                              mapinfo.inode, mapinfo.shared));
414                     }
415                 })) {
416         LOG(ERROR) << "Failed to parse " << maps_file;
417         maps_.clear();
418         return false;
419     }
420 
421     if (!get_usage_stats) {
422         return true;
423     }
424 
425     if (!GetUsageStats(get_wss, use_pageidle, update_mem_usage)) {
426         maps_.clear();
427         return false;
428     }
429     return true;
430 }
431 
GetUsageStats(bool get_wss,bool use_pageidle,bool update_mem_usage)432 bool ProcMemInfo::GetUsageStats(bool get_wss, bool use_pageidle, bool update_mem_usage) {
433     ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_));
434     if (pagemap_fd == -1) {
435         return false;
436     }
437 
438     for (auto& vma : maps_) {
439         if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss, use_pageidle, update_mem_usage, true)) {
440             LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
441                        << vma.end << "]";
442             return false;
443         }
444         add_mem_usage(&usage_, vma.usage);
445     }
446 
447     return true;
448 }
449 
FillInVmaStats(Vma & vma,bool use_kb)450 bool ProcMemInfo::FillInVmaStats(Vma& vma, bool use_kb) {
451     ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_));
452     if (pagemap_fd == -1) {
453         return false;
454     }
455 
456     if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss_, false, true, true)) {
457         LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
458                    << vma.end << "]";
459         return false;
460     }
461     if (!use_kb) {
462         convert_usage_kb_to_b(vma.usage);
463     }
464     return true;
465 }
466 
ReadVmaStats(int pagemap_fd,Vma & vma,bool get_wss,bool use_pageidle,bool update_mem_usage,bool update_swap_usage)467 bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle,
468                                bool update_mem_usage, bool update_swap_usage) {
469     PageAcct& pinfo = PageAcct::Instance();
470     if (get_wss && use_pageidle && !pinfo.InitPageAcct(true)) {
471         LOG(ERROR) << "Failed to init idle page accounting";
472         return false;
473     }
474 
475     uint64_t pagesz_kb = getpagesize() / 1024;
476     size_t num_pages = (vma.end - vma.start) / getpagesize();
477     size_t first_page = vma.start / getpagesize();
478 
479     std::vector<uint64_t> page_cache;
480     size_t cur_page_cache_index = 0;
481     size_t num_in_page_cache = 0;
482     size_t num_leftover_pages = num_pages;
483     for (size_t cur_page = first_page; cur_page < first_page + num_pages; ++cur_page) {
484         // Cache page map data.
485         if (cur_page_cache_index == num_in_page_cache) {
486             static constexpr size_t kMaxPages = 2048;
487             num_leftover_pages -= num_in_page_cache;
488             if (num_leftover_pages > kMaxPages) {
489                 num_in_page_cache = kMaxPages;
490             } else {
491                 num_in_page_cache = num_leftover_pages;
492             }
493             page_cache.resize(num_in_page_cache);
494             size_t total_bytes = page_cache.size() * sizeof(uint64_t);
495             ssize_t bytes = pread64(pagemap_fd, page_cache.data(), total_bytes,
496                                     cur_page * sizeof(uint64_t));
497             if (bytes != total_bytes) {
498                 if (bytes == -1) {
499                     PLOG(ERROR) << "Failed to read page data at offset 0x" << std::hex
500                                 << cur_page * sizeof(uint64_t);
501                 } else {
502                     LOG(ERROR) << "Failed to read page data at offset 0x" << std::hex
503                                << cur_page * sizeof(uint64_t) << std::dec << " read bytes " << bytes
504                                << " expected bytes " << total_bytes;
505                 }
506                 return false;
507             }
508             cur_page_cache_index = 0;
509         }
510 
511         uint64_t page_info = page_cache[cur_page_cache_index++];
512         if (!PAGE_PRESENT(page_info) && !PAGE_SWAPPED(page_info)) continue;
513 
514         if (PAGE_SWAPPED(page_info)) {
515             if (update_swap_usage) {
516                 vma.usage.swap += pagesz_kb;
517             }
518             swap_offsets_.emplace_back(PAGE_SWAP_OFFSET(page_info));
519             continue;
520         }
521 
522         if (!update_mem_usage) continue;
523 
524         uint64_t page_frame = PAGE_PFN(page_info);
525         uint64_t cur_page_flags;
526         if (!pinfo.PageFlags(page_frame, &cur_page_flags)) {
527             LOG(ERROR) << "Failed to get page flags for " << page_frame << " in process " << pid_;
528             swap_offsets_.clear();
529             return false;
530         }
531 
532         if (KPAGEFLAG_THP(cur_page_flags)) {
533             vma.usage.thp += pagesz_kb;
534         }
535 
536         // skip unwanted pages from the count
537         if ((cur_page_flags & pgflags_mask_) != pgflags_) continue;
538 
539         uint64_t cur_page_counts;
540         if (!pinfo.PageMapCount(page_frame, &cur_page_counts)) {
541             LOG(ERROR) << "Failed to get page count for " << page_frame << " in process " << pid_;
542             swap_offsets_.clear();
543             return false;
544         }
545 
546         // Page was unmapped between the presence check at the beginning of the loop and here.
547         if (cur_page_counts == 0) {
548             continue;
549         }
550 
551         bool is_dirty = !!(cur_page_flags & (1 << KPF_DIRTY));
552         bool is_private = (cur_page_counts == 1);
553         // Working set
554         if (get_wss) {
555             bool is_referenced = use_pageidle ? (pinfo.IsPageIdle(page_frame) == 1)
556                                               : !!(cur_page_flags & (1 << KPF_REFERENCED));
557             if (!is_referenced) {
558                 continue;
559             }
560             // This effectively makes vss = rss for the working set is requested.
561             // The libpagemap implementation returns vss > rss for
562             // working set, which doesn't make sense.
563             vma.usage.vss += pagesz_kb;
564         }
565 
566         vma.usage.rss += pagesz_kb;
567         vma.usage.uss += is_private ? pagesz_kb : 0;
568         vma.usage.pss += pagesz_kb / cur_page_counts;
569         if (is_private) {
570             vma.usage.private_dirty += is_dirty ? pagesz_kb : 0;
571             vma.usage.private_clean += is_dirty ? 0 : pagesz_kb;
572         } else {
573             vma.usage.shared_dirty += is_dirty ? pagesz_kb : 0;
574             vma.usage.shared_clean += is_dirty ? 0 : pagesz_kb;
575         }
576     }
577     if (!get_wss) {
578         vma.usage.vss += pagesz_kb * num_pages;
579     }
580     return true;
581 }
582 
583 // Public APIs
ForEachVmaFromFile(const std::string & path,const VmaCallback & callback,bool read_smaps_fields)584 bool ForEachVmaFromFile(const std::string& path, const VmaCallback& callback,
585                         bool read_smaps_fields) {
586     auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
587     if (fp == nullptr) {
588         return false;
589     }
590 
591     char* line = nullptr;
592     bool parsing_vma = false;
593     ssize_t line_len;
594     size_t line_alloc = 0;
595     Vma vma;
596     while ((line_len = getline(&line, &line_alloc, fp.get())) > 0) {
597         // Make sure the line buffer terminates like a C string for ReadMapFile
598         line[line_len] = '\0';
599 
600         if (parsing_vma) {
601             if (parse_smaps_field(line, &vma.usage)) {
602                 // This was a stats field
603                 continue;
604             }
605 
606             // Done collecting stats, make the call back
607             if (!callback(vma)) {
608                 free(line);
609                 return false;
610             }
611             parsing_vma = false;
612         }
613 
614         vma.clear();
615         // If it has, we are looking for the vma stats
616         // 00400000-00409000 r-xp 00000000 fc:00 426998  /usr/lib/gvfs/gvfsd-http
617         if (!::android::procinfo::ReadMapFileContent(
618                     line, [&](const android::procinfo::MapInfo& mapinfo) {
619                         vma.start = mapinfo.start;
620                         vma.end = mapinfo.end;
621                         vma.flags = mapinfo.flags;
622                         vma.offset = mapinfo.pgoff;
623                         vma.name = mapinfo.name;
624                         vma.inode = mapinfo.inode;
625                         vma.is_shared = mapinfo.shared;
626                     })) {
627             // free getline() managed buffer
628             free(line);
629             LOG(ERROR) << "Failed to parse " << path;
630             return false;
631         }
632         if (read_smaps_fields) {
633             parsing_vma = true;
634         } else {
635             // Done collecting stats, make the call back
636             if (!callback(vma)) {
637                 free(line);
638                 return false;
639             }
640         }
641     }
642 
643     // free getline() managed buffer
644     free(line);
645 
646     if (parsing_vma) {
647         if (!callback(vma)) {
648             return false;
649         }
650     }
651 
652     return true;
653 }
654 
655 enum smaps_rollup_support { UNTRIED, SUPPORTED, UNSUPPORTED };
656 
657 static std::atomic<smaps_rollup_support> g_rollup_support = UNTRIED;
658 
IsSmapsRollupSupported()659 bool IsSmapsRollupSupported() {
660     // Similar to OpenSmapsOrRollup checks from android_os_Debug.cpp, except
661     // the method only checks if rollup is supported and returns the status
662     // right away.
663     enum smaps_rollup_support rollup_support = g_rollup_support.load(std::memory_order_relaxed);
664     if (rollup_support != UNTRIED) {
665         return rollup_support == SUPPORTED;
666     }
667 
668     // Check the calling process for smaps_rollup since it is guaranteed to be alive
669     if (access("/proc/self/smaps_rollup", F_OK | R_OK)) {
670         g_rollup_support.store(UNSUPPORTED, std::memory_order_relaxed);
671         return false;
672     }
673 
674     g_rollup_support.store(SUPPORTED, std::memory_order_relaxed);
675     LOG(INFO) << "Using smaps_rollup for pss collection";
676     return true;
677 }
678 
SmapsOrRollupFromFile(const std::string & path,MemUsage * stats)679 bool SmapsOrRollupFromFile(const std::string& path, MemUsage* stats) {
680     auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
681     if (fp == nullptr) {
682         return false;
683     }
684 
685     char* line = nullptr;
686     size_t line_alloc = 0;
687     stats->clear();
688     while (getline(&line, &line_alloc, fp.get()) > 0) {
689         switch (line[0]) {
690             case 'P':
691                 if (strncmp(line, "Pss:", 4) == 0) {
692                     char* c = line + 4;
693                     stats->pss += strtoull(c, nullptr, 10);
694                 } else if (strncmp(line, "Private_Clean:", 14) == 0) {
695                     char* c = line + 14;
696                     uint64_t prcl = strtoull(c, nullptr, 10);
697                     stats->private_clean += prcl;
698                     stats->uss += prcl;
699                 } else if (strncmp(line, "Private_Dirty:", 14) == 0) {
700                     char* c = line + 14;
701                     uint64_t prdi = strtoull(c, nullptr, 10);
702                     stats->private_dirty += prdi;
703                     stats->uss += prdi;
704                 }
705                 break;
706             case 'R':
707                 if (strncmp(line, "Rss:", 4) == 0) {
708                     char* c = line + 4;
709                     stats->rss += strtoull(c, nullptr, 10);
710                 }
711                 break;
712             case 'S':
713                 if (strncmp(line, "SwapPss:", 8) == 0) {
714                     char* c = line + 8;
715                     stats->swap_pss += strtoull(c, nullptr, 10);
716                 }
717                 break;
718         }
719     }
720 
721     // free getline() managed buffer
722     free(line);
723     return true;
724 }
725 
SmapsOrRollupPssFromFile(const std::string & path,uint64_t * pss)726 bool SmapsOrRollupPssFromFile(const std::string& path, uint64_t* pss) {
727     auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
728     if (fp == nullptr) {
729         return false;
730     }
731     *pss = 0;
732     char* line = nullptr;
733     size_t line_alloc = 0;
734     while (getline(&line, &line_alloc, fp.get()) > 0) {
735         uint64_t v;
736         if (sscanf(line, "Pss: %" SCNu64 " kB", &v) == 1) {
737             *pss += v;
738         }
739     }
740 
741     // free getline() managed buffer
742     free(line);
743     return true;
744 }
745 
StatusVmRSSFromFile(const std::string & path,uint64_t * rss)746 bool StatusVmRSSFromFile(const std::string& path, uint64_t* rss) {
747     auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
748     if (fp == nullptr) {
749         return false;
750     }
751 
752     // We use this bool because -1 as an "invalid" value for RSS will wrap
753     // around to a positive number.
754     bool success = false;
755 
756     *rss = 0;
757     char* line = nullptr;
758     size_t line_alloc = 0;
759     while (getline(&line, &line_alloc, fp.get()) > 0) {
760         uint64_t v;
761         if (sscanf(line, "VmRSS: %" SCNu64 " kB", &v) == 1) {
762             *rss = v;
763             success = true;
764             // Break because there is only one VmRSS field in status.
765             break;
766         }
767     }
768 
769     // free getline() managed buffer
770     free(line);
771     return success;
772 }
773 
GetFormat(std::string_view arg)774 Format GetFormat(std::string_view arg) {
775     if (arg == "json") {
776         return Format::JSON;
777     }
778     if (arg == "csv") {
779         return Format::CSV;
780     }
781     if (arg == "raw") {
782         return Format::RAW;
783     }
784     return Format::INVALID;
785 }
786 
EscapeCsvString(const std::string & raw)787 std::string EscapeCsvString(const std::string& raw) {
788     std::string ret;
789     for (auto it = raw.cbegin(); it != raw.cend(); it++) {
790         switch (*it) {
791             case '"':
792                 ret += "\"";
793                 break;
794             default:
795                 ret += *it;
796                 break;
797         }
798     }
799     return '"' + ret + '"';
800 }
801 
EscapeJsonString(const std::string & raw)802 std::string EscapeJsonString(const std::string& raw) {
803     std::string ret;
804     for (auto it = raw.cbegin(); it != raw.cend(); it++) {
805         switch (*it) {
806             case '\\':
807                 ret += "\\\\";
808                 break;
809             case '"':
810                 ret += "\\\"";
811                 break;
812             case '/':
813                 ret += "\\/";
814                 break;
815             case '\b':
816                 ret += "\\b";
817                 break;
818             case '\f':
819                 ret += "\\f";
820                 break;
821             case '\n':
822                 ret += "\\n";
823                 break;
824             case '\r':
825                 ret += "\\r";
826                 break;
827             case '\t':
828                 ret += "\\t";
829                 break;
830             default:
831                 ret += *it;
832                 break;
833         }
834     }
835     return '"' + ret + '"';
836 }
837 
838 }  // namespace meminfo
839 }  // namespace android
840