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