1 /*
2 * Copyright (C) 2022 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 <inttypes.h>
18 #include <linux/oom.h>
19 #include <stdlib.h>
20 #include <sys/mman.h>
21 #include <unistd.h>
22
23 #include <chrono>
24 #include <iomanip>
25 #include <iostream>
26 #include <set>
27 #include <string>
28 #include <vector>
29
30 #include <android-base/file.h>
31 #include <android-base/parseint.h>
32 #include <android-base/stringprintf.h>
33 #include <android-base/strings.h>
34 #include <meminfo/sysmeminfo.h>
35
36 #include <processrecord.h>
37 #include <smapinfo.h>
38
39 namespace android {
40 namespace smapinfo {
41
42 using ::android::base::StringPrintf;
43 using ::android::meminfo::EscapeCsvString;
44 using ::android::meminfo::EscapeJsonString;
45 using ::android::meminfo::Format;
46 using ::android::meminfo::MemUsage;
47 using ::android::meminfo::Vma;
48
get_all_pids(std::set<pid_t> * pids)49 bool get_all_pids(std::set<pid_t>* pids) {
50 pids->clear();
51 std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
52 if (!procdir) return false;
53
54 struct dirent* dir;
55 pid_t pid;
56 while ((dir = readdir(procdir.get()))) {
57 if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
58 pids->insert(pid);
59 }
60 return true;
61 }
62
63 namespace procrank {
64
count_swap_offsets(const ProcessRecord & proc,std::vector<uint16_t> & swap_offset_array,std::ostream & err)65 static bool count_swap_offsets(const ProcessRecord& proc, std::vector<uint16_t>& swap_offset_array,
66 std::ostream& err) {
67 const std::vector<uint64_t>& swp_offs = proc.SwapOffsets();
68 for (auto& off : swp_offs) {
69 if (off >= swap_offset_array.size()) {
70 err << "swap offset " << off << " is out of bounds for process: " << proc.pid() << "\n";
71 return false;
72 }
73 if (swap_offset_array[off] == USHRT_MAX) {
74 err << "swap offset " << off << " ref count overflow in process: " << proc.pid()
75 << "\n";
76 return false;
77 }
78 swap_offset_array[off]++;
79 }
80 return true;
81 }
82
83 struct params {
84 // Calculated total memory usage across all processes in the system.
85 uint64_t total_pss;
86 uint64_t total_uss;
87 uint64_t total_swap;
88 uint64_t total_pswap;
89 uint64_t total_uswap;
90 uint64_t total_zswap;
91
92 // Print options.
93 bool show_oomadj;
94 bool show_wss;
95 bool swap_enabled;
96 bool zram_enabled;
97
98 // If zram is enabled, the compression ratio is zram used / swap used.
99 float zram_compression_ratio;
100 };
101
select_sort(struct params * params,SortOrder sort_order)102 static std::function<bool(ProcessRecord& a, ProcessRecord& b)> select_sort(struct params* params,
103 SortOrder sort_order) {
104 // Create sort function based on sort_order.
105 std::function<bool(ProcessRecord & a, ProcessRecord & b)> proc_sort;
106 switch (sort_order) {
107 case (SortOrder::BY_OOMADJ):
108 proc_sort = [](ProcessRecord& a, ProcessRecord& b) { return a.oomadj() > b.oomadj(); };
109 break;
110 case (SortOrder::BY_RSS):
111 proc_sort = [=](ProcessRecord& a, ProcessRecord& b) {
112 return a.Usage(params->show_wss).rss > b.Usage(params->show_wss).rss;
113 };
114 break;
115 case (SortOrder::BY_SWAP):
116 proc_sort = [=](ProcessRecord& a, ProcessRecord& b) {
117 return a.Usage(params->show_wss).swap > b.Usage(params->show_wss).swap;
118 };
119 break;
120 case (SortOrder::BY_USS):
121 proc_sort = [=](ProcessRecord& a, ProcessRecord& b) {
122 return a.Usage(params->show_wss).uss > b.Usage(params->show_wss).uss;
123 };
124 break;
125 case (SortOrder::BY_VSS):
126 proc_sort = [=](ProcessRecord& a, ProcessRecord& b) {
127 return a.Usage(params->show_wss).vss > b.Usage(params->show_wss).vss;
128 };
129 break;
130 case (SortOrder::BY_PSS):
131 default:
132 proc_sort = [=](ProcessRecord& a, ProcessRecord& b) {
133 return a.Usage(params->show_wss).pss > b.Usage(params->show_wss).pss;
134 };
135 break;
136 }
137 return proc_sort;
138 }
139
populate_procs(struct params * params,uint64_t pgflags,uint64_t pgflags_mask,std::vector<uint16_t> & swap_offset_array,const std::set<pid_t> & pids,std::vector<ProcessRecord> * procs,std::map<pid_t,ProcessRecord> * processrecords_ptr,std::ostream & err)140 static bool populate_procs(struct params* params, uint64_t pgflags, uint64_t pgflags_mask,
141 std::vector<uint16_t>& swap_offset_array, const std::set<pid_t>& pids,
142 std::vector<ProcessRecord>* procs,
143 std::map<pid_t, ProcessRecord>* processrecords_ptr, std::ostream& err) {
144 // Fall back to using an empty map of ProcessRecords if nullptr was passed in.
145 std::map<pid_t, ProcessRecord> processrecords;
146 if (!processrecords_ptr) {
147 processrecords_ptr = &processrecords;
148 }
149 // Mark each swap offset used by the process as we find them for calculating
150 // proportional swap usage later.
151 for (pid_t pid : pids) {
152 // Check if a ProcessRecord already exists for this pid, create one if one does not exist.
153 auto iter = processrecords_ptr->find(pid);
154 ProcessRecord& proc =
155 (iter != processrecords_ptr->end())
156 ? iter->second
157 : processrecords_ptr
158 ->emplace(pid, ProcessRecord(pid, params->show_wss, pgflags,
159 pgflags_mask, true,
160 params->show_oomadj, err))
161 .first->second;
162
163 if (!proc.valid()) {
164 // Check to see if the process is still around, skip the process if the proc
165 // directory is inaccessible. It was most likely killed while creating the process
166 // record.
167 std::string procdir = StringPrintf("/proc/%d", pid);
168 if (access(procdir.c_str(), F_OK | R_OK)) continue;
169
170 // Warn if we failed to gather process stats even while it is still alive.
171 // Return success here, so we continue to print stats for other processes.
172 err << "warning: failed to create process record for: " << pid << "\n";
173 continue;
174 }
175
176 // Skip processes with no memory mappings.
177 uint64_t vss = proc.Usage(params->show_wss).vss;
178 if (vss == 0) continue;
179
180 // Collect swap_offset counts from all processes in 1st pass.
181 if (!params->show_wss && params->swap_enabled &&
182 !count_swap_offsets(proc, swap_offset_array, err)) {
183 err << "Failed to count swap offsets for process: " << pid << "\n";
184 err << "Failed to read all pids from the system\n";
185 return false;
186 }
187
188 procs->push_back(proc);
189 }
190 return true;
191 }
192
print_header(struct params * params,std::ostream & out)193 static void print_header(struct params* params, std::ostream& out) {
194 out << StringPrintf("%5s ", "PID");
195 if (params->show_oomadj) {
196 out << StringPrintf("%5s ", "oom");
197 }
198
199 if (params->show_wss) {
200 out << StringPrintf("%7s %7s %7s ", "WRss", "WPss", "WUss");
201 } else {
202 // Swap statistics here, as working set pages by definition shouldn't end up in swap.
203 out << StringPrintf("%8s %7s %7s %7s ", "Vss", "Rss", "Pss", "Uss");
204 if (params->swap_enabled) {
205 out << StringPrintf("%7s %7s %7s ", "Swap", "PSwap", "USwap");
206 if (params->zram_enabled) {
207 out << StringPrintf("%7s ", "ZSwap");
208 }
209 }
210 }
211
212 out << "cmdline\n";
213 }
214
print_divider(struct params * params,std::ostream & out)215 static void print_divider(struct params* params, std::ostream& out) {
216 out << StringPrintf("%5s ", "");
217 if (params->show_oomadj) {
218 out << StringPrintf("%5s ", "");
219 }
220
221 if (params->show_wss) {
222 out << StringPrintf("%7s %7s %7s ", "", "------", "------");
223 } else {
224 out << StringPrintf("%8s %7s %7s %7s ", "", "", "------", "------");
225 if (params->swap_enabled) {
226 out << StringPrintf("%7s %7s %7s ", "------", "------", "------");
227 if (params->zram_enabled) {
228 out << StringPrintf("%7s ", "------");
229 }
230 }
231 }
232
233 out << StringPrintf("%s\n", "------");
234 }
235
print_processrecord(struct params * params,ProcessRecord & proc,std::ostream & out)236 static void print_processrecord(struct params* params, ProcessRecord& proc, std::ostream& out) {
237 out << StringPrintf("%5d ", proc.pid());
238 if (params->show_oomadj) {
239 out << StringPrintf("%5d ", proc.oomadj());
240 }
241
242 if (params->show_wss) {
243 out << StringPrintf("%6" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K ",
244 proc.Usage(params->show_wss).rss, proc.Usage(params->show_wss).pss,
245 proc.Usage(params->show_wss).uss);
246 } else {
247 out << StringPrintf("%7" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K ",
248 proc.Usage(params->show_wss).vss, proc.Usage(params->show_wss).rss,
249 proc.Usage(params->show_wss).pss, proc.Usage(params->show_wss).uss);
250 if (params->swap_enabled) {
251 out << StringPrintf("%6" PRIu64 "K ", proc.Usage(params->show_wss).swap);
252 out << StringPrintf("%6" PRIu64 "K ", proc.proportional_swap());
253 out << StringPrintf("%6" PRIu64 "K ", proc.unique_swap());
254 if (params->zram_enabled) {
255 out << StringPrintf("%6" PRIu64 "K ", proc.zswap());
256 }
257 }
258 }
259 out << proc.cmdline() << "\n";
260 }
261
print_totals(struct params * params,std::ostream & out)262 static void print_totals(struct params* params, std::ostream& out) {
263 out << StringPrintf("%5s ", "");
264 if (params->show_oomadj) {
265 out << StringPrintf("%5s ", "");
266 }
267
268 if (params->show_wss) {
269 out << StringPrintf("%7s %6" PRIu64 "K %6" PRIu64 "K ", "", params->total_pss,
270 params->total_uss);
271 } else {
272 out << StringPrintf("%8s %7s %6" PRIu64 "K %6" PRIu64 "K ", "", "", params->total_pss,
273 params->total_uss);
274 if (params->swap_enabled) {
275 out << StringPrintf("%6" PRIu64 "K ", params->total_swap);
276 out << StringPrintf("%6" PRIu64 "K ", params->total_pswap);
277 out << StringPrintf("%6" PRIu64 "K ", params->total_uswap);
278 if (params->zram_enabled) {
279 out << StringPrintf("%6" PRIu64 "K ", params->total_zswap);
280 }
281 }
282 }
283 out << "TOTAL\n\n";
284 }
285
print_sysmeminfo(struct params * params,const::android::meminfo::SysMemInfo & smi,std::ostream & out)286 static void print_sysmeminfo(struct params* params, const ::android::meminfo::SysMemInfo& smi,
287 std::ostream& out) {
288 if (params->swap_enabled) {
289 out << StringPrintf("ZRAM: %" PRIu64 "K physical used for %" PRIu64 "K in swap (%" PRIu64
290 "K total swap)\n",
291 smi.mem_zram_kb(), (smi.mem_swap_kb() - smi.mem_swap_free_kb()),
292 smi.mem_swap_kb());
293 }
294
295 out << StringPrintf(" RAM: %" PRIu64 "K total, %" PRIu64 "K free, %" PRIu64
296 "K buffers, %" PRIu64 "K cached, %" PRIu64 "K shmem, %" PRIu64 "K slab\n",
297 smi.mem_total_kb(), smi.mem_free_kb(), smi.mem_buffers_kb(),
298 smi.mem_cached_kb(), smi.mem_shmem_kb(), smi.mem_slab_kb());
299 }
300
add_to_totals(struct params * params,ProcessRecord & proc,const std::vector<uint16_t> & swap_offset_array)301 static void add_to_totals(struct params* params, ProcessRecord& proc,
302 const std::vector<uint16_t>& swap_offset_array) {
303 params->total_pss += proc.Usage(params->show_wss).pss;
304 params->total_uss += proc.Usage(params->show_wss).uss;
305 if (!params->show_wss && params->swap_enabled) {
306 proc.CalculateSwap(swap_offset_array, params->zram_compression_ratio);
307 params->total_swap += proc.Usage(params->show_wss).swap;
308 params->total_pswap += proc.proportional_swap();
309 params->total_uswap += proc.unique_swap();
310 if (params->zram_enabled) {
311 params->total_zswap += proc.zswap();
312 }
313 }
314 }
315
316 } // namespace procrank
317
run_procrank(uint64_t pgflags,uint64_t pgflags_mask,const std::set<pid_t> & pids,bool get_oomadj,bool get_wss,SortOrder sort_order,bool reverse_sort,std::map<pid_t,ProcessRecord> * processrecords_ptr,std::ostream & out,std::ostream & err)318 bool run_procrank(uint64_t pgflags, uint64_t pgflags_mask, const std::set<pid_t>& pids,
319 bool get_oomadj, bool get_wss, SortOrder sort_order, bool reverse_sort,
320 std::map<pid_t, ProcessRecord>* processrecords_ptr, std::ostream& out,
321 std::ostream& err) {
322 ::android::meminfo::SysMemInfo smi;
323 if (!smi.ReadMemInfo()) {
324 err << "Failed to get system memory info\n";
325 return false;
326 }
327
328 struct procrank::params params = {
329 .total_pss = 0,
330 .total_uss = 0,
331 .total_swap = 0,
332 .total_pswap = 0,
333 .total_uswap = 0,
334 .total_zswap = 0,
335 .show_oomadj = get_oomadj,
336 .show_wss = get_wss,
337 .swap_enabled = false,
338 .zram_enabled = false,
339 .zram_compression_ratio = 0.0,
340 };
341
342 // Figure out swap and zram.
343 uint64_t swap_total = smi.mem_swap_kb() * 1024;
344 params.swap_enabled = swap_total > 0;
345 // Allocate the swap array.
346 std::vector<uint16_t> swap_offset_array(swap_total / getpagesize() + 1, 0);
347 if (params.swap_enabled) {
348 params.zram_enabled = smi.mem_zram_kb() > 0;
349 if (params.zram_enabled) {
350 params.zram_compression_ratio = static_cast<float>(smi.mem_zram_kb()) /
351 (smi.mem_swap_kb() - smi.mem_swap_free_kb());
352 }
353 }
354
355 std::vector<ProcessRecord> procs;
356 if (!procrank::populate_procs(¶ms, pgflags, pgflags_mask, swap_offset_array, pids, &procs,
357 processrecords_ptr, err)) {
358 return false;
359 }
360
361 if (procs.empty()) {
362 // This would happen in corner cases where procrank is being run to find KSM usage on a
363 // system with no KSM and combined with working set determination as follows
364 // procrank -w -u -k
365 // procrank -w -s -k
366 // procrank -w -o -k
367 out << "<empty>\n\n";
368 procrank::print_sysmeminfo(¶ms, smi, out);
369 return true;
370 }
371
372 // Create sort function based on sort_order, default is PSS descending.
373 std::function<bool(ProcessRecord & a, ProcessRecord & b)> proc_sort =
374 procrank::select_sort(¶ms, sort_order);
375
376 // Sort all process records, default is PSS descending.
377 if (reverse_sort) {
378 std::sort(procs.rbegin(), procs.rend(), proc_sort);
379 } else {
380 std::sort(procs.begin(), procs.end(), proc_sort);
381 }
382
383 procrank::print_header(¶ms, out);
384
385 for (auto& proc : procs) {
386 procrank::add_to_totals(¶ms, proc, swap_offset_array);
387 procrank::print_processrecord(¶ms, proc, out);
388 }
389
390 procrank::print_divider(¶ms, out);
391 procrank::print_totals(¶ms, out);
392 procrank::print_sysmeminfo(¶ms, smi, out);
393
394 return true;
395 }
396
397 namespace librank {
398
add_mem_usage(MemUsage * to,const MemUsage & from)399 static void add_mem_usage(MemUsage* to, const MemUsage& from) {
400 to->vss += from.vss;
401 to->rss += from.rss;
402 to->pss += from.pss;
403 to->uss += from.uss;
404
405 to->swap += from.swap;
406
407 to->private_clean += from.private_clean;
408 to->private_dirty += from.private_dirty;
409 to->shared_clean += from.shared_clean;
410 to->shared_dirty += from.shared_dirty;
411 }
412
413 // Represents a specific process's usage of a library.
414 struct LibProcRecord {
415 public:
LibProcRecordandroid::smapinfo::librank::LibProcRecord416 LibProcRecord(ProcessRecord& proc) : pid_(-1), oomadj_(OOM_SCORE_ADJ_MAX + 1) {
417 pid_ = proc.pid();
418 cmdline_ = proc.cmdline();
419 oomadj_ = proc.oomadj();
420 usage_.clear();
421 }
422
validandroid::smapinfo::librank::LibProcRecord423 bool valid() const { return pid_ != -1; }
AddUsageandroid::smapinfo::librank::LibProcRecord424 void AddUsage(const MemUsage& mem_usage) { add_mem_usage(&usage_, mem_usage); }
425
426 // Getters
pidandroid::smapinfo::librank::LibProcRecord427 pid_t pid() const { return pid_; }
cmdlineandroid::smapinfo::librank::LibProcRecord428 const std::string& cmdline() const { return cmdline_; }
oomadjandroid::smapinfo::librank::LibProcRecord429 int32_t oomadj() const { return oomadj_; }
usageandroid::smapinfo::librank::LibProcRecord430 const MemUsage& usage() const { return usage_; }
431
432 private:
433 pid_t pid_;
434 std::string cmdline_;
435 int32_t oomadj_;
436 MemUsage usage_;
437 };
438
439 // Represents all processes' usage of a specific library.
440 struct LibRecord {
441 public:
LibRecordandroid::smapinfo::librank::LibRecord442 LibRecord(const std::string& name) : name_(name) {}
443
AddUsageandroid::smapinfo::librank::LibRecord444 void AddUsage(const LibProcRecord& proc, const MemUsage& mem_usage) {
445 auto [it, inserted] = procs_.insert(std::pair<pid_t, LibProcRecord>(proc.pid(), proc));
446 // Adds to proc's PID's contribution to usage of this lib, as well as total lib usage.
447 it->second.AddUsage(mem_usage);
448 add_mem_usage(&usage_, mem_usage);
449 }
pssandroid::smapinfo::librank::LibRecord450 uint64_t pss() const { return usage_.pss; }
451
452 // Getters
nameandroid::smapinfo::librank::LibRecord453 const std::string& name() const { return name_; }
processesandroid::smapinfo::librank::LibRecord454 const std::map<pid_t, LibProcRecord>& processes() const { return procs_; }
455
456 private:
457 std::string name_;
458 MemUsage usage_;
459 std::map<pid_t, LibProcRecord> procs_;
460 };
461
select_sort(SortOrder sort_order)462 static std::function<bool(LibProcRecord& a, LibProcRecord& b)> select_sort(SortOrder sort_order) {
463 // Create sort function based on sort_order.
464 std::function<bool(LibProcRecord & a, LibProcRecord & b)> proc_sort;
465 switch (sort_order) {
466 case (SortOrder::BY_RSS):
467 proc_sort = [](LibProcRecord& a, LibProcRecord& b) {
468 return a.usage().rss > b.usage().rss;
469 };
470 break;
471 case (SortOrder::BY_USS):
472 proc_sort = [](LibProcRecord& a, LibProcRecord& b) {
473 return a.usage().uss > b.usage().uss;
474 };
475 break;
476 case (SortOrder::BY_VSS):
477 proc_sort = [](LibProcRecord& a, LibProcRecord& b) {
478 return a.usage().vss > b.usage().vss;
479 };
480 break;
481 case (SortOrder::BY_OOMADJ):
482 proc_sort = [](LibProcRecord& a, LibProcRecord& b) { return a.oomadj() > b.oomadj(); };
483 break;
484 case (SortOrder::BY_PSS):
485 default:
486 proc_sort = [](LibProcRecord& a, LibProcRecord& b) {
487 return a.usage().pss > b.usage().pss;
488 };
489 break;
490 }
491 return proc_sort;
492 }
493
494 struct params {
495 // Filtering options.
496 std::string lib_prefix;
497 bool all_libs;
498 const std::vector<std::string>& excluded_libs;
499 uint16_t mapflags_mask;
500
501 // Print options.
502 Format format;
503 bool swap_enabled;
504 bool show_oomadj;
505 };
506
populate_libs(struct params * params,uint64_t pgflags,uint64_t pgflags_mask,const std::set<pid_t> & pids,std::map<std::string,LibRecord> & lib_name_map,std::map<pid_t,ProcessRecord> * processrecords_ptr,std::ostream & err)507 static bool populate_libs(struct params* params, uint64_t pgflags, uint64_t pgflags_mask,
508 const std::set<pid_t>& pids,
509 std::map<std::string, LibRecord>& lib_name_map,
510 std::map<pid_t, ProcessRecord>* processrecords_ptr, std::ostream& err) {
511 // Fall back to using an empty map of ProcessRecords if nullptr was passed in.
512 std::map<pid_t, ProcessRecord> processrecords;
513 if (!processrecords_ptr) {
514 processrecords_ptr = &processrecords;
515 }
516 for (pid_t pid : pids) {
517 // Check if a ProcessRecord already exists for this pid, create one if one does not exist.
518 auto iter = processrecords_ptr->find(pid);
519 ProcessRecord& proc =
520 (iter != processrecords_ptr->end())
521 ? iter->second
522 : processrecords_ptr
523 ->emplace(pid, ProcessRecord(pid, false, pgflags, pgflags_mask,
524 true, params->show_oomadj, err))
525 .first->second;
526
527 if (!proc.valid()) {
528 err << "error: failed to create process record for: " << pid << "\n";
529 return false;
530 }
531
532 const std::vector<Vma>& maps = proc.Smaps();
533 if (maps.size() == 0) {
534 continue;
535 }
536
537 LibProcRecord record(proc);
538 for (const Vma& map : maps) {
539 // Skip library/map if the prefix for the path doesn't match.
540 if (!params->lib_prefix.empty() &&
541 !::android::base::StartsWith(map.name, params->lib_prefix)) {
542 continue;
543 }
544 // Skip excluded library/map names.
545 if (!params->all_libs &&
546 (std::find(params->excluded_libs.begin(), params->excluded_libs.end(), map.name) !=
547 params->excluded_libs.end())) {
548 continue;
549 }
550 // Skip maps based on map permissions.
551 if (params->mapflags_mask &&
552 ((map.flags & (PROT_READ | PROT_WRITE | PROT_EXEC)) != params->mapflags_mask)) {
553 continue;
554 }
555
556 // Add memory for lib usage.
557 auto [it, inserted] = lib_name_map.emplace(map.name, LibRecord(map.name));
558 it->second.AddUsage(record, map.usage);
559
560 if (!params->swap_enabled && map.usage.swap) {
561 params->swap_enabled = true;
562 }
563 }
564 }
565 return true;
566 }
567
print_header(struct params * params,std::ostream & out)568 static void print_header(struct params* params, std::ostream& out) {
569 switch (params->format) {
570 case Format::RAW:
571 // clang-format off
572 out << std::setw(7) << "RSStot"
573 << std::setw(10) << "VSS"
574 << std::setw(9) << "RSS"
575 << std::setw(9) << "PSS"
576 << std::setw(9) << "USS"
577 << " ";
578 //clang-format on
579 if (params->swap_enabled) {
580 out << std::setw(7) << "Swap"
581 << " ";
582 }
583 if (params->show_oomadj) {
584 out << std::setw(7) << "Oom"
585 << " ";
586 }
587 out << "Name/PID\n";
588 break;
589 case Format::CSV:
590 out << "\"Library\",\"Total_RSS\",\"Process\",\"PID\",\"VSS\",\"RSS\",\"PSS\",\"USS\"";
591 if (params->swap_enabled) {
592 out << ",\"Swap\"";
593 }
594 if (params->show_oomadj) {
595 out << ",\"Oomadj\"";
596 }
597 out << "\n";
598 break;
599 case Format::JSON:
600 default:
601 break;
602 }
603 }
604
print_library(struct params * params,const LibRecord & lib,std::ostream & out)605 static void print_library(struct params* params, const LibRecord& lib,
606 std::ostream& out) {
607 if (params->format == Format::RAW) {
608 // clang-format off
609 out << std::setw(6) << lib.pss() << "K"
610 << std::setw(10) << ""
611 << std::setw(9) << ""
612 << std::setw(9) << ""
613 << std::setw(9) << ""
614 << " ";
615 // clang-format on
616 if (params->swap_enabled) {
617 out << std::setw(7) << ""
618 << " ";
619 }
620 if (params->show_oomadj) {
621 out << std::setw(7) << ""
622 << " ";
623 }
624 out << lib.name() << "\n";
625 }
626 }
627
print_proc_as_raw(struct params * params,const LibProcRecord & p,std::ostream & out)628 static void print_proc_as_raw(struct params* params, const LibProcRecord& p, std::ostream& out) {
629 const MemUsage& usage = p.usage();
630 // clang-format off
631 out << std::setw(7) << ""
632 << std::setw(9) << usage.vss << "K "
633 << std::setw(6) << usage.rss << "K "
634 << std::setw(6) << usage.pss << "K "
635 << std::setw(6) << usage.uss << "K ";
636 // clang-format on
637 if (params->swap_enabled) {
638 out << std::setw(6) << usage.swap << "K ";
639 }
640 if (params->show_oomadj) {
641 out << std::setw(7) << p.oomadj() << " ";
642 }
643 out << " " << p.cmdline() << " [" << p.pid() << "]\n";
644 }
645
print_proc_as_json(struct params * params,const LibRecord & l,const LibProcRecord & p,std::ostream & out)646 static void print_proc_as_json(struct params* params, const LibRecord& l, const LibProcRecord& p,
647 std::ostream& out) {
648 const MemUsage& usage = p.usage();
649 // clang-format off
650 out << "{\"Library\":" << EscapeJsonString(l.name())
651 << ",\"Total_RSS\":" << l.pss()
652 << ",\"Process\":" << EscapeJsonString(p.cmdline())
653 << ",\"PID\":\"" << p.pid() << "\""
654 << ",\"VSS\":" << usage.vss
655 << ",\"RSS\":" << usage.rss
656 << ",\"PSS\":" << usage.pss
657 << ",\"USS\":" << usage.uss;
658 // clang-format on
659 if (params->swap_enabled) {
660 out << ",\"Swap\":" << usage.swap;
661 }
662 if (params->show_oomadj) {
663 out << ",\"Oom\":" << p.oomadj();
664 }
665 out << "}\n";
666 }
667
print_proc_as_csv(struct params * params,const LibRecord & l,const LibProcRecord & p,std::ostream & out)668 static void print_proc_as_csv(struct params* params, const LibRecord& l, const LibProcRecord& p,
669 std::ostream& out) {
670 const MemUsage& usage = p.usage();
671 // clang-format off
672 out << EscapeCsvString(l.name())
673 << "," << l.pss()
674 << "," << EscapeCsvString(p.cmdline())
675 << ",\"[" << p.pid() << "]\""
676 << "," << usage.vss
677 << "," << usage.rss
678 << "," << usage.pss
679 << "," << usage.uss;
680 // clang-format on
681 if (params->swap_enabled) {
682 out << "," << usage.swap;
683 }
684 if (params->show_oomadj) {
685 out << "," << p.oomadj();
686 }
687 out << "\n";
688 }
689
print_procs(struct params * params,const LibRecord & lib,const std::vector<LibProcRecord> & procs,std::ostream & out)690 static void print_procs(struct params* params, const LibRecord& lib,
691 const std::vector<LibProcRecord>& procs, std::ostream& out) {
692 for (const LibProcRecord& p : procs) {
693 switch (params->format) {
694 case Format::RAW:
695 print_proc_as_raw(params, p, out);
696 break;
697 case Format::JSON:
698 print_proc_as_json(params, lib, p, out);
699 break;
700 case Format::CSV:
701 print_proc_as_csv(params, lib, p, out);
702 break;
703 default:
704 break;
705 }
706 }
707 }
708
709 } // namespace librank
710
run_librank(uint64_t pgflags,uint64_t pgflags_mask,const std::set<pid_t> & pids,const std::string & lib_prefix,bool all_libs,const std::vector<std::string> & excluded_libs,uint16_t mapflags_mask,Format format,SortOrder sort_order,bool reverse_sort,std::map<pid_t,ProcessRecord> * processrecords_ptr,std::ostream & out,std::ostream & err)711 bool run_librank(uint64_t pgflags, uint64_t pgflags_mask, const std::set<pid_t>& pids,
712 const std::string& lib_prefix, bool all_libs,
713 const std::vector<std::string>& excluded_libs, uint16_t mapflags_mask,
714 Format format, SortOrder sort_order, bool reverse_sort,
715 std::map<pid_t, ProcessRecord>* processrecords_ptr, std::ostream& out,
716 std::ostream& err) {
717 struct librank::params params = {
718 .lib_prefix = lib_prefix,
719 .all_libs = all_libs,
720 .excluded_libs = excluded_libs,
721 .mapflags_mask = mapflags_mask,
722 .format = format,
723 .swap_enabled = false,
724 .show_oomadj = (sort_order == SortOrder::BY_OOMADJ),
725 };
726
727 // Fills in usage info for each LibRecord.
728 std::map<std::string, librank::LibRecord> lib_name_map;
729 if (!librank::populate_libs(¶ms, pgflags, pgflags_mask, pids, lib_name_map,
730 processrecords_ptr, err)) {
731 return false;
732 }
733
734 librank::print_header(¶ms, out);
735
736 // Create vector of all LibRecords, sorted by descending PSS.
737 std::vector<librank::LibRecord> libs;
738 libs.reserve(lib_name_map.size());
739 for (const auto& [k, v] : lib_name_map) {
740 libs.push_back(v);
741 }
742 std::sort(libs.begin(), libs.end(),
743 [](const librank::LibRecord& l1, const librank::LibRecord& l2) {
744 return l1.pss() > l2.pss();
745 });
746
747 std::function<bool(librank::LibProcRecord & a, librank::LibProcRecord & b)> libproc_sort =
748 librank::select_sort(sort_order);
749 for (librank::LibRecord& lib : libs) {
750 // Sort all processes for this library, default is PSS-descending.
751 std::vector<librank::LibProcRecord> procs;
752 procs.reserve(lib.processes().size());
753 for (const auto& [k, v] : lib.processes()) {
754 procs.push_back(v);
755 }
756 if (reverse_sort) {
757 std::sort(procs.rbegin(), procs.rend(), libproc_sort);
758 } else {
759 std::sort(procs.begin(), procs.end(), libproc_sort);
760 }
761
762 librank::print_library(¶ms, lib, out);
763 librank::print_procs(¶ms, lib, procs, out);
764 }
765
766 return true;
767 }
768
769 namespace showmap {
770
771 // These are defined as static variables instead of a struct (as in procrank::params and
772 // librank::params) because the collect_vma callback references them.
773 static bool show_addr;
774 static bool verbose;
775
get_vma_name(const Vma & vma,bool total,bool is_bss)776 static std::string get_vma_name(const Vma& vma, bool total, bool is_bss) {
777 if (total) {
778 return "TOTAL";
779 }
780 std::string vma_name = vma.name;
781 if (is_bss) {
782 vma_name.append(" [bss]");
783 }
784 return vma_name;
785 }
786
get_flags(const Vma & vma,bool total)787 static std::string get_flags(const Vma& vma, bool total) {
788 std::string flags_str("---");
789 if (verbose && !total) {
790 if (vma.flags & PROT_READ) flags_str[0] = 'r';
791 if (vma.flags & PROT_WRITE) flags_str[1] = 'w';
792 if (vma.flags & PROT_EXEC) flags_str[2] = 'x';
793 }
794 return flags_str;
795 }
796
797 struct VmaInfo {
798 Vma vma;
799 bool is_bss;
800 uint32_t count;
801
802 VmaInfo() = default;
VmaInfoandroid::smapinfo::showmap::VmaInfo803 VmaInfo(const Vma& v) : vma(v), is_bss(false), count(1) {}
VmaInfoandroid::smapinfo::showmap::VmaInfo804 VmaInfo(const Vma& v, bool bss) : vma(v), is_bss(bss), count(1) {}
VmaInfoandroid::smapinfo::showmap::VmaInfo805 VmaInfo(const Vma& v, const std::string& name, bool bss) : vma(v), is_bss(bss), count(1) {
806 vma.name = name;
807 }
808
809 void to_raw(bool total, std::ostream& out) const;
810 void to_csv(bool total, std::ostream& out) const;
811 void to_json(bool total, std::ostream& out) const;
812 };
813
to_raw(bool total,std::ostream & out) const814 void VmaInfo::to_raw(bool total, std::ostream& out) const {
815 if (show_addr) {
816 if (total) {
817 out << " ";
818 } else {
819 out << std::hex << std::setw(16) << vma.start << " " << std::setw(16) << vma.end << " "
820 << std::dec;
821 }
822 }
823 // clang-format off
824 out << std::setw(8) << vma.usage.vss << " "
825 << std::setw(8) << vma.usage.rss << " "
826 << std::setw(8) << vma.usage.pss << " "
827 << std::setw(8) << vma.usage.shared_clean << " "
828 << std::setw(8) << vma.usage.shared_dirty << " "
829 << std::setw(8) << vma.usage.private_clean << " "
830 << std::setw(8) << vma.usage.private_dirty << " "
831 << std::setw(8) << vma.usage.swap << " "
832 << std::setw(8) << vma.usage.swap_pss << " "
833 << std::setw(9) << vma.usage.anon_huge_pages << " "
834 << std::setw(9) << vma.usage.shmem_pmd_mapped << " "
835 << std::setw(9) << vma.usage.file_pmd_mapped << " "
836 << std::setw(8) << vma.usage.shared_hugetlb << " "
837 << std::setw(8) << vma.usage.private_hugetlb << " "
838 << std::setw(8) << vma.usage.locked << " ";
839 // clang-format on
840 if (!verbose && !show_addr) {
841 out << std::setw(4) << count << " ";
842 }
843 if (verbose) {
844 if (total) {
845 out << " ";
846 } else {
847 out << std::setw(5) << get_flags(vma, total) << " ";
848 }
849 }
850 out << get_vma_name(vma, total, is_bss) << "\n";
851 }
852
to_csv(bool total,std::ostream & out) const853 void VmaInfo::to_csv(bool total, std::ostream& out) const {
854 // clang-format off
855 out << vma.usage.vss
856 << "," << vma.usage.rss
857 << "," << vma.usage.pss
858 << "," << vma.usage.shared_clean
859 << "," << vma.usage.shared_dirty
860 << "," << vma.usage.private_clean
861 << "," << vma.usage.private_dirty
862 << "," << vma.usage.swap
863 << "," << vma.usage.swap_pss
864 << "," << vma.usage.anon_huge_pages
865 << "," << vma.usage.shmem_pmd_mapped
866 << "," << vma.usage.file_pmd_mapped
867 << "," << vma.usage.shared_hugetlb
868 << "," << vma.usage.private_hugetlb
869 << "," << vma.usage.locked;
870 // clang-format on
871 if (show_addr) {
872 out << ",";
873 if (total) {
874 out << ",";
875 } else {
876 out << std::hex << vma.start << "," << vma.end << std::dec;
877 }
878 }
879 if (!verbose && !show_addr) {
880 out << "," << count;
881 }
882 if (verbose) {
883 out << ",";
884 if (!total) {
885 out << EscapeCsvString(get_flags(vma, total));
886 }
887 }
888 out << "," << EscapeCsvString(get_vma_name(vma, total, is_bss)) << "\n";
889 }
890
to_json(bool total,std::ostream & out) const891 void VmaInfo::to_json(bool total, std::ostream& out) const {
892 // clang-format off
893 out << "{\"virtual size\":" << vma.usage.vss
894 << ",\"RSS\":" << vma.usage.rss
895 << ",\"PSS\":" << vma.usage.pss
896 << ",\"shared clean\":" << vma.usage.shared_clean
897 << ",\"shared dirty\":" << vma.usage.shared_dirty
898 << ",\"private clean\":" << vma.usage.private_clean
899 << ",\"private dirty\":" << vma.usage.private_dirty
900 << ",\"swap\":" << vma.usage.swap
901 << ",\"swapPSS\":" << vma.usage.swap_pss
902 << ",\"Anon HugePages\":" << vma.usage.anon_huge_pages
903 << ",\"Shmem PmdMapped\":" << vma.usage.shmem_pmd_mapped
904 << ",\"File PmdMapped\":" << vma.usage.file_pmd_mapped
905 << ",\"Shared Hugetlb\":" << vma.usage.shared_hugetlb
906 << ",\"Private Hugetlb\":" << vma.usage.private_hugetlb
907 << ",\"Locked\":" << vma.usage.locked;
908 // clang-format on
909 if (show_addr) {
910 if (total) {
911 out << ",\"start addr\":\"\",\"end addr\":\"\"";
912 } else {
913 out << ",\"start addr\":\"" << std::hex << vma.start << "\",\"end addr\":\"" << vma.end
914 << "\"" << std::dec;
915 }
916 }
917 if (!verbose && !show_addr) {
918 out << ",\"#\":" << count;
919 }
920 if (verbose) {
921 out << ",\"flags\":" << EscapeJsonString(get_flags(vma, total));
922 }
923 out << ",\"object\":" << EscapeJsonString(get_vma_name(vma, total, is_bss)) << "}";
924 }
925
is_library(const std::string & name)926 static bool is_library(const std::string& name) {
927 return (name.size() > 4) && (name[0] == '/') && ::android::base::EndsWith(name, ".so");
928 }
929
infer_vma_name(VmaInfo & current,const VmaInfo & recent)930 static void infer_vma_name(VmaInfo& current, const VmaInfo& recent) {
931 if (current.vma.name.empty()) {
932 if (recent.vma.end == current.vma.start && is_library(recent.vma.name)) {
933 current.vma.name = recent.vma.name;
934 current.is_bss = true;
935 } else {
936 current.vma.name = "[anon]";
937 }
938 }
939 }
940
add_mem_usage(MemUsage * to,const MemUsage & from)941 static void add_mem_usage(MemUsage* to, const MemUsage& from) {
942 to->vss += from.vss;
943 to->rss += from.rss;
944 to->pss += from.pss;
945
946 to->swap += from.swap;
947 to->swap_pss += from.swap_pss;
948
949 to->private_clean += from.private_clean;
950 to->private_dirty += from.private_dirty;
951 to->shared_clean += from.shared_clean;
952 to->shared_dirty += from.shared_dirty;
953
954 to->anon_huge_pages += from.anon_huge_pages;
955 to->shmem_pmd_mapped += from.shmem_pmd_mapped;
956 to->file_pmd_mapped += from.file_pmd_mapped;
957 to->shared_hugetlb += from.shared_hugetlb;
958 to->private_hugetlb += from.private_hugetlb;
959 }
960
961 // A multimap is used instead of a map to allow for duplicate keys in case verbose output is used.
962 static std::multimap<std::string, VmaInfo> vmas;
963
collect_vma(const Vma & vma)964 static void collect_vma(const Vma& vma) {
965 static VmaInfo recent;
966 VmaInfo current(vma);
967
968 std::string key;
969 if (show_addr) {
970 // vma.end is included in case vma.start is identical for two VMAs.
971 key = StringPrintf("%16" PRIx64 "%16" PRIx64, vma.start, vma.end);
972 } else {
973 key = vma.name;
974 }
975
976 if (vmas.empty()) {
977 vmas.emplace(key, current);
978 recent = current;
979 return;
980 }
981
982 infer_vma_name(current, recent);
983 recent = current;
984
985 // If sorting by address, the VMA can be placed into the map as-is.
986 if (show_addr) {
987 vmas.emplace(key, current);
988 return;
989 }
990
991 // infer_vma_name() may have changed current.vma.name, so this key needs to be set again before
992 // using it to sort by name. For verbose output, the VMA can immediately be placed into the map.
993 key = current.vma.name;
994 if (verbose) {
995 vmas.emplace(key, current);
996 return;
997 }
998
999 // Coalesces VMAs' usage by name, if !show_addr && !verbose.
1000 auto iter = vmas.find(key);
1001 if (iter == vmas.end()) {
1002 vmas.emplace(key, current);
1003 return;
1004 }
1005
1006 VmaInfo& match = iter->second;
1007 add_mem_usage(&match.vma.usage, current.vma.usage);
1008 match.is_bss &= current.is_bss;
1009 }
1010
print_text_header(std::ostream & out)1011 static void print_text_header(std::ostream& out) {
1012 if (show_addr) {
1013 out << " start end ";
1014 }
1015 out << " virtual shared shared private private "
1016 "Anon Shmem File Shared Private\n";
1017 if (show_addr) {
1018 out << " addr addr ";
1019 }
1020 out << " size RSS PSS clean dirty clean dirty swap swapPSS "
1021 "HugePages PmdMapped PmdMapped Hugetlb Hugetlb Locked ";
1022 if (!verbose && !show_addr) {
1023 out << " # ";
1024 }
1025 if (verbose) {
1026 out << "flags ";
1027 }
1028 out << "object\n";
1029 }
1030
print_text_divider(std::ostream & out)1031 static void print_text_divider(std::ostream& out) {
1032 if (show_addr) {
1033 out << "---------------- ---------------- ";
1034 }
1035 out << "-------- -------- -------- -------- -------- -------- -------- -------- -------- "
1036 "--------- --------- --------- -------- -------- -------- ";
1037 if (!verbose && !show_addr) {
1038 out << "---- ";
1039 }
1040 if (verbose) {
1041 out << "----- ";
1042 }
1043 out << "------------------------------\n";
1044 }
1045
print_csv_header(std::ostream & out)1046 static void print_csv_header(std::ostream& out) {
1047 out << "\"virtual size\",\"RSS\",\"PSS\",\"shared clean\",\"shared dirty\",\"private clean\","
1048 "\"private dirty\",\"swap\",\"swapPSS\",\"Anon HugePages\",\"Shmem PmdMapped\","
1049 "\"File PmdMapped\",\"Shared Hugetlb\",\"Private Hugetlb\",\"Locked\"";
1050 if (show_addr) {
1051 out << ",\"start addr\",\"end addr\"";
1052 }
1053 if (!verbose && !show_addr) {
1054 out << ",\"#\"";
1055 }
1056 if (verbose) {
1057 out << ",\"flags\"";
1058 }
1059 out << ",\"object\"\n";
1060 }
1061
print_header(Format format,std::ostream & out)1062 static void print_header(Format format, std::ostream& out) {
1063 switch (format) {
1064 case Format::RAW:
1065 print_text_header(out);
1066 print_text_divider(out);
1067 break;
1068 case Format::CSV:
1069 print_csv_header(out);
1070 break;
1071 case Format::JSON:
1072 out << "[";
1073 break;
1074 default:
1075 break;
1076 }
1077 }
1078
print_vmainfo(const VmaInfo & v,Format format,std::ostream & out)1079 static void print_vmainfo(const VmaInfo& v, Format format, std::ostream& out) {
1080 switch (format) {
1081 case Format::RAW:
1082 v.to_raw(false, out);
1083 break;
1084 case Format::CSV:
1085 v.to_csv(false, out);
1086 break;
1087 case Format::JSON:
1088 v.to_json(false, out);
1089 out << ",";
1090 break;
1091 default:
1092 break;
1093 }
1094 }
1095
print_vmainfo_totals(const VmaInfo & total_usage,Format format,std::ostream & out)1096 static void print_vmainfo_totals(const VmaInfo& total_usage, Format format, std::ostream& out) {
1097 switch (format) {
1098 case Format::RAW:
1099 print_text_divider(out);
1100 print_text_header(out);
1101 print_text_divider(out);
1102 total_usage.to_raw(true, out);
1103 break;
1104 case Format::CSV:
1105 total_usage.to_csv(true, out);
1106 break;
1107 case Format::JSON:
1108 total_usage.to_json(true, out);
1109 out << "]\n";
1110 break;
1111 default:
1112 break;
1113 }
1114 }
1115
1116 } // namespace showmap
1117
run_showmap(pid_t pid,const std::string & filename,bool terse,bool verbose,bool show_addr,bool quiet,Format format,std::map<pid_t,ProcessRecord> * processrecords_ptr,std::ostream & out,std::ostream & err)1118 bool run_showmap(pid_t pid, const std::string& filename, bool terse, bool verbose, bool show_addr,
1119 bool quiet, Format format, std::map<pid_t, ProcessRecord>* processrecords_ptr,
1120 std::ostream& out, std::ostream& err) {
1121 // Accumulated vmas are cleared to account for sequential showmap calls by bugreport_procdump.
1122 showmap::vmas.clear();
1123
1124 showmap::show_addr = show_addr;
1125 showmap::verbose = verbose;
1126
1127 bool success;
1128 if (!filename.empty()) {
1129 success = ::android::meminfo::ForEachVmaFromFile(filename, showmap::collect_vma);
1130 } else if (!processrecords_ptr) {
1131 ProcessRecord proc(pid, false, 0, 0, false, false, err);
1132 success = proc.ForEachExistingVma(showmap::collect_vma);
1133 } else {
1134 // Check if a ProcessRecord already exists for this pid, create one if one does not exist.
1135 auto iter = processrecords_ptr->find(pid);
1136 ProcessRecord& proc =
1137 (iter != processrecords_ptr->end())
1138 ? iter->second
1139 : processrecords_ptr
1140 ->emplace(pid, ProcessRecord(pid, false, 0, 0, false, false, err))
1141 .first->second;
1142 success = proc.ForEachExistingVma(showmap::collect_vma);
1143 }
1144
1145 if (!success) {
1146 if (!quiet) {
1147 if (!filename.empty()) {
1148 err << "Failed to parse file " << filename << "\n";
1149 } else {
1150 err << "No maps for pid " << pid << "\n";
1151 }
1152 }
1153 return false;
1154 }
1155
1156 showmap::print_header(format, out);
1157
1158 showmap::VmaInfo total_usage;
1159 for (const auto& entry : showmap::vmas) {
1160 const showmap::VmaInfo& v = entry.second;
1161 showmap::add_mem_usage(&total_usage.vma.usage, v.vma.usage);
1162 total_usage.count += v.count;
1163 if (terse && !(v.vma.usage.private_dirty || v.vma.usage.private_clean)) {
1164 continue;
1165 }
1166 showmap::print_vmainfo(v, format, out);
1167 }
1168 showmap::print_vmainfo_totals(total_usage, format, out);
1169
1170 return true;
1171 }
1172
1173 namespace bugreport_procdump {
1174
create_processrecords(const std::set<pid_t> & pids,std::map<pid_t,ProcessRecord> & processrecords,std::ostream & err)1175 static void create_processrecords(const std::set<pid_t>& pids,
1176 std::map<pid_t, ProcessRecord>& processrecords,
1177 std::ostream& err) {
1178 for (pid_t pid : pids) {
1179 ProcessRecord proc(pid, false, 0, 0, true, false, err);
1180 if (!proc.valid()) {
1181 err << "Could not create a ProcessRecord for pid " << pid << "\n";
1182 continue;
1183 }
1184 processrecords.emplace(pid, std::move(proc));
1185 }
1186 }
1187
print_section_start(const std::string & name,std::ostream & out)1188 static void print_section_start(const std::string& name, std::ostream& out) {
1189 out << "------ " << name << " ------\n";
1190 }
1191
print_section_end(const std::string & name,const std::chrono::time_point<std::chrono::steady_clock> & start,std::ostream & out)1192 static void print_section_end(const std::string& name,
1193 const std::chrono::time_point<std::chrono::steady_clock>& start,
1194 std::ostream& out) {
1195 // std::ratio<1> represents the period for one second.
1196 using floatsecs = std::chrono::duration<float, std::ratio<1>>;
1197 auto end = std::chrono::steady_clock::now();
1198 std::streamsize precision = out.precision();
1199 out << "------ " << std::setprecision(3) << std::fixed << floatsecs(end - start).count()
1200 << " was the duration of '" << name << "' ------\n";
1201 out << std::setprecision(precision) << std::defaultfloat;
1202 }
1203
call_smaps_of_all_processes(const std::string & filename,bool terse,bool verbose,bool show_addr,bool quiet,Format format,std::map<pid_t,ProcessRecord> & processrecords,std::ostream & out,std::ostream & err)1204 static void call_smaps_of_all_processes(const std::string& filename, bool terse, bool verbose,
1205 bool show_addr, bool quiet, Format format,
1206 std::map<pid_t, ProcessRecord>& processrecords,
1207 std::ostream& out, std::ostream& err) {
1208 for (const auto& [pid, record] : processrecords) {
1209 std::string showmap_title = StringPrintf("SHOW MAP %d: %s", pid, record.cmdline().c_str());
1210
1211 auto showmap_start = std::chrono::steady_clock::now();
1212 print_section_start(showmap_title, out);
1213 run_showmap(pid, filename, terse, verbose, show_addr, quiet, format, &processrecords, out,
1214 err);
1215 print_section_end(showmap_title, showmap_start, out);
1216 }
1217 }
1218
call_librank(const std::set<pid_t> & pids,std::map<pid_t,ProcessRecord> & processrecords,std::ostream & out,std::ostream & err)1219 static void call_librank(const std::set<pid_t>& pids,
1220 std::map<pid_t, ProcessRecord>& processrecords, std::ostream& out,
1221 std::ostream& err) {
1222 auto librank_start = std::chrono::steady_clock::now();
1223 print_section_start("LIBRANK", out);
1224 run_librank(0, 0, pids, "", false, {"[heap]", "[stack]"}, 0, Format::RAW, SortOrder::BY_PSS,
1225 false, &processrecords, out, err);
1226 print_section_end("LIBRANK", librank_start, out);
1227 }
1228
call_procrank(const std::set<pid_t> & pids,std::map<pid_t,ProcessRecord> & processrecords,std::ostream & out,std::ostream & err)1229 static void call_procrank(const std::set<pid_t>& pids,
1230 std::map<pid_t, ProcessRecord>& processrecords, std::ostream& out,
1231 std::ostream& err) {
1232 auto procrank_start = std::chrono::steady_clock::now();
1233 print_section_start("PROCRANK", out);
1234 run_procrank(0, 0, pids, false, false, SortOrder::BY_PSS, false, &processrecords, out, err);
1235 print_section_end("PROCRANK", procrank_start, out);
1236 }
1237
1238 } // namespace bugreport_procdump
1239
run_bugreport_procdump(std::ostream & out,std::ostream & err)1240 bool run_bugreport_procdump(std::ostream& out, std::ostream& err) {
1241 std::set<pid_t> pids;
1242 if (!::android::smapinfo::get_all_pids(&pids)) {
1243 err << "Failed to get all pids.\n";
1244 return false;
1245 }
1246
1247 // create_processrecords is the only expensive call in this function, as showmap, librank, and
1248 // procrank will only print already-collected information. This duration is captured by
1249 // dumpstate in the BUGREPORT PROCDUMP section.
1250 std::map<pid_t, ProcessRecord> processrecords;
1251 bugreport_procdump::create_processrecords(pids, processrecords, err);
1252
1253 // pids without associated ProcessRecords are removed so that librank/procrank do not fall back
1254 // to creating new ProcessRecords for them.
1255 for (pid_t pid : pids) {
1256 if (processrecords.find(pid) == processrecords.end()) {
1257 pids.erase(pid);
1258 }
1259 }
1260
1261 auto all_smaps_start = std::chrono::steady_clock::now();
1262 bugreport_procdump::print_section_start("SMAPS OF ALL PROCESSES", out);
1263 bugreport_procdump::call_smaps_of_all_processes("", false, false, false, true, Format::RAW,
1264 processrecords, out, err);
1265 bugreport_procdump::print_section_end("SMAPS OF ALL PROCESSES", all_smaps_start, out);
1266
1267 bugreport_procdump::call_librank(pids, processrecords, out, err);
1268 bugreport_procdump::call_procrank(pids, processrecords, out, err);
1269
1270 return true;
1271 }
1272
1273 } // namespace smapinfo
1274 } // namespace android
1275