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