blob: 7a64c647933550e24951563fc1e1adfbcf4b9feb [file] [log] [blame]
Sandeep Patila60a7762018-08-29 17:10:47 -07001/*
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
Sandeep Patil8ab02fa2019-01-13 17:39:08 -080024#include <atomic>
Sandeep Patila60a7762018-08-29 17:10:47 -070025#include <fstream>
26#include <iostream>
27#include <memory>
28#include <string>
29#include <utility>
Christopher Ferriscbf23492019-06-25 17:30:56 -070030#include <vector>
Sandeep Patila60a7762018-08-29 17:10:47 -070031
32#include <android-base/file.h>
33#include <android-base/logging.h>
34#include <android-base/stringprintf.h>
Sandeep Patila5e9d6b2018-12-29 21:05:38 -080035#include <android-base/strings.h>
Sandeep Patila60a7762018-08-29 17:10:47 -070036#include <android-base/unique_fd.h>
37#include <procinfo/process_map.h>
38
39#include "meminfo_private.h"
40
41namespace android {
42namespace meminfo {
43
Vic Yang70663b12020-01-16 22:38:37 -080044// 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.
Suren Baghdasaryanbef95402020-09-25 19:57:18 -070046// - On x86, [vsyscall] is a kernel memory that is outside of pagemap range.
47static const std::vector<std::string> g_excluded_vmas = {
48 "[vectors]",
49#ifdef __x86_64__
50 "[vsyscall]"
51#endif
52};
Vic Yang70663b12020-01-16 22:38:37 -080053
Sandeep Patila60a7762018-08-29 17:10:47 -070054static 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
Sandeep Patil4c174ef2018-11-09 16:42:45 -080060 to->swap += from.swap;
61
Sandeep Patila60a7762018-08-29 17:10:47 -070062 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
Sandeep Patilb9c94702019-01-01 16:04:04 -080069// Returns true if the line was valid smaps stats line false otherwise.
70static bool parse_smaps_field(const char* line, MemUsage* stats) {
71 char field[64];
72 int len;
73 if (sscanf(line, "%63s %n", field, &len) == 1 && *field && field[strlen(field) - 1] == ':') {
74 const char* c = line + len;
75 switch (field[0]) {
76 case 'P':
77 if (strncmp(field, "Pss:", 4) == 0) {
78 stats->pss = strtoull(c, nullptr, 10);
79 } else if (strncmp(field, "Private_Clean:", 14) == 0) {
80 uint64_t prcl = strtoull(c, nullptr, 10);
81 stats->private_clean = prcl;
82 stats->uss += prcl;
83 } else if (strncmp(field, "Private_Dirty:", 14) == 0) {
84 uint64_t prdi = strtoull(c, nullptr, 10);
85 stats->private_dirty = prdi;
86 stats->uss += prdi;
87 }
88 break;
89 case 'S':
90 if (strncmp(field, "Size:", 5) == 0) {
91 stats->vss = strtoull(c, nullptr, 10);
92 } else if (strncmp(field, "Shared_Clean:", 13) == 0) {
93 stats->shared_clean = strtoull(c, nullptr, 10);
94 } else if (strncmp(field, "Shared_Dirty:", 13) == 0) {
95 stats->shared_dirty = strtoull(c, nullptr, 10);
96 } else if (strncmp(field, "Swap:", 5) == 0) {
97 stats->swap = strtoull(c, nullptr, 10);
98 } else if (strncmp(field, "SwapPss:", 8) == 0) {
99 stats->swap_pss = strtoull(c, nullptr, 10);
100 }
101 break;
102 case 'R':
103 if (strncmp(field, "Rss:", 4) == 0) {
104 stats->rss = strtoull(c, nullptr, 10);
105 }
106 break;
107 }
108 return true;
109 }
110
111 return false;
112}
113
Sandeep Patile3015042018-11-19 15:25:18 -0800114bool ProcMemInfo::ResetWorkingSet(pid_t pid) {
115 std::string clear_refs_path = ::android::base::StringPrintf("/proc/%d/clear_refs", pid);
116 if (!::android::base::WriteStringToFile("1\n", clear_refs_path)) {
117 PLOG(ERROR) << "Failed to write to " << clear_refs_path;
118 return false;
119 }
120
121 return true;
122}
123
Sandeep Patil584d0be2018-11-19 11:38:40 -0800124ProcMemInfo::ProcMemInfo(pid_t pid, bool get_wss, uint64_t pgflags, uint64_t pgflags_mask)
Sandeep Patil82409b12018-11-20 09:31:36 -0800125 : pid_(pid), get_wss_(get_wss), pgflags_(pgflags), pgflags_mask_(pgflags_mask) {}
Sandeep Patila60a7762018-08-29 17:10:47 -0700126
127const std::vector<Vma>& ProcMemInfo::Maps() {
Sandeep Patil82409b12018-11-20 09:31:36 -0800128 if (maps_.empty() && !ReadMaps(get_wss_)) {
129 LOG(ERROR) << "Failed to read maps for Process " << pid_;
130 }
131
Sandeep Patila60a7762018-08-29 17:10:47 -0700132 return maps_;
133}
134
Sandeep Patilb28668e2019-01-19 21:11:01 -0800135const std::vector<Vma>& ProcMemInfo::MapsWithPageIdle() {
136 if (maps_.empty() && !ReadMaps(get_wss_, true)) {
137 LOG(ERROR) << "Failed to read maps with page idle for Process " << pid_;
138 }
139
140 return maps_;
141}
142
Christopher Ferris1952d362019-07-12 14:25:48 -0700143const std::vector<Vma>& ProcMemInfo::MapsWithoutUsageStats() {
144 if (maps_.empty() && !ReadMaps(get_wss_, false, false)) {
145 LOG(ERROR) << "Failed to read maps for Process " << pid_;
146 }
147
148 return maps_;
149}
150
Sandeep Patilb9c94702019-01-01 16:04:04 -0800151const std::vector<Vma>& ProcMemInfo::Smaps(const std::string& path) {
152 if (!maps_.empty()) {
153 return maps_;
154 }
155
Vic Yang70663b12020-01-16 22:38:37 -0800156 auto collect_vmas = [&](const Vma& vma) {
Suren Baghdasaryan68f5dd12020-09-25 19:45:28 -0700157 if (std::find(g_excluded_vmas.begin(), g_excluded_vmas.end(), vma.name) ==
158 g_excluded_vmas.end()) {
Vic Yang70663b12020-01-16 22:38:37 -0800159 maps_.emplace_back(vma);
160 }
161 };
Sandeep Patilb9c94702019-01-01 16:04:04 -0800162 if (path.empty() && !ForEachVma(collect_vmas)) {
163 LOG(ERROR) << "Failed to read smaps for Process " << pid_;
164 maps_.clear();
165 }
166
167 if (!path.empty() && !ForEachVmaFromFile(path, collect_vmas)) {
168 LOG(ERROR) << "Failed to read smaps from file " << path;
169 maps_.clear();
170 }
171
172 return maps_;
173}
174
Sandeep Patila60a7762018-08-29 17:10:47 -0700175const MemUsage& ProcMemInfo::Usage() {
176 if (get_wss_) {
Sandeep Patile3015042018-11-19 15:25:18 -0800177 LOG(WARNING) << "Trying to read process memory usage for " << pid_
178 << " using invalid object";
Sandeep Patil82409b12018-11-20 09:31:36 -0800179 return usage_;
Sandeep Patila60a7762018-08-29 17:10:47 -0700180 }
Sandeep Patil82409b12018-11-20 09:31:36 -0800181
182 if (maps_.empty() && !ReadMaps(get_wss_)) {
183 LOG(ERROR) << "Failed to get memory usage for Process " << pid_;
184 }
185
Sandeep Patila60a7762018-08-29 17:10:47 -0700186 return usage_;
187}
188
189const MemUsage& ProcMemInfo::Wss() {
190 if (!get_wss_) {
Sandeep Patile3015042018-11-19 15:25:18 -0800191 LOG(WARNING) << "Trying to read process working set for " << pid_
192 << " using invalid object";
Sandeep Patila109d6e2019-01-12 21:29:19 -0800193 return usage_;
Sandeep Patil82409b12018-11-20 09:31:36 -0800194 }
195
196 if (maps_.empty() && !ReadMaps(get_wss_)) {
197 LOG(ERROR) << "Failed to get working set for Process " << pid_;
Sandeep Patila60a7762018-08-29 17:10:47 -0700198 }
199
Sandeep Patila109d6e2019-01-12 21:29:19 -0800200 return usage_;
Sandeep Patila60a7762018-08-29 17:10:47 -0700201}
202
Sandeep Patilb9c94702019-01-01 16:04:04 -0800203bool ProcMemInfo::ForEachVma(const VmaCallback& callback) {
204 std::string path = ::android::base::StringPrintf("/proc/%d/smaps", pid_);
205 return ForEachVmaFromFile(path, callback);
206}
207
Sandeep Patil8ab02fa2019-01-13 17:39:08 -0800208bool ProcMemInfo::SmapsOrRollup(MemUsage* stats) const {
209 std::string path = ::android::base::StringPrintf(
210 "/proc/%d/%s", pid_, IsSmapsRollupSupported(pid_) ? "smaps_rollup" : "smaps");
Sandeep Patila5e9d6b2018-12-29 21:05:38 -0800211 return SmapsOrRollupFromFile(path, stats);
Sandeep Patil8ab02fa2019-01-13 17:39:08 -0800212}
Sandeep Patila5e9d6b2018-12-29 21:05:38 -0800213
Sandeep Patil8ab02fa2019-01-13 17:39:08 -0800214bool ProcMemInfo::SmapsOrRollupPss(uint64_t* pss) const {
215 std::string path = ::android::base::StringPrintf(
216 "/proc/%d/%s", pid_, IsSmapsRollupSupported(pid_) ? "smaps_rollup" : "smaps");
Sandeep Patil33ecfae2019-01-13 16:47:20 -0800217 return SmapsOrRollupPssFromFile(path, pss);
218}
219
Vic Yang100426f2020-01-06 21:52:50 -0800220const std::vector<uint64_t>& ProcMemInfo::SwapOffsets() {
Sandeep Patil82409b12018-11-20 09:31:36 -0800221 if (get_wss_) {
222 LOG(WARNING) << "Trying to read process swap offsets for " << pid_
223 << " using invalid object";
224 return swap_offsets_;
225 }
226
227 if (maps_.empty() && !ReadMaps(get_wss_)) {
228 LOG(ERROR) << "Failed to get swap offsets for Process " << pid_;
229 }
230
Sandeep Patil4c174ef2018-11-09 16:42:45 -0800231 return swap_offsets_;
232}
233
Sandeep Patilee49d942019-01-21 16:47:24 -0800234bool ProcMemInfo::PageMap(const Vma& vma, std::vector<uint64_t>* pagemap) {
235 pagemap->clear();
236 std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
237 ::android::base::unique_fd pagemap_fd(
238 TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
Christopher Ferris1952d362019-07-12 14:25:48 -0700239 if (pagemap_fd == -1) {
Sandeep Patilee49d942019-01-21 16:47:24 -0800240 PLOG(ERROR) << "Failed to open " << pagemap_file;
241 return false;
242 }
243
244 uint64_t nr_pages = (vma.end - vma.start) / getpagesize();
Christopher Ferris1952d362019-07-12 14:25:48 -0700245 pagemap->resize(nr_pages);
Sandeep Patilee49d942019-01-21 16:47:24 -0800246
Christopher Ferris1952d362019-07-12 14:25:48 -0700247 size_t bytes_to_read = sizeof(uint64_t) * nr_pages;
248 off64_t start_addr = (vma.start / getpagesize()) * sizeof(uint64_t);
249 ssize_t bytes_read = pread64(pagemap_fd, pagemap->data(), bytes_to_read, start_addr);
250 if (bytes_read == -1) {
251 PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
252 return false;
253 } else if (static_cast<size_t>(bytes_read) != bytes_to_read) {
254 LOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_
255 << ": read bytes " << bytes_read << " expected bytes " << bytes_to_read;
256 return false;
Sandeep Patilee49d942019-01-21 16:47:24 -0800257 }
258
259 return true;
260}
261
Christopher Ferrisd9897c52019-10-10 13:25:00 -0700262static int GetPagemapFd(pid_t pid) {
263 std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid);
264 int fd = TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC));
265 if (fd == -1) {
266 PLOG(ERROR) << "Failed to open " << pagemap_file;
267 }
268 return fd;
269}
270
Christopher Ferris1952d362019-07-12 14:25:48 -0700271bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle, bool get_usage_stats) {
Sandeep Patil82409b12018-11-20 09:31:36 -0800272 // Each object reads /proc/<pid>/maps only once. This is done to make sure programs that are
273 // running for the lifetime of the system can recycle the objects and don't have to
274 // unnecessarily retain and update this object in memory (which can get significantly large).
275 // E.g. A program that only needs to reset the working set will never all ->Maps(), ->Usage().
276 // E.g. A program that is monitoring smaps_rollup, may never call ->maps(), Usage(), so it
277 // doesn't make sense for us to parse and retain unnecessary memory accounting stats by default.
278 if (!maps_.empty()) return true;
279
Sandeep Patila60a7762018-08-29 17:10:47 -0700280 // parse and read /proc/<pid>/maps
281 std::string maps_file = ::android::base::StringPrintf("/proc/%d/maps", pid_);
282 if (!::android::procinfo::ReadMapFile(
Sandeep Patilf9175ef2019-01-30 17:43:22 -0800283 maps_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t,
Sandeep Patila60a7762018-08-29 17:10:47 -0700284 const char* name) {
Suren Baghdasaryan68f5dd12020-09-25 19:45:28 -0700285 if (std::find(g_excluded_vmas.begin(), g_excluded_vmas.end(), name) ==
286 g_excluded_vmas.end()) {
Vic Yang70663b12020-01-16 22:38:37 -0800287 maps_.emplace_back(Vma(start, end, pgoff, flags, name));
288 }
Sandeep Patila60a7762018-08-29 17:10:47 -0700289 })) {
290 LOG(ERROR) << "Failed to parse " << maps_file;
291 maps_.clear();
292 return false;
293 }
294
Christopher Ferris1952d362019-07-12 14:25:48 -0700295 if (!get_usage_stats) {
296 return true;
297 }
298
Christopher Ferrisd9897c52019-10-10 13:25:00 -0700299 ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_));
300 if (pagemap_fd == -1) {
Sandeep Patila60a7762018-08-29 17:10:47 -0700301 return false;
302 }
303
304 for (auto& vma : maps_) {
Sandeep Patilb28668e2019-01-19 21:11:01 -0800305 if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss, use_pageidle)) {
Sandeep Patil4c174ef2018-11-09 16:42:45 -0800306 LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
307 << vma.end << "]";
Sandeep Patila60a7762018-08-29 17:10:47 -0700308 maps_.clear();
309 return false;
310 }
Sandeep Patila109d6e2019-01-12 21:29:19 -0800311 add_mem_usage(&usage_, vma.usage);
Sandeep Patila60a7762018-08-29 17:10:47 -0700312 }
313
314 return true;
315}
316
Christopher Ferrisd9897c52019-10-10 13:25:00 -0700317bool ProcMemInfo::FillInVmaStats(Vma& vma) {
318 ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_));
319 if (pagemap_fd == -1) {
320 return false;
321 }
322
323 if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss_, false)) {
324 LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
325 << vma.end << "]";
326 return false;
327 }
328 return true;
329}
330
Sandeep Patilb28668e2019-01-19 21:11:01 -0800331bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle) {
Sandeep Patila60a7762018-08-29 17:10:47 -0700332 PageAcct& pinfo = PageAcct::Instance();
Christopher Ferriscbf23492019-06-25 17:30:56 -0700333 if (get_wss && use_pageidle && !pinfo.InitPageAcct(true)) {
334 LOG(ERROR) << "Failed to init idle page accounting";
Sandeep Patila60a7762018-08-29 17:10:47 -0700335 return false;
336 }
337
Christopher Ferriscbf23492019-06-25 17:30:56 -0700338 uint64_t pagesz = getpagesize();
339 size_t num_pages = (vma.end - vma.start) / pagesz;
340 size_t first_page = vma.start / pagesz;
Sandeep Patilb28668e2019-01-19 21:11:01 -0800341
Christopher Ferriscbf23492019-06-25 17:30:56 -0700342 std::vector<uint64_t> page_cache;
343 size_t cur_page_cache_index = 0;
344 size_t num_in_page_cache = 0;
345 size_t num_leftover_pages = num_pages;
346 for (size_t cur_page = first_page; cur_page < first_page + num_pages; ++cur_page) {
Sandeep Patila60a7762018-08-29 17:10:47 -0700347 if (!get_wss) {
348 vma.usage.vss += pagesz;
349 }
Sandeep Patila60a7762018-08-29 17:10:47 -0700350
Christopher Ferriscbf23492019-06-25 17:30:56 -0700351 // Cache page map data.
352 if (cur_page_cache_index == num_in_page_cache) {
353 static constexpr size_t kMaxPages = 2048;
354 num_leftover_pages -= num_in_page_cache;
355 if (num_leftover_pages > kMaxPages) {
356 num_in_page_cache = kMaxPages;
357 } else {
358 num_in_page_cache = num_leftover_pages;
359 }
360 page_cache.resize(num_in_page_cache);
361 size_t total_bytes = page_cache.size() * sizeof(uint64_t);
362 ssize_t bytes = pread64(pagemap_fd, page_cache.data(), total_bytes,
363 cur_page * sizeof(uint64_t));
364 if (bytes != total_bytes) {
365 if (bytes == -1) {
Christopher Ferris4ac1e6d2019-06-28 11:24:30 -0700366 PLOG(ERROR) << "Failed to read page data at offset 0x" << std::hex
Christopher Ferriscbf23492019-06-25 17:30:56 -0700367 << cur_page * sizeof(uint64_t);
368 } else {
Christopher Ferris4ac1e6d2019-06-28 11:24:30 -0700369 LOG(ERROR) << "Failed to read page data at offset 0x" << std::hex
370 << cur_page * sizeof(uint64_t) << std::dec << " read bytes " << bytes
371 << " expected bytes " << total_bytes;
Christopher Ferriscbf23492019-06-25 17:30:56 -0700372 }
373 return false;
374 }
375 cur_page_cache_index = 0;
376 }
377
378 uint64_t page_info = page_cache[cur_page_cache_index++];
379 if (!PAGE_PRESENT(page_info) && !PAGE_SWAPPED(page_info)) continue;
380
381 if (PAGE_SWAPPED(page_info)) {
Sandeep Patil4c174ef2018-11-09 16:42:45 -0800382 vma.usage.swap += pagesz;
Christopher Ferriscbf23492019-06-25 17:30:56 -0700383 swap_offsets_.emplace_back(PAGE_SWAP_OFFSET(page_info));
Sandeep Patila60a7762018-08-29 17:10:47 -0700384 continue;
385 }
386
Christopher Ferriscbf23492019-06-25 17:30:56 -0700387 uint64_t page_frame = PAGE_PFN(page_info);
388 uint64_t cur_page_flags;
389 if (!pinfo.PageFlags(page_frame, &cur_page_flags)) {
Sandeep Patila60a7762018-08-29 17:10:47 -0700390 LOG(ERROR) << "Failed to get page flags for " << page_frame << " in process " << pid_;
Sandeep Patil82409b12018-11-20 09:31:36 -0800391 swap_offsets_.clear();
Sandeep Patila60a7762018-08-29 17:10:47 -0700392 return false;
393 }
394
Sandeep Patil584d0be2018-11-19 11:38:40 -0800395 // skip unwanted pages from the count
Christopher Ferriscbf23492019-06-25 17:30:56 -0700396 if ((cur_page_flags & pgflags_mask_) != pgflags_) continue;
Sandeep Patil584d0be2018-11-19 11:38:40 -0800397
Christopher Ferriscbf23492019-06-25 17:30:56 -0700398 uint64_t cur_page_counts;
399 if (!pinfo.PageMapCount(page_frame, &cur_page_counts)) {
Sandeep Patila60a7762018-08-29 17:10:47 -0700400 LOG(ERROR) << "Failed to get page count for " << page_frame << " in process " << pid_;
Sandeep Patil82409b12018-11-20 09:31:36 -0800401 swap_offsets_.clear();
Sandeep Patila60a7762018-08-29 17:10:47 -0700402 return false;
403 }
404
405 // Page was unmapped between the presence check at the beginning of the loop and here.
Christopher Ferriscbf23492019-06-25 17:30:56 -0700406 if (cur_page_counts == 0) {
Sandeep Patila60a7762018-08-29 17:10:47 -0700407 continue;
408 }
409
Christopher Ferriscbf23492019-06-25 17:30:56 -0700410 bool is_dirty = !!(cur_page_flags & (1 << KPF_DIRTY));
411 bool is_private = (cur_page_counts == 1);
Sandeep Patila60a7762018-08-29 17:10:47 -0700412 // Working set
413 if (get_wss) {
Sandeep Patilb28668e2019-01-19 21:11:01 -0800414 bool is_referenced = use_pageidle ? (pinfo.IsPageIdle(page_frame) == 1)
Christopher Ferriscbf23492019-06-25 17:30:56 -0700415 : !!(cur_page_flags & (1 << KPF_REFERENCED));
Sandeep Patila60a7762018-08-29 17:10:47 -0700416 if (!is_referenced) {
417 continue;
418 }
419 // This effectively makes vss = rss for the working set is requested.
420 // The libpagemap implementation returns vss > rss for
421 // working set, which doesn't make sense.
Sandeep Patila109d6e2019-01-12 21:29:19 -0800422 vma.usage.vss += pagesz;
423 }
424
425 vma.usage.rss += pagesz;
426 vma.usage.uss += is_private ? pagesz : 0;
Christopher Ferriscbf23492019-06-25 17:30:56 -0700427 vma.usage.pss += pagesz / cur_page_counts;
Sandeep Patila109d6e2019-01-12 21:29:19 -0800428 if (is_private) {
429 vma.usage.private_dirty += is_dirty ? pagesz : 0;
430 vma.usage.private_clean += is_dirty ? 0 : pagesz;
Sandeep Patila60a7762018-08-29 17:10:47 -0700431 } else {
Sandeep Patila109d6e2019-01-12 21:29:19 -0800432 vma.usage.shared_dirty += is_dirty ? pagesz : 0;
433 vma.usage.shared_clean += is_dirty ? 0 : pagesz;
Sandeep Patila60a7762018-08-29 17:10:47 -0700434 }
435 }
Sandeep Patila60a7762018-08-29 17:10:47 -0700436 return true;
437}
438
Sandeep Patila5e9d6b2018-12-29 21:05:38 -0800439// Public APIs
Sandeep Patilb9c94702019-01-01 16:04:04 -0800440bool ForEachVmaFromFile(const std::string& path, const VmaCallback& callback) {
441 auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
442 if (fp == nullptr) {
443 return false;
444 }
445
446 char* line = nullptr;
447 bool parsing_vma = false;
448 ssize_t line_len;
Sandeep Patil6a65b462019-01-13 19:39:46 -0800449 size_t line_alloc = 0;
Sandeep Patilb9c94702019-01-01 16:04:04 -0800450 Vma vma;
Sandeep Patil6a65b462019-01-13 19:39:46 -0800451 while ((line_len = getline(&line, &line_alloc, fp.get())) > 0) {
Sandeep Patilb9c94702019-01-01 16:04:04 -0800452 // Make sure the line buffer terminates like a C string for ReadMapFile
453 line[line_len] = '\0';
454
455 if (parsing_vma) {
456 if (parse_smaps_field(line, &vma.usage)) {
457 // This was a stats field
458 continue;
459 }
460
461 // Done collecting stats, make the call back
462 callback(vma);
463 parsing_vma = false;
464 }
465
466 vma.clear();
467 // If it has, we are looking for the vma stats
468 // 00400000-00409000 r-xp 00000000 fc:00 426998 /usr/lib/gvfs/gvfsd-http
469 if (!::android::procinfo::ReadMapFileContent(
Sandeep Patilf9175ef2019-01-30 17:43:22 -0800470 line, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t,
Sandeep Patilb9c94702019-01-01 16:04:04 -0800471 const char* name) {
472 vma.start = start;
473 vma.end = end;
474 vma.flags = flags;
475 vma.offset = pgoff;
476 vma.name = name;
477 })) {
478 LOG(ERROR) << "Failed to parse " << path;
479 return false;
480 }
481 parsing_vma = true;
482 }
483
484 // free getline() managed buffer
485 free(line);
486
487 if (parsing_vma) {
488 callback(vma);
489 }
490
491 return true;
492}
493
Sandeep Patil8ab02fa2019-01-13 17:39:08 -0800494enum smaps_rollup_support { UNTRIED, SUPPORTED, UNSUPPORTED };
495
496static std::atomic<smaps_rollup_support> g_rollup_support = UNTRIED;
497
498bool IsSmapsRollupSupported(pid_t pid) {
499 // Similar to OpenSmapsOrRollup checks from android_os_Debug.cpp, except
500 // the method only checks if rollup is supported and returns the status
501 // right away.
502 enum smaps_rollup_support rollup_support = g_rollup_support.load(std::memory_order_relaxed);
503 if (rollup_support != UNTRIED) {
504 return rollup_support == SUPPORTED;
505 }
506 std::string rollup_file = ::android::base::StringPrintf("/proc/%d/smaps_rollup", pid);
507 if (access(rollup_file.c_str(), F_OK | R_OK)) {
508 // No check for errno = ENOENT necessary here. The caller MUST fallback to
509 // using /proc/<pid>/smaps instead anyway.
510 g_rollup_support.store(UNSUPPORTED, std::memory_order_relaxed);
511 return false;
512 }
513
514 g_rollup_support.store(SUPPORTED, std::memory_order_relaxed);
515 LOG(INFO) << "Using smaps_rollup for pss collection";
516 return true;
517}
518
Sandeep Patila5e9d6b2018-12-29 21:05:38 -0800519bool SmapsOrRollupFromFile(const std::string& path, MemUsage* stats) {
520 auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
521 if (fp == nullptr) {
522 return false;
523 }
524
Sandeep Patil353bfcf2019-01-14 17:44:34 -0800525 char* line = nullptr;
526 size_t line_alloc = 0;
Sandeep Patila5e9d6b2018-12-29 21:05:38 -0800527 stats->clear();
Sandeep Patil353bfcf2019-01-14 17:44:34 -0800528 while (getline(&line, &line_alloc, fp.get()) > 0) {
Sandeep Patila5e9d6b2018-12-29 21:05:38 -0800529 switch (line[0]) {
530 case 'P':
531 if (strncmp(line, "Pss:", 4) == 0) {
532 char* c = line + 4;
533 stats->pss += strtoull(c, nullptr, 10);
534 } else if (strncmp(line, "Private_Clean:", 14) == 0) {
535 char* c = line + 14;
536 uint64_t prcl = strtoull(c, nullptr, 10);
537 stats->private_clean += prcl;
538 stats->uss += prcl;
539 } else if (strncmp(line, "Private_Dirty:", 14) == 0) {
540 char* c = line + 14;
541 uint64_t prdi = strtoull(c, nullptr, 10);
542 stats->private_dirty += prdi;
543 stats->uss += prdi;
544 }
545 break;
546 case 'R':
547 if (strncmp(line, "Rss:", 4) == 0) {
548 char* c = line + 4;
549 stats->rss += strtoull(c, nullptr, 10);
550 }
551 break;
552 case 'S':
553 if (strncmp(line, "SwapPss:", 8) == 0) {
554 char* c = line + 8;
555 stats->swap_pss += strtoull(c, nullptr, 10);
556 }
557 break;
558 }
559 }
560
Sandeep Patil353bfcf2019-01-14 17:44:34 -0800561 // free getline() managed buffer
562 free(line);
Sandeep Patila5e9d6b2018-12-29 21:05:38 -0800563 return true;
564}
565
Sandeep Patil33ecfae2019-01-13 16:47:20 -0800566bool SmapsOrRollupPssFromFile(const std::string& path, uint64_t* pss) {
567 auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
568 if (fp == nullptr) {
569 return false;
570 }
571 *pss = 0;
Sandeep Patil353bfcf2019-01-14 17:44:34 -0800572 char* line = nullptr;
573 size_t line_alloc = 0;
574 while (getline(&line, &line_alloc, fp.get()) > 0) {
Sandeep Patil33ecfae2019-01-13 16:47:20 -0800575 uint64_t v;
576 if (sscanf(line, "Pss: %" SCNu64 " kB", &v) == 1) {
577 *pss += v;
578 }
579 }
580
Sandeep Patil353bfcf2019-01-14 17:44:34 -0800581 // free getline() managed buffer
582 free(line);
Sandeep Patil33ecfae2019-01-13 16:47:20 -0800583 return true;
584}
585
Sandeep Patila60a7762018-08-29 17:10:47 -0700586} // namespace meminfo
587} // namespace android