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 <algorithm>
25 #include <memory>
26 #include <string>
27 #include <vector>
28 
29 #include <meminfo/pageacct.h>
30 #include <meminfo/procmeminfo.h>
31 
32 using ::android::meminfo::ProcMemInfo;
33 using ::android::meminfo::Vma;
34 
35 // Global options
36 static int32_t g_delay = 0;
37 static int32_t g_total = 2;
38 static pid_t g_pid = -1;
39 
usage(int exit_status)40 [[noreturn]] static void usage(int exit_status) {
41     fprintf(stderr,
42             "%s [-d DELAY_BETWEEN_EACH_SAMPLE] [-n REFRESH_TOTAL] PID\n"
43             "-d\tdelay between each working set sample (default 0)\n"
44             "-n\ttotal number of refreshes before we exit (default 2)\n",
45             getprogname());
46 
47     exit(exit_status);
48 }
49 
print_header()50 static void print_header() {
51     const char* addr1 = "           start              end ";
52     const char* addr2 = "            addr             addr ";
53 
54     printf("%s  virtual                        shared    shared   private   private\n", addr1);
55     printf("%s     size       RSS       PSS     clean     dirty     clean     dirty      swap   "
56            "swapPSS",
57            addr2);
58     printf(" object\n");
59 }
60 
print_divider()61 static void print_divider() {
62     printf("---------------- ---------------- ");
63     printf("--------- --------- --------- --------- --------- --------- --------- --------- "
64            "--------- ");
65     printf("------------------------------\n");
66 }
67 
print_vma(const Vma & v)68 static void print_vma(const Vma& v) {
69     printf("%16" PRIx64 " %16" PRIx64 " ", v.start, v.end);
70     printf("%8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64
71            "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K ",
72            v.usage.vss / 1024, v.usage.rss / 1024, v.usage.pss / 1024, v.usage.shared_clean / 1024,
73            v.usage.shared_dirty / 1024, v.usage.private_clean / 1024, v.usage.private_dirty / 1024,
74            v.usage.swap / 1024, v.usage.swap_pss / 1024);
75     printf("%s\n", v.name.c_str());
76 }
77 
same_vma(const Vma & cur,const Vma & last)78 static bool same_vma(const Vma& cur, const Vma& last) {
79     return (cur.start == last.start && cur.end == last.end && cur.name == last.name &&
80             cur.flags == last.flags && cur.offset == last.offset);
81 }
82 
diff_vma_params(const Vma & cur,const Vma & last)83 static Vma diff_vma_params(const Vma& cur, const Vma& last) {
84     Vma res;
85     res.usage.shared_clean = cur.usage.shared_clean > last.usage.shared_clean
86                                      ? cur.usage.shared_clean - last.usage.shared_clean
87                                      : 0;
88     res.usage.shared_dirty = cur.usage.shared_dirty > last.usage.shared_dirty
89                                      ? cur.usage.shared_dirty - last.usage.shared_dirty
90                                      : 0;
91     res.usage.private_clean = cur.usage.private_clean > last.usage.private_clean
92                                       ? cur.usage.private_clean - last.usage.private_clean
93                                       : 0;
94     res.usage.private_dirty = cur.usage.private_dirty > last.usage.private_dirty
95                                       ? cur.usage.private_dirty - last.usage.private_dirty
96                                       : 0;
97 
98     res.usage.rss = cur.usage.rss > last.usage.rss ? cur.usage.rss - last.usage.rss : 0;
99     res.usage.pss = cur.usage.pss > last.usage.pss ? cur.usage.pss - last.usage.pss : 0;
100     res.usage.uss = cur.usage.uss > last.usage.uss ? cur.usage.uss - last.usage.uss : 0;
101     res.usage.swap = cur.usage.swap > last.usage.swap ? cur.usage.swap - last.usage.swap : 0;
102     res.usage.swap_pss =
103             cur.usage.swap_pss > last.usage.swap_pss ? cur.usage.swap_pss - last.usage.swap_pss : 0;
104 
105     // set vma properties to the same as the current one.
106     res.start = cur.start;
107     res.end = cur.end;
108     res.offset = cur.offset;
109     res.flags = cur.flags;
110     res.name = cur.name;
111     return res;
112 }
113 
diff_workingset(std::vector<Vma> & wss,std::vector<Vma> & old,std::vector<Vma> * res)114 static void diff_workingset(std::vector<Vma>& wss, std::vector<Vma>& old, std::vector<Vma>* res) {
115     res->clear();
116     auto vma_sorter = [](const Vma& a, const Vma& b) { return a.start < b.start; };
117     std::sort(wss.begin(), wss.end(), vma_sorter);
118     std::sort(old.begin(), old.end(), vma_sorter);
119     if (old.empty()) {
120         *res = wss;
121         return;
122     }
123 
124     for (auto& i : wss) {
125         bool found_same_vma = false;
126         // TODO: This is highly inefficient, fix it if it takes
127         // too long. Worst case will be system_server
128         for (auto& j : old) {
129             if (same_vma(i, j)) {
130                 res->emplace_back(diff_vma_params(i, j));
131                 found_same_vma = true;
132                 break;
133             }
134         }
135 
136         if (!found_same_vma) {
137             res->emplace_back(i);
138         }
139     }
140 
141     std::sort(res->begin(), res->end(), vma_sorter);
142     return;
143 }
144 
workingset()145 static int workingset() {
146     std::vector<Vma> last_wss = {};
147     std::vector<Vma> diff_wss = {};
148     uint32_t nr_refresh = 0;
149 
150     while (true) {
151         std::unique_ptr<ProcMemInfo> proc_mem = std::make_unique<ProcMemInfo>(g_pid, true);
152         std::vector<Vma> wss = proc_mem->MapsWithPageIdle();
153 
154         diff_workingset(wss, last_wss, &diff_wss);
155         diff_wss.erase(std::remove_if(diff_wss.begin(), diff_wss.end(),
156                                       [](const auto& v) { return v.usage.rss == 0; }),
157                        diff_wss.end());
158         if ((nr_refresh % 5) == 0) {
159             print_header();
160             print_divider();
161         }
162 
163         for (const auto& v : diff_wss) {
164             print_vma(v);
165         }
166 
167         nr_refresh++;
168         if (nr_refresh == g_total) {
169             break;
170         }
171 
172         last_wss = wss;
173         sleep(g_delay);
174         print_divider();
175     }
176 
177     return 0;
178 }
179 
main(int argc,char * argv[])180 int main(int argc, char* argv[]) {
181     struct option longopts[] = {
182             {"help", no_argument, nullptr, 'h'},
183             {0, 0, nullptr, 0},
184     };
185 
186     int opt;
187     while ((opt = getopt_long(argc, argv, "d:n:h", longopts, nullptr)) != -1) {
188         switch (opt) {
189             case 'd':
190                 g_delay = atoi(optarg);
191                 break;
192             case 'n':
193                 g_total = atoi(optarg);
194                 break;
195             case 'h':
196                 usage(EXIT_SUCCESS);
197             default:
198                 usage(EXIT_FAILURE);
199         }
200     }
201 
202     if ((argc - 1) < optind) {
203         fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n");
204         usage(EXIT_FAILURE);
205     }
206 
207     g_pid = atoi(argv[optind]);
208     if (g_pid <= 0) {
209         fprintf(stderr, "Invalid process id %s\n", argv[optind]);
210         usage(EXIT_FAILURE);
211     }
212 
213     if (!::android::meminfo::PageAcct::KernelHasPageIdle()) {
214         fprintf(stderr, "Missing support for Idle page tracking in the kernel\n");
215         return 0;
216     }
217 
218     return workingset();
219 }
220