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(&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() && !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(&params, pgflags, pgflags_mask, pids, lib_name_map,
729                                 processrecords_ptr, err)) {
730         return false;
731     }
732 
733     librank::print_header(&params, 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(&params, lib, out);
762         librank::print_procs(&params, 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