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 <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() && !map.name.starts_with(params->lib_prefix)) {
541 continue;
542 }
543 // Skip excluded library/map names.
544 if (!params->all_libs &&
545 (std::find(params->excluded_libs.begin(), params->excluded_libs.end(), map.name) !=
546 params->excluded_libs.end())) {
547 continue;
548 }
549 // Skip maps based on map permissions.
550 if (params->mapflags_mask &&
551 ((map.flags & (PROT_READ | PROT_WRITE | PROT_EXEC)) != params->mapflags_mask)) {
552 continue;
553 }
554
555 // Add memory for lib usage.
556 auto [it, inserted] = lib_name_map.emplace(map.name, LibRecord(map.name));
557 it->second.AddUsage(record, map.usage);
558
559 if (!params->swap_enabled && map.usage.swap) {
560 params->swap_enabled = true;
561 }
562 }
563 }
564 return true;
565 }
566
print_header(struct params * params,std::ostream & out)567 static void print_header(struct params* params, std::ostream& out) {
568 switch (params->format) {
569 case Format::RAW:
570 // clang-format off
571 out << std::setw(7) << "RSStot"
572 << std::setw(10) << "VSS"
573 << std::setw(9) << "RSS"
574 << std::setw(9) << "PSS"
575 << std::setw(9) << "USS"
576 << " ";
577 //clang-format on
578 if (params->swap_enabled) {
579 out << std::setw(7) << "Swap"
580 << " ";
581 }
582 if (params->show_oomadj) {
583 out << std::setw(7) << "Oom"
584 << " ";
585 }
586 out << "Name/PID\n";
587 break;
588 case Format::CSV:
589 out << "\"Library\",\"Total_RSS\",\"Process\",\"PID\",\"VSS\",\"RSS\",\"PSS\",\"USS\"";
590 if (params->swap_enabled) {
591 out << ",\"Swap\"";
592 }
593 if (params->show_oomadj) {
594 out << ",\"Oomadj\"";
595 }
596 out << "\n";
597 break;
598 case Format::JSON:
599 default:
600 break;
601 }
602 }
603
print_library(struct params * params,const LibRecord & lib,std::ostream & out)604 static void print_library(struct params* params, const LibRecord& lib,
605 std::ostream& out) {
606 if (params->format == Format::RAW) {
607 // clang-format off
608 out << std::setw(6) << lib.pss() << "K"
609 << std::setw(10) << ""
610 << std::setw(9) << ""
611 << std::setw(9) << ""
612 << std::setw(9) << ""
613 << " ";
614 // clang-format on
615 if (params->swap_enabled) {
616 out << std::setw(7) << ""
617 << " ";
618 }
619 if (params->show_oomadj) {
620 out << std::setw(7) << ""
621 << " ";
622 }
623 out << lib.name() << "\n";
624 }
625 }
626
print_proc_as_raw(struct params * params,const LibProcRecord & p,std::ostream & out)627 static void print_proc_as_raw(struct params* params, const LibProcRecord& p, std::ostream& out) {
628 const MemUsage& usage = p.usage();
629 // clang-format off
630 out << std::setw(7) << ""
631 << std::setw(9) << usage.vss << "K "
632 << std::setw(6) << usage.rss << "K "
633 << std::setw(6) << usage.pss << "K "
634 << std::setw(6) << usage.uss << "K ";
635 // clang-format on
636 if (params->swap_enabled) {
637 out << std::setw(6) << usage.swap << "K ";
638 }
639 if (params->show_oomadj) {
640 out << std::setw(7) << p.oomadj() << " ";
641 }
642 out << " " << p.cmdline() << " [" << p.pid() << "]\n";
643 }
644
print_proc_as_json(struct params * params,const LibRecord & l,const LibProcRecord & p,std::ostream & out)645 static void print_proc_as_json(struct params* params, const LibRecord& l, const LibProcRecord& p,
646 std::ostream& out) {
647 const MemUsage& usage = p.usage();
648 // clang-format off
649 out << "{\"Library\":" << EscapeJsonString(l.name())
650 << ",\"Total_RSS\":" << l.pss()
651 << ",\"Process\":" << EscapeJsonString(p.cmdline())
652 << ",\"PID\":\"" << p.pid() << "\""
653 << ",\"VSS\":" << usage.vss
654 << ",\"RSS\":" << usage.rss
655 << ",\"PSS\":" << usage.pss
656 << ",\"USS\":" << usage.uss;
657 // clang-format on
658 if (params->swap_enabled) {
659 out << ",\"Swap\":" << usage.swap;
660 }
661 if (params->show_oomadj) {
662 out << ",\"Oom\":" << p.oomadj();
663 }
664 out << "}\n";
665 }
666
print_proc_as_csv(struct params * params,const LibRecord & l,const LibProcRecord & p,std::ostream & out)667 static void print_proc_as_csv(struct params* params, const LibRecord& l, const LibProcRecord& p,
668 std::ostream& out) {
669 const MemUsage& usage = p.usage();
670 // clang-format off
671 out << EscapeCsvString(l.name())
672 << "," << l.pss()
673 << "," << EscapeCsvString(p.cmdline())
674 << ",\"[" << p.pid() << "]\""
675 << "," << usage.vss
676 << "," << usage.rss
677 << "," << usage.pss
678 << "," << usage.uss;
679 // clang-format on
680 if (params->swap_enabled) {
681 out << "," << usage.swap;
682 }
683 if (params->show_oomadj) {
684 out << "," << p.oomadj();
685 }
686 out << "\n";
687 }
688
print_procs(struct params * params,const LibRecord & lib,const std::vector<LibProcRecord> & procs,std::ostream & out)689 static void print_procs(struct params* params, const LibRecord& lib,
690 const std::vector<LibProcRecord>& procs, std::ostream& out) {
691 for (const LibProcRecord& p : procs) {
692 switch (params->format) {
693 case Format::RAW:
694 print_proc_as_raw(params, p, out);
695 break;
696 case Format::JSON:
697 print_proc_as_json(params, lib, p, out);
698 break;
699 case Format::CSV:
700 print_proc_as_csv(params, lib, p, out);
701 break;
702 default:
703 break;
704 }
705 }
706 }
707
708 } // namespace librank
709
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)710 bool run_librank(uint64_t pgflags, uint64_t pgflags_mask, const std::set<pid_t>& pids,
711 const std::string& lib_prefix, bool all_libs,
712 const std::vector<std::string>& excluded_libs, uint16_t mapflags_mask,
713 Format format, SortOrder sort_order, bool reverse_sort,
714 std::map<pid_t, ProcessRecord>* processrecords_ptr, std::ostream& out,
715 std::ostream& err) {
716 struct librank::params params = {
717 .lib_prefix = lib_prefix,
718 .all_libs = all_libs,
719 .excluded_libs = excluded_libs,
720 .mapflags_mask = mapflags_mask,
721 .format = format,
722 .swap_enabled = false,
723 .show_oomadj = (sort_order == SortOrder::BY_OOMADJ),
724 };
725
726 // Fills in usage info for each LibRecord.
727 std::map<std::string, librank::LibRecord> lib_name_map;
728 if (!librank::populate_libs(¶ms, pgflags, pgflags_mask, pids, lib_name_map,
729 processrecords_ptr, err)) {
730 return false;
731 }
732
733 librank::print_header(¶ms, out);
734
735 // Create vector of all LibRecords, sorted by descending PSS.
736 std::vector<librank::LibRecord> libs;
737 libs.reserve(lib_name_map.size());
738 for (const auto& [k, v] : lib_name_map) {
739 libs.push_back(v);
740 }
741 std::sort(libs.begin(), libs.end(),
742 [](const librank::LibRecord& l1, const librank::LibRecord& l2) {
743 return l1.pss() > l2.pss();
744 });
745
746 std::function<bool(librank::LibProcRecord & a, librank::LibProcRecord & b)> libproc_sort =
747 librank::select_sort(sort_order);
748 for (librank::LibRecord& lib : libs) {
749 // Sort all processes for this library, default is PSS-descending.
750 std::vector<librank::LibProcRecord> procs;
751 procs.reserve(lib.processes().size());
752 for (const auto& [k, v] : lib.processes()) {
753 procs.push_back(v);
754 }
755 if (reverse_sort) {
756 std::sort(procs.rbegin(), procs.rend(), libproc_sort);
757 } else {
758 std::sort(procs.begin(), procs.end(), libproc_sort);
759 }
760
761 librank::print_library(¶ms, lib, out);
762 librank::print_procs(¶ms, lib, procs, out);
763 }
764
765 return true;
766 }
767
768 namespace showmap {
769
770 // These are defined as static variables instead of a struct (as in procrank::params and
771 // librank::params) because the collect_vma callback references them.
772 static bool show_addr;
773 static bool verbose;
774
get_vma_name(const Vma & vma,bool total,bool is_bss)775 static std::string get_vma_name(const Vma& vma, bool total, bool is_bss) {
776 if (total) {
777 return "TOTAL";
778 }
779 std::string vma_name = vma.name;
780 if (is_bss) {
781 vma_name.append(" [bss]");
782 }
783 return vma_name;
784 }
785
get_flags(const Vma & vma,bool total)786 static std::string get_flags(const Vma& vma, bool total) {
787 std::string flags_str("---");
788 if (verbose && !total) {
789 if (vma.flags & PROT_READ) flags_str[0] = 'r';
790 if (vma.flags & PROT_WRITE) flags_str[1] = 'w';
791 if (vma.flags & PROT_EXEC) flags_str[2] = 'x';
792 }
793 return flags_str;
794 }
795
796 struct VmaInfo {
797 Vma vma;
798 bool is_bss;
799 uint32_t count;
800
VmaInfoandroid::smapinfo::showmap::VmaInfo801 VmaInfo() : is_bss(false), count(0) {};
VmaInfoandroid::smapinfo::showmap::VmaInfo802 VmaInfo(const Vma& v) : vma(v), is_bss(false), count(1) {}
VmaInfoandroid::smapinfo::showmap::VmaInfo803 VmaInfo(const Vma& v, bool bss) : vma(v), is_bss(bss), count(1) {}
VmaInfoandroid::smapinfo::showmap::VmaInfo804 VmaInfo(const Vma& v, const std::string& name, bool bss) : vma(v), is_bss(bss), count(1) {
805 vma.name = name;
806 }
807
808 void to_raw(bool total, std::ostream& out) const;
809 void to_csv(bool total, std::ostream& out) const;
810 void to_json(bool total, std::ostream& out) const;
811 };
812
to_raw(bool total,std::ostream & out) const813 void VmaInfo::to_raw(bool total, std::ostream& out) const {
814 if (show_addr) {
815 if (total) {
816 out << " ";
817 } else {
818 out << std::hex << std::setw(16) << vma.start << " " << std::setw(16) << vma.end << " "
819 << std::dec;
820 }
821 }
822 // clang-format off
823 out << std::setw(8) << vma.usage.vss << " "
824 << std::setw(8) << vma.usage.rss << " "
825 << std::setw(8) << vma.usage.pss << " "
826 << std::setw(8) << vma.usage.shared_clean << " "
827 << std::setw(8) << vma.usage.shared_dirty << " "
828 << std::setw(8) << vma.usage.private_clean << " "
829 << std::setw(8) << vma.usage.private_dirty << " "
830 << std::setw(8) << vma.usage.swap << " "
831 << std::setw(8) << vma.usage.swap_pss << " "
832 << std::setw(9) << vma.usage.anon_huge_pages << " "
833 << std::setw(9) << vma.usage.shmem_pmd_mapped << " "
834 << std::setw(9) << vma.usage.file_pmd_mapped << " "
835 << std::setw(8) << vma.usage.shared_hugetlb << " "
836 << std::setw(8) << vma.usage.private_hugetlb << " "
837 << std::setw(8) << vma.usage.locked << " ";
838 // clang-format on
839 if (!verbose && !show_addr) {
840 out << std::setw(4) << count << " ";
841 }
842 if (verbose) {
843 if (total) {
844 out << " ";
845 } else {
846 out << std::setw(5) << get_flags(vma, total) << " ";
847 }
848 }
849 out << get_vma_name(vma, total, is_bss) << "\n";
850 }
851
to_csv(bool total,std::ostream & out) const852 void VmaInfo::to_csv(bool total, std::ostream& out) const {
853 // clang-format off
854 out << vma.usage.vss
855 << "," << vma.usage.rss
856 << "," << vma.usage.pss
857 << "," << vma.usage.shared_clean
858 << "," << vma.usage.shared_dirty
859 << "," << vma.usage.private_clean
860 << "," << vma.usage.private_dirty
861 << "," << vma.usage.swap
862 << "," << vma.usage.swap_pss
863 << "," << vma.usage.anon_huge_pages
864 << "," << vma.usage.shmem_pmd_mapped
865 << "," << vma.usage.file_pmd_mapped
866 << "," << vma.usage.shared_hugetlb
867 << "," << vma.usage.private_hugetlb
868 << "," << vma.usage.locked;
869 // clang-format on
870 if (show_addr) {
871 out << ",";
872 if (total) {
873 out << ",";
874 } else {
875 out << std::hex << vma.start << "," << vma.end << std::dec;
876 }
877 }
878 if (!verbose && !show_addr) {
879 out << "," << count;
880 }
881 if (verbose) {
882 out << ",";
883 if (!total) {
884 out << EscapeCsvString(get_flags(vma, total));
885 }
886 }
887 out << "," << EscapeCsvString(get_vma_name(vma, total, is_bss)) << "\n";
888 }
889
to_json(bool total,std::ostream & out) const890 void VmaInfo::to_json(bool total, std::ostream& out) const {
891 // clang-format off
892 out << "{\"virtual size\":" << vma.usage.vss
893 << ",\"RSS\":" << vma.usage.rss
894 << ",\"PSS\":" << vma.usage.pss
895 << ",\"shared clean\":" << vma.usage.shared_clean
896 << ",\"shared dirty\":" << vma.usage.shared_dirty
897 << ",\"private clean\":" << vma.usage.private_clean
898 << ",\"private dirty\":" << vma.usage.private_dirty
899 << ",\"swap\":" << vma.usage.swap
900 << ",\"swapPSS\":" << vma.usage.swap_pss
901 << ",\"Anon HugePages\":" << vma.usage.anon_huge_pages
902 << ",\"Shmem PmdMapped\":" << vma.usage.shmem_pmd_mapped
903 << ",\"File PmdMapped\":" << vma.usage.file_pmd_mapped
904 << ",\"Shared Hugetlb\":" << vma.usage.shared_hugetlb
905 << ",\"Private Hugetlb\":" << vma.usage.private_hugetlb
906 << ",\"Locked\":" << vma.usage.locked;
907 // clang-format on
908 if (show_addr) {
909 if (total) {
910 out << ",\"start addr\":\"\",\"end addr\":\"\"";
911 } else {
912 out << ",\"start addr\":\"" << std::hex << vma.start << "\",\"end addr\":\"" << vma.end
913 << "\"" << std::dec;
914 }
915 }
916 if (!verbose && !show_addr) {
917 out << ",\"#\":" << count;
918 }
919 if (verbose) {
920 out << ",\"flags\":" << EscapeJsonString(get_flags(vma, total));
921 }
922 out << ",\"object\":" << EscapeJsonString(get_vma_name(vma, total, is_bss)) << "}";
923 }
924
is_library(const std::string & name)925 static bool is_library(const std::string& name) {
926 return (name.size() > 4) && (name[0] == '/') && name.ends_with(".so");
927 }
928
infer_vma_name(VmaInfo & current,const VmaInfo & recent)929 static void infer_vma_name(VmaInfo& current, const VmaInfo& recent) {
930 if (current.vma.name.empty()) {
931 if (recent.vma.end == current.vma.start && is_library(recent.vma.name)) {
932 current.vma.name = recent.vma.name;
933 current.is_bss = true;
934 } else {
935 current.vma.name = "[anon]";
936 }
937 }
938 }
939
add_mem_usage(MemUsage * to,const MemUsage & from)940 static void add_mem_usage(MemUsage* to, const MemUsage& from) {
941 to->vss += from.vss;
942 to->rss += from.rss;
943 to->pss += from.pss;
944
945 to->swap += from.swap;
946 to->swap_pss += from.swap_pss;
947
948 to->private_clean += from.private_clean;
949 to->private_dirty += from.private_dirty;
950 to->shared_clean += from.shared_clean;
951 to->shared_dirty += from.shared_dirty;
952
953 to->anon_huge_pages += from.anon_huge_pages;
954 to->shmem_pmd_mapped += from.shmem_pmd_mapped;
955 to->file_pmd_mapped += from.file_pmd_mapped;
956 to->shared_hugetlb += from.shared_hugetlb;
957 to->private_hugetlb += from.private_hugetlb;
958 to->locked += from.locked;
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 bool 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 true;
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 true;
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 true;
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 true;
1004 }
1005
1006 VmaInfo& match = iter->second;
1007 add_mem_usage(&match.vma.usage, current.vma.usage);
1008 match.count += 1;
1009 match.is_bss &= current.is_bss;
1010 return true;
1011 }
1012
print_text_header(std::ostream & out)1013 static void print_text_header(std::ostream& out) {
1014 if (show_addr) {
1015 out << " start end ";
1016 }
1017 out << " virtual shared shared private private "
1018 "Anon Shmem File Shared Private\n";
1019 if (show_addr) {
1020 out << " addr addr ";
1021 }
1022 out << " size RSS PSS clean dirty clean dirty swap swapPSS "
1023 "HugePages PmdMapped PmdMapped Hugetlb Hugetlb Locked ";
1024 if (!verbose && !show_addr) {
1025 out << " # ";
1026 }
1027 if (verbose) {
1028 out << "flags ";
1029 }
1030 out << "object\n";
1031 }
1032
print_text_divider(std::ostream & out)1033 static void print_text_divider(std::ostream& out) {
1034 if (show_addr) {
1035 out << "---------------- ---------------- ";
1036 }
1037 out << "-------- -------- -------- -------- -------- -------- -------- -------- -------- "
1038 "--------- --------- --------- -------- -------- -------- ";
1039 if (!verbose && !show_addr) {
1040 out << "---- ";
1041 }
1042 if (verbose) {
1043 out << "----- ";
1044 }
1045 out << "------------------------------\n";
1046 }
1047
print_csv_header(std::ostream & out)1048 static void print_csv_header(std::ostream& out) {
1049 out << "\"virtual size\",\"RSS\",\"PSS\",\"shared clean\",\"shared dirty\",\"private clean\","
1050 "\"private dirty\",\"swap\",\"swapPSS\",\"Anon HugePages\",\"Shmem PmdMapped\","
1051 "\"File PmdMapped\",\"Shared Hugetlb\",\"Private Hugetlb\",\"Locked\"";
1052 if (show_addr) {
1053 out << ",\"start addr\",\"end addr\"";
1054 }
1055 if (!verbose && !show_addr) {
1056 out << ",\"#\"";
1057 }
1058 if (verbose) {
1059 out << ",\"flags\"";
1060 }
1061 out << ",\"object\"\n";
1062 }
1063
print_header(Format format,std::ostream & out)1064 static void print_header(Format format, std::ostream& out) {
1065 switch (format) {
1066 case Format::RAW:
1067 print_text_header(out);
1068 print_text_divider(out);
1069 break;
1070 case Format::CSV:
1071 print_csv_header(out);
1072 break;
1073 case Format::JSON:
1074 out << "[";
1075 break;
1076 default:
1077 break;
1078 }
1079 }
1080
print_vmainfo(const VmaInfo & v,Format format,std::ostream & out)1081 static void print_vmainfo(const VmaInfo& v, Format format, std::ostream& out) {
1082 switch (format) {
1083 case Format::RAW:
1084 v.to_raw(false, out);
1085 break;
1086 case Format::CSV:
1087 v.to_csv(false, out);
1088 break;
1089 case Format::JSON:
1090 v.to_json(false, out);
1091 out << ",";
1092 break;
1093 default:
1094 break;
1095 }
1096 }
1097
print_vmainfo_totals(const VmaInfo & total_usage,Format format,std::ostream & out)1098 static void print_vmainfo_totals(const VmaInfo& total_usage, Format format, std::ostream& out) {
1099 switch (format) {
1100 case Format::RAW:
1101 print_text_divider(out);
1102 print_text_header(out);
1103 print_text_divider(out);
1104 total_usage.to_raw(true, out);
1105 break;
1106 case Format::CSV:
1107 total_usage.to_csv(true, out);
1108 break;
1109 case Format::JSON:
1110 total_usage.to_json(true, out);
1111 out << "]\n";
1112 break;
1113 default:
1114 break;
1115 }
1116 }
1117
1118 } // namespace showmap
1119
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)1120 bool run_showmap(pid_t pid, const std::string& filename, bool terse, bool verbose, bool show_addr,
1121 bool quiet, Format format, std::map<pid_t, ProcessRecord>* processrecords_ptr,
1122 std::ostream& out, std::ostream& err) {
1123 // Accumulated vmas are cleared to account for sequential showmap calls by bugreport_procdump.
1124 showmap::vmas.clear();
1125
1126 showmap::show_addr = show_addr;
1127 showmap::verbose = verbose;
1128
1129 bool success;
1130 if (!filename.empty()) {
1131 success = ::android::meminfo::ForEachVmaFromFile(filename, showmap::collect_vma);
1132 } else if (!processrecords_ptr) {
1133 ProcessRecord proc(pid, false, 0, 0, false, false, err);
1134 success = proc.ForEachExistingVma(showmap::collect_vma);
1135 } else {
1136 // Check if a ProcessRecord already exists for this pid, create one if one does not exist.
1137 auto iter = processrecords_ptr->find(pid);
1138 ProcessRecord& proc =
1139 (iter != processrecords_ptr->end())
1140 ? iter->second
1141 : processrecords_ptr
1142 ->emplace(pid, ProcessRecord(pid, false, 0, 0, false, false, err))
1143 .first->second;
1144 success = proc.ForEachExistingVma(showmap::collect_vma);
1145 }
1146
1147 if (!success) {
1148 if (!quiet) {
1149 if (!filename.empty()) {
1150 err << "Failed to parse file " << filename << "\n";
1151 } else {
1152 err << "No maps for pid " << pid << "\n";
1153 }
1154 }
1155 return false;
1156 }
1157
1158 showmap::print_header(format, out);
1159
1160 showmap::VmaInfo total_usage;
1161 for (const auto& entry : showmap::vmas) {
1162 const showmap::VmaInfo& v = entry.second;
1163 showmap::add_mem_usage(&total_usage.vma.usage, v.vma.usage);
1164 total_usage.count += v.count;
1165 if (terse && !(v.vma.usage.private_dirty || v.vma.usage.private_clean)) {
1166 continue;
1167 }
1168 showmap::print_vmainfo(v, format, out);
1169 }
1170 showmap::print_vmainfo_totals(total_usage, format, out);
1171
1172 return true;
1173 }
1174
1175 namespace bugreport_procdump {
1176
create_processrecords(const std::set<pid_t> & pids,std::map<pid_t,ProcessRecord> & processrecords,std::ostream & err)1177 static void create_processrecords(const std::set<pid_t>& pids,
1178 std::map<pid_t, ProcessRecord>& processrecords,
1179 std::ostream& err) {
1180 for (pid_t pid : pids) {
1181 ProcessRecord proc(pid, false, 0, 0, true, false, err);
1182 if (!proc.valid()) {
1183 err << "Could not create a ProcessRecord for pid " << pid << "\n";
1184 continue;
1185 }
1186 processrecords.emplace(pid, std::move(proc));
1187 }
1188 }
1189
print_section_start(const std::string & name,std::ostream & out)1190 static void print_section_start(const std::string& name, std::ostream& out) {
1191 out << "------ " << name << " ------\n";
1192 }
1193
print_section_end(const std::string & name,const std::chrono::time_point<std::chrono::steady_clock> & start,std::ostream & out)1194 static void print_section_end(const std::string& name,
1195 const std::chrono::time_point<std::chrono::steady_clock>& start,
1196 std::ostream& out) {
1197 // std::ratio<1> represents the period for one second.
1198 using floatsecs = std::chrono::duration<float, std::ratio<1>>;
1199 auto end = std::chrono::steady_clock::now();
1200 std::streamsize precision = out.precision();
1201 out << "------ " << std::setprecision(3) << std::fixed << floatsecs(end - start).count()
1202 << " was the duration of '" << name << "' ------\n";
1203 out << std::setprecision(precision) << std::defaultfloat;
1204 }
1205
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)1206 static void call_smaps_of_all_processes(const std::string& filename, bool terse, bool verbose,
1207 bool show_addr, bool quiet, Format format,
1208 std::map<pid_t, ProcessRecord>& processrecords,
1209 std::ostream& out, std::ostream& err) {
1210 for (const auto& [pid, record] : processrecords) {
1211 std::string showmap_title = StringPrintf("SHOW MAP %d: %s", pid, record.cmdline().c_str());
1212
1213 auto showmap_start = std::chrono::steady_clock::now();
1214 print_section_start(showmap_title, out);
1215 run_showmap(pid, filename, terse, verbose, show_addr, quiet, format, &processrecords, out,
1216 err);
1217 print_section_end(showmap_title, showmap_start, out);
1218 }
1219 }
1220
call_librank(const std::set<pid_t> & pids,std::map<pid_t,ProcessRecord> & processrecords,std::ostream & out,std::ostream & err)1221 static void call_librank(const std::set<pid_t>& pids,
1222 std::map<pid_t, ProcessRecord>& processrecords, std::ostream& out,
1223 std::ostream& err) {
1224 auto librank_start = std::chrono::steady_clock::now();
1225 print_section_start("LIBRANK", out);
1226 run_librank(0, 0, pids, "", false, {"[heap]", "[stack]"}, 0, Format::RAW, SortOrder::BY_PSS,
1227 false, &processrecords, out, err);
1228 print_section_end("LIBRANK", librank_start, out);
1229 }
1230
call_procrank(const std::set<pid_t> & pids,std::map<pid_t,ProcessRecord> & processrecords,std::ostream & out,std::ostream & err)1231 static void call_procrank(const std::set<pid_t>& pids,
1232 std::map<pid_t, ProcessRecord>& processrecords, std::ostream& out,
1233 std::ostream& err) {
1234 auto procrank_start = std::chrono::steady_clock::now();
1235 print_section_start("PROCRANK", out);
1236 run_procrank(0, 0, pids, false, false, SortOrder::BY_PSS, false, &processrecords, out, err);
1237 print_section_end("PROCRANK", procrank_start, out);
1238 }
1239
1240 } // namespace bugreport_procdump
1241
run_bugreport_procdump(std::ostream & out,std::ostream & err)1242 bool run_bugreport_procdump(std::ostream& out, std::ostream& err) {
1243 std::set<pid_t> pids;
1244 if (!::android::smapinfo::get_all_pids(&pids)) {
1245 err << "Failed to get all pids.\n";
1246 return false;
1247 }
1248
1249 // create_processrecords is the only expensive call in this function, as showmap, librank, and
1250 // procrank will only print already-collected information. This duration is captured by
1251 // dumpstate in the BUGREPORT PROCDUMP section.
1252 std::map<pid_t, ProcessRecord> processrecords;
1253 bugreport_procdump::create_processrecords(pids, processrecords, err);
1254
1255 // pids without associated ProcessRecords are removed so that librank/procrank do not fall back
1256 // to creating new ProcessRecords for them.
1257 for (pid_t pid : pids) {
1258 if (processrecords.find(pid) == processrecords.end()) {
1259 pids.erase(pid);
1260 }
1261 }
1262
1263 auto all_smaps_start = std::chrono::steady_clock::now();
1264 bugreport_procdump::print_section_start("SMAPS OF ALL PROCESSES", out);
1265 bugreport_procdump::call_smaps_of_all_processes("", false, false, false, true, Format::RAW,
1266 processrecords, out, err);
1267 bugreport_procdump::print_section_end("SMAPS OF ALL PROCESSES", all_smaps_start, out);
1268
1269 bugreport_procdump::call_librank(pids, processrecords, out, err);
1270 bugreport_procdump::call_procrank(pids, processrecords, out, err);
1271
1272 return true;
1273 }
1274
1275 } // namespace smapinfo
1276 } // namespace android
1277