• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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