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