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, v.usage.rss, v.usage.pss, v.usage.shared_clean, v.usage.shared_dirty,
73 v.usage.private_clean, v.usage.private_dirty, v.usage.swap, v.usage.swap_pss);
74 printf("%s\n", v.name.c_str());
75 }
76
same_vma(const Vma & cur,const Vma & last)77 static bool same_vma(const Vma& cur, const Vma& last) {
78 return (cur.start == last.start && cur.end == last.end && cur.name == last.name &&
79 cur.flags == last.flags && cur.offset == last.offset);
80 }
81
diff_vma_params(const Vma & cur,const Vma & last)82 static Vma diff_vma_params(const Vma& cur, const Vma& last) {
83 Vma res;
84 res.usage.shared_clean = cur.usage.shared_clean > last.usage.shared_clean
85 ? cur.usage.shared_clean - last.usage.shared_clean
86 : 0;
87 res.usage.shared_dirty = cur.usage.shared_dirty > last.usage.shared_dirty
88 ? cur.usage.shared_dirty - last.usage.shared_dirty
89 : 0;
90 res.usage.private_clean = cur.usage.private_clean > last.usage.private_clean
91 ? cur.usage.private_clean - last.usage.private_clean
92 : 0;
93 res.usage.private_dirty = cur.usage.private_dirty > last.usage.private_dirty
94 ? cur.usage.private_dirty - last.usage.private_dirty
95 : 0;
96
97 res.usage.rss = cur.usage.rss > last.usage.rss ? cur.usage.rss - last.usage.rss : 0;
98 res.usage.pss = cur.usage.pss > last.usage.pss ? cur.usage.pss - last.usage.pss : 0;
99 res.usage.uss = cur.usage.uss > last.usage.uss ? cur.usage.uss - last.usage.uss : 0;
100 res.usage.swap = cur.usage.swap > last.usage.swap ? cur.usage.swap - last.usage.swap : 0;
101 res.usage.swap_pss =
102 cur.usage.swap_pss > last.usage.swap_pss ? cur.usage.swap_pss - last.usage.swap_pss : 0;
103
104 // set vma properties to the same as the current one.
105 res.start = cur.start;
106 res.end = cur.end;
107 res.offset = cur.offset;
108 res.flags = cur.flags;
109 res.name = cur.name;
110 return res;
111 }
112
diff_workingset(std::vector<Vma> & wss,std::vector<Vma> & old,std::vector<Vma> * res)113 static void diff_workingset(std::vector<Vma>& wss, std::vector<Vma>& old, std::vector<Vma>* res) {
114 res->clear();
115 auto vma_sorter = [](const Vma& a, const Vma& b) { return a.start < b.start; };
116 std::sort(wss.begin(), wss.end(), vma_sorter);
117 std::sort(old.begin(), old.end(), vma_sorter);
118 if (old.empty()) {
119 *res = wss;
120 return;
121 }
122
123 for (auto& i : wss) {
124 bool found_same_vma = false;
125 // TODO: This is highly inefficient, fix it if it takes
126 // too long. Worst case will be system_server
127 for (auto& j : old) {
128 if (same_vma(i, j)) {
129 res->emplace_back(diff_vma_params(i, j));
130 found_same_vma = true;
131 break;
132 }
133 }
134
135 if (!found_same_vma) {
136 res->emplace_back(i);
137 }
138 }
139
140 std::sort(res->begin(), res->end(), vma_sorter);
141 return;
142 }
143
workingset()144 static int workingset() {
145 std::vector<Vma> last_wss = {};
146 std::vector<Vma> diff_wss = {};
147 uint32_t nr_refresh = 0;
148
149 while (true) {
150 std::unique_ptr<ProcMemInfo> proc_mem = std::make_unique<ProcMemInfo>(g_pid, true);
151 std::vector<Vma> wss = proc_mem->MapsWithPageIdle();
152
153 diff_workingset(wss, last_wss, &diff_wss);
154 diff_wss.erase(std::remove_if(diff_wss.begin(), diff_wss.end(),
155 [](const auto& v) { return v.usage.rss == 0; }),
156 diff_wss.end());
157 if ((nr_refresh % 5) == 0) {
158 print_header();
159 print_divider();
160 }
161
162 for (const auto& v : diff_wss) {
163 print_vma(v);
164 }
165
166 nr_refresh++;
167 if (nr_refresh == g_total) {
168 break;
169 }
170
171 last_wss = wss;
172 sleep(g_delay);
173 print_divider();
174 }
175
176 return 0;
177 }
178
main(int argc,char * argv[])179 int main(int argc, char* argv[]) {
180 struct option longopts[] = {
181 {"help", no_argument, nullptr, 'h'},
182 {0, 0, nullptr, 0},
183 };
184
185 int opt;
186 while ((opt = getopt_long(argc, argv, "d:n:h", longopts, nullptr)) != -1) {
187 switch (opt) {
188 case 'd':
189 g_delay = atoi(optarg);
190 break;
191 case 'n':
192 g_total = atoi(optarg);
193 break;
194 case 'h':
195 usage(EXIT_SUCCESS);
196 default:
197 usage(EXIT_FAILURE);
198 }
199 }
200
201 if ((argc - 1) < optind) {
202 fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n");
203 usage(EXIT_FAILURE);
204 }
205
206 g_pid = atoi(argv[optind]);
207 if (g_pid <= 0) {
208 fprintf(stderr, "Invalid process id %s\n", argv[optind]);
209 usage(EXIT_FAILURE);
210 }
211
212 if (!::android::meminfo::PageAcct::KernelHasPageIdle()) {
213 fprintf(stderr, "Missing support for Idle page tracking in the kernel\n");
214 return 0;
215 }
216
217 return workingset();
218 }
219