1 /*
2  * Copyright (C) 2018 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 <errno.h>
18 #include <inttypes.h>
19 #include <stdlib.h>
20 #include <sys/mman.h>
21 #include <unistd.h>
22 
23 #include <iostream>
24 #include <sstream>
25 #include <string>
26 #include <vector>
27 
28 #include <android-base/stringprintf.h>
29 #include <meminfo/procmeminfo.h>
30 
31 using Vma = ::android::meminfo::Vma;
32 using ProcMemInfo = ::android::meminfo::ProcMemInfo;
33 using MemUsage = ::android::meminfo::MemUsage;
34 
35 // Global flags to control procmem output
36 
37 // Set to use page idle bits for working set detection
38 bool use_pageidle = false;
39 // hides map entries with zero rss
40 bool hide_zeroes = false;
41 // Reset working set and exit
42 bool reset_wss = false;
43 // Show working set, mutually exclusive with reset_wss;
44 bool show_wss = false;
45 
46 [[noreturn]] static void usage(int exit_status) {
47     fprintf(stderr,
48             "Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
49             "    -i  Uses idle page tracking for working set statistics.\n"
50             "    -w  Displays statistics for the working set only.\n"
51             "    -W  Resets the working set of the process.\n"
52             "    -p  Sort by PSS.\n"
53             "    -u  Sort by USS.\n"
54             "    -m  Sort by mapping order (as read from /proc).\n"
55             "    -h  Hide maps with no RSS.\n",
56             getprogname());
57 
58     exit(exit_status);
59 }
60 
61 static void print_separator(std::stringstream& ss) {
62     if (show_wss) {
63         ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n",
64                                             "-------", "-------", "-------", "-------", "-------",
65                                             "-------", "-------", "-------", "");
66         return;
67     }
68     ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n",
69                                         "-------", "-------", "-------", "-------", "-------",
70                                         "-------", "-------", "-------", "-------", "");
71 }
72 
73 static void print_header(std::stringstream& ss) {
74     if (show_wss) {
75         ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "WRss",
76                                             "WPss", "WUss", "WShCl", "WShDi", "WPrCl", "WPrDi",
77                                             "Flags", "Name");
78     } else {
79         ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n",
80                                             "Vss", "Rss", "Pss", "Uss", "ShCl", "ShDi", "PrCl",
81                                             "PrDi", "Flags", "Name");
82     }
83     print_separator(ss);
84 }
85 
86 static void print_stats(std::stringstream& ss, const MemUsage& stats) {
87     if (!show_wss) {
88         ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", stats.vss / 1024);
89     }
90 
91     ss << ::android::base::StringPrintf("%6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64
92                                         "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ",
93                                         stats.rss / 1024, stats.pss / 1024, stats.uss / 1024,
94                                         stats.shared_clean / 1024, stats.shared_dirty / 1024,
95                                         stats.private_clean / 1024, stats.private_dirty / 1024);
96 }
97 
98 static int show(const MemUsage& proc_stats, const std::vector<Vma>& maps) {
99     std::stringstream ss;
100     print_header(ss);
101     for (auto& vma : maps) {
102         const MemUsage& vma_stats = vma.usage;
103         if (hide_zeroes && vma_stats.rss == 0) {
104             continue;
105         }
106         print_stats(ss, vma_stats);
107 
108         // TODO: b/141711064 fix libprocinfo to record (p)rivate or (s)hared flag
109         // for now always report as private
110         std::string flags_str("---p");
111         if (vma.flags & PROT_READ) flags_str[0] = 'r';
112         if (vma.flags & PROT_WRITE) flags_str[1] = 'w';
113         if (vma.flags & PROT_EXEC) flags_str[2] = 'x';
114 
115         ss << ::android::base::StringPrintf("%7s  ", flags_str.c_str()) << vma.name << std::endl;
116     }
117     print_separator(ss);
118     print_stats(ss, proc_stats);
119     ss << "TOTAL" << std::endl;
120     std::cout << ss.str();
121 
122     return 0;
123 }
124 
125 int main(int argc, char* argv[]) {
126     int opt;
127     auto pss_sort = [](const Vma& a, const Vma& b) {
128         uint64_t pss_a = a.usage.pss;
129         uint64_t pss_b = b.usage.pss;
130         return pss_a > pss_b;
131     };
132 
133     auto uss_sort = [](const Vma& a, const Vma& b) {
134         uint64_t uss_a = a.usage.uss;
135         uint64_t uss_b = b.usage.uss;
136         return uss_a > uss_b;
137     };
138 
139     std::function<bool(const Vma& a, const Vma& b)> sort_func = nullptr;
140     while ((opt = getopt(argc, argv, "himpuWw")) != -1) {
141         switch (opt) {
142             case 'h':
143                 hide_zeroes = true;
144                 break;
145             case 'i':
146                 // TODO: libmeminfo doesn't support the flag to chose
147                 // between idle page tracking vs clear_refs. So for now,
148                 // this flag is unused and the library defaults to using
149                 // /proc/<pid>/clear_refs for finding the working set.
150                 use_pageidle = true;
151                 break;
152             case 'm':
153                 // this is the default
154                 break;
155             case 'p':
156                 sort_func = pss_sort;
157                 break;
158             case 'u':
159                 sort_func = uss_sort;
160                 break;
161             case 'W':
162                 reset_wss = true;
163                 break;
164             case 'w':
165                 show_wss = true;
166                 break;
167             case '?':
168                 usage(EXIT_SUCCESS);
169             default:
170                 usage(EXIT_FAILURE);
171         }
172     }
173 
174     if (optind != (argc - 1)) {
175         fprintf(stderr, "Need exactly one pid at the end\n");
176         usage(EXIT_FAILURE);
177     }
178 
179     pid_t pid = atoi(argv[optind]);
180     if (pid == 0) {
181         std::cerr << "Invalid process id" << std::endl;
182         exit(EXIT_FAILURE);
183     }
184 
185     if (reset_wss) {
186         if (!ProcMemInfo::ResetWorkingSet(pid)) {
187             std::cerr << "Failed to reset working set of pid : " << pid << std::endl;
188             exit(EXIT_FAILURE);
189         }
190         return 0;
191     }
192 
193     ProcMemInfo proc(pid, show_wss);
194     const MemUsage& proc_stats = proc.Usage();
195     std::vector<Vma> maps(proc.Maps());
196     if (sort_func != nullptr) {
197         std::sort(maps.begin(), maps.end(), sort_func);
198     }
199 
200     return show(proc_stats, maps);
201 }
202