1 /*
2 * Copyright (C) 2019 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 <getopt.h>
18 #include <inttypes.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <sys/mman.h>
22 #include <sys/signal.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25
26 #include <memory>
27 #include <string>
28 #include <vector>
29
30 #include <android-base/stringprintf.h>
31 #include <android-base/strings.h>
32 #include <meminfo/procmeminfo.h>
33
34 using ::android::meminfo::Vma;
35
36 struct VmaInfo {
37 Vma vma;
38 bool is_bss;
39 uint32_t count;
40
41 VmaInfo() = default;
VmaInfoVmaInfo42 VmaInfo(const Vma& v) : vma(v), is_bss(false), count(1) {}
VmaInfoVmaInfo43 VmaInfo(const Vma& v, bool bss) : vma(v), is_bss(bss), count(1) {}
VmaInfoVmaInfo44 VmaInfo(const Vma& v, const std::string& name, bool bss) : vma(v), is_bss(bss), count(1) {
45 vma.name = name;
46 }
47 };
48
49 // Global options
50 static std::string g_filename = "";
51 static bool g_merge_by_names = false;
52 static bool g_terse = false;
53 static bool g_verbose = false;
54 static bool g_show_addr = false;
55 static bool g_quiet = false;
56 static pid_t g_pid = -1;
57
58 static VmaInfo g_total;
59 static std::vector<VmaInfo> g_vmas;
60
usage(const char * progname,int exit_status)61 [[noreturn]] static void usage(const char* progname, int exit_status) {
62 fprintf(stderr,
63 "%s [-aqtv] [-f FILE] PID\n"
64 "-a\taddresses (show virtual memory map)\n"
65 "-q\tquiet (don't show error if map could not be read)\n"
66 "-t\tterse (show only items with private pages)\n"
67 "-v\tverbose (don't coalesce maps with the same name)\n"
68 "-f\tFILE (read from input from FILE instead of PID)\n",
69 progname);
70
71 exit(exit_status);
72 }
73
is_library(const std::string & name)74 static bool is_library(const std::string& name) {
75 return (name.size() > 4) && (name[0] == '/') && ::android::base::EndsWith(name, ".so");
76 }
77
insert_before(const VmaInfo & a,const VmaInfo & b)78 static bool insert_before(const VmaInfo& a, const VmaInfo& b) {
79 if (g_show_addr) {
80 return (a.vma.start < b.vma.start || (a.vma.start == b.vma.start && a.vma.end < b.vma.end));
81 }
82
83 return strcmp(a.vma.name.c_str(), b.vma.name.c_str()) < 0;
84 }
85
collect_vma(const Vma & vma)86 static void collect_vma(const Vma& vma) {
87 if (g_vmas.empty()) {
88 g_vmas.emplace_back(vma);
89 return;
90 }
91
92 VmaInfo current(vma);
93 VmaInfo& last = g_vmas.back();
94 // determine if this is bss;
95 if (vma.name.empty()) {
96 if (last.vma.end == current.vma.start && is_library(last.vma.name)) {
97 current.vma.name = last.vma.name;
98 current.is_bss = true;
99 } else {
100 current.vma.name = "[anon]";
101 }
102 }
103
104 std::vector<VmaInfo>::iterator it;
105 for (it = g_vmas.begin(); it != g_vmas.end(); it++) {
106 if (g_merge_by_names && (it->vma.name == current.vma.name)) {
107 it->vma.usage.vss += current.vma.usage.vss;
108 it->vma.usage.rss += current.vma.usage.rss;
109 it->vma.usage.pss += current.vma.usage.pss;
110
111 it->vma.usage.shared_clean += current.vma.usage.shared_clean;
112 it->vma.usage.shared_dirty += current.vma.usage.shared_dirty;
113 it->vma.usage.private_clean += current.vma.usage.private_clean;
114 it->vma.usage.private_dirty += current.vma.usage.private_dirty;
115 it->vma.usage.swap += current.vma.usage.swap;
116 it->vma.usage.swap_pss += current.vma.usage.swap_pss;
117 it->is_bss &= current.is_bss;
118 it->count++;
119 break;
120 }
121
122 if (insert_before(current, *it)) {
123 g_vmas.insert(it, current);
124 break;
125 }
126 }
127
128 if (it == g_vmas.end()) {
129 g_vmas.emplace_back(current);
130 }
131 }
132
print_header()133 static void print_header() {
134 const char* addr1 = g_show_addr ? " start end " : "";
135 const char* addr2 = g_show_addr ? " addr addr " : "";
136
137 printf("%s virtual shared shared private private\n", addr1);
138 printf("%s size RSS PSS clean dirty clean dirty swap swapPSS",
139 addr2);
140 if (!g_verbose && !g_show_addr) {
141 printf(" # ");
142 }
143 if (g_verbose) {
144 printf(" flags ");
145 }
146 printf(" object\n");
147 }
148
print_divider()149 static void print_divider() {
150 if (g_show_addr) {
151 printf("-------- -------- ");
152 }
153 printf("-------- -------- -------- -------- -------- -------- -------- -------- -------- ");
154 if (!g_verbose && !g_show_addr) {
155 printf("---- ");
156 }
157 if (g_verbose) {
158 printf("------ ");
159 }
160 printf("------------------------------\n");
161 }
162
print_vmainfo(const VmaInfo & v,bool total)163 static void print_vmainfo(const VmaInfo& v, bool total) {
164 if (g_show_addr) {
165 if (total) {
166 printf(" ");
167 } else {
168 printf("%16" PRIx64 " %16" PRIx64 " ", v.vma.start, v.vma.end);
169 }
170 }
171 printf("%8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64
172 " %8" PRIu64 " %8" PRIu64 " ",
173 v.vma.usage.vss, v.vma.usage.rss, v.vma.usage.pss, v.vma.usage.shared_clean,
174 v.vma.usage.shared_dirty, v.vma.usage.private_clean, v.vma.usage.private_dirty,
175 v.vma.usage.swap, v.vma.usage.swap_pss);
176 if (!g_verbose && !g_show_addr) {
177 printf("%4" PRIu32 " ", v.count);
178 }
179 if (g_verbose) {
180 if (total) {
181 printf(" ");
182 } else {
183 std::string flags_str("---");
184 if (v.vma.flags & PROT_READ) flags_str[0] = 'r';
185 if (v.vma.flags & PROT_WRITE) flags_str[1] = 'w';
186 if (v.vma.flags & PROT_EXEC) flags_str[2] = 'x';
187
188 printf("%6s ", flags_str.c_str());
189 }
190 }
191 }
192
showmap(void)193 static int showmap(void) {
194 if (!::android::meminfo::ForEachVmaFromFile(g_filename, collect_vma)) {
195 if (!g_quiet) {
196 fprintf(stderr, "Failed to parse file %s\n", g_filename.c_str());
197 }
198 return 1;
199 }
200
201 print_header();
202 print_divider();
203
204 for (const auto& v : g_vmas) {
205 g_total.vma.usage.vss += v.vma.usage.vss;
206 g_total.vma.usage.rss += v.vma.usage.rss;
207 g_total.vma.usage.pss += v.vma.usage.pss;
208
209 g_total.vma.usage.private_clean += v.vma.usage.private_clean;
210 g_total.vma.usage.private_dirty += v.vma.usage.private_dirty;
211 g_total.vma.usage.shared_clean += v.vma.usage.shared_clean;
212 g_total.vma.usage.shared_dirty += v.vma.usage.shared_dirty;
213
214 g_total.vma.usage.swap += v.vma.usage.swap;
215 g_total.vma.usage.swap_pss += v.vma.usage.swap_pss;
216 g_total.count += v.count;
217
218 if (g_terse && !(v.vma.usage.private_dirty || v.vma.usage.private_clean)) {
219 continue;
220 }
221
222 print_vmainfo(v, false);
223 printf("%s%s\n", v.vma.name.c_str(), v.is_bss ? " [bss]" : "");
224 }
225
226 print_divider();
227 print_header();
228 print_divider();
229
230 print_vmainfo(g_total, true);
231 printf("TOTAL\n");
232
233 return 0;
234 }
235
main(int argc,char * argv[])236 int main(int argc, char* argv[]) {
237 signal(SIGPIPE, SIG_IGN);
238 struct option longopts[] = {
239 {"help", no_argument, nullptr, 'h'},
240 {0, 0, nullptr, 0},
241 };
242
243 int opt;
244 while ((opt = getopt_long(argc, argv, "tvaqf:h", longopts, nullptr)) != -1) {
245 switch (opt) {
246 case 't':
247 g_terse = true;
248 break;
249 case 'a':
250 g_show_addr = true;
251 break;
252 case 'v':
253 g_verbose = true;
254 break;
255 case 'q':
256 g_quiet = true;
257 break;
258 case 'f':
259 g_filename = optarg;
260 break;
261 case 'h':
262 usage(argv[0], EXIT_SUCCESS);
263 default:
264 usage(argv[0], EXIT_FAILURE);
265 }
266 }
267
268 if (g_filename.empty()) {
269 if ((argc - 1) < optind) {
270 fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n");
271 usage(argv[0], EXIT_FAILURE);
272 }
273
274 g_pid = atoi(argv[optind]);
275 if (g_pid <= 0) {
276 fprintf(stderr, "Invalid process id %s\n", argv[optind]);
277 usage(argv[0], EXIT_FAILURE);
278 }
279
280 g_filename = ::android::base::StringPrintf("/proc/%d/smaps", g_pid);
281 }
282
283 g_merge_by_names = !g_verbose && !g_show_addr;
284 return showmap();
285 }
286