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