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