1 // Copyright (C) 2015 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <getopt.h>
16 #include <inttypes.h>
17 #include <stdint.h>
18 #include <stdlib.h>
19
20 #include <algorithm>
21 #include <map>
22 #include <unordered_map>
23 #include <vector>
24
25 #include <android-base/logging.h>
26
27 #include "tasklist.h"
28 #include "taskstats.h"
29
30 constexpr uint64_t NSEC_PER_SEC = 1000000000;
31
BytesToKB(uint64_t bytes)32 static uint64_t BytesToKB(uint64_t bytes) {
33 return (bytes + 1024-1) / 1024;
34 }
35
TimeToTgidPercent(uint64_t ns,int time,const TaskStatistics & stats)36 static float TimeToTgidPercent(uint64_t ns, int time, const TaskStatistics& stats) {
37 float percent = ns / stats.threads() / (time * NSEC_PER_SEC / 100.0f);
38 return std::min(percent, 99.99f);
39 }
40
usage(char * myname)41 static void usage(char* myname) {
42 printf(
43 "Usage: %s [-h] [-P] [-d <delay>] [-n <cycles>] [-s <column>]\n"
44 " -a Show byte count instead of rate\n"
45 " -d Set the delay between refreshes in seconds.\n"
46 " -h Display this help screen.\n"
47 " -m Set the number of processes or threads to show\n"
48 " -n Set the number of refreshes before exiting.\n"
49 " -P Show processes instead of the default threads.\n"
50 " -s Set the column to sort by:\n"
51 " pid, read, write, total, io, swap, faults, sched, mem or delay.\n",
52 myname);
53 }
54
55 using Sorter = std::function<void(std::vector<TaskStatistics>&)>;
GetSorter(const std::string & field)56 static Sorter GetSorter(const std::string& field) {
57 // Generic comparator
58 static auto comparator = [](auto& lhs, auto& rhs, auto field, bool ascending) -> bool {
59 auto a = (lhs.*field)();
60 auto b = (rhs.*field)();
61 if (a != b) {
62 // Sort by selected field
63 return ascending ^ (a < b);
64 } else {
65 // And then fall back to sorting by pid
66 return lhs.pid() < rhs.pid();
67 }
68 };
69
70 auto make_sorter = [](auto field, bool ascending) {
71 // Make closure for comparator on a specific field
72 using namespace std::placeholders;
73 auto bound_comparator = std::bind(comparator, _1, _2, field, ascending);
74
75 // Return closure to std::sort with specialized comparator
76 return [bound_comparator](auto& vector) {
77 return std::sort(vector.begin(), vector.end(), bound_comparator);
78 };
79 };
80
81 static const std::map<std::string, Sorter> sorters{
82 {"pid", make_sorter(&TaskStatistics::pid, false)},
83 {"read", make_sorter(&TaskStatistics::read, true)},
84 {"write", make_sorter(&TaskStatistics::write, true)},
85 {"total", make_sorter(&TaskStatistics::read_write, true)},
86 {"io", make_sorter(&TaskStatistics::delay_io, true)},
87 {"swap", make_sorter(&TaskStatistics::delay_swap, true)},
88 {"faults", make_sorter(&TaskStatistics::faults, true)},
89 {"sched", make_sorter(&TaskStatistics::delay_sched, true)},
90 {"mem", make_sorter(&TaskStatistics::delay_mem, true)},
91 {"delay", make_sorter(&TaskStatistics::delay_total, true)},
92 };
93
94 auto it = sorters.find(field);
95 if (it == sorters.end()) {
96 return nullptr;
97 }
98 return it->second;
99 }
100
main(int argc,char * argv[])101 int main(int argc, char* argv[]) {
102 bool accumulated = false;
103 bool processes = false;
104 int delay = 1;
105 int cycles = -1;
106 int limit = -1;
107 Sorter sorter = GetSorter("total");
108
109 android::base::InitLogging(argv, android::base::StderrLogger);
110
111 while (1) {
112 int c;
113 static const option longopts[] = {
114 {"accumulated", 0, 0, 'a'},
115 {"delay", required_argument, 0, 'd'},
116 {"help", 0, 0, 'h'},
117 {"limit", required_argument, 0, 'm'},
118 {"iter", required_argument, 0, 'n'},
119 {"sort", required_argument, 0, 's'},
120 {"processes", 0, 0, 'P'},
121 {0, 0, 0, 0},
122 };
123 c = getopt_long(argc, argv, "ad:hm:n:Ps:", longopts, NULL);
124 if (c < 0) {
125 break;
126 }
127 switch (c) {
128 case 'a':
129 accumulated = true;
130 break;
131 case 'd':
132 delay = atoi(optarg);
133 break;
134 case 'h':
135 usage(argv[0]);
136 return(EXIT_SUCCESS);
137 case 'm':
138 limit = atoi(optarg);
139 break;
140 case 'n':
141 cycles = atoi(optarg);
142 break;
143 case 's': {
144 sorter = GetSorter(optarg);
145 if (sorter == nullptr) {
146 LOG(ERROR) << "Invalid sort column \"" << optarg << "\"";
147 usage(argv[0]);
148 return EXIT_FAILURE;
149 }
150 break;
151 }
152 case 'P':
153 processes = true;
154 break;
155 case '?':
156 usage(argv[0]);
157 return EXIT_FAILURE;
158 default:
159 abort();
160 }
161 }
162
163 std::map<pid_t, std::vector<pid_t>> tgid_map;
164
165 TaskstatsSocket taskstats_socket;
166 if (!taskstats_socket.Open()) {
167 return EXIT_FAILURE;
168 }
169
170 std::unordered_map<pid_t, TaskStatistics> pid_stats;
171 std::unordered_map<pid_t, TaskStatistics> tgid_stats;
172 std::vector<TaskStatistics> stats;
173
174 bool first = true;
175 bool second = true;
176
177 while (true) {
178 stats.clear();
179 if (!TaskList::Scan(tgid_map)) {
180 LOG(ERROR) << "failed to scan tasks";
181 return EXIT_FAILURE;
182 }
183 for (auto& tgid_it : tgid_map) {
184 pid_t tgid = tgid_it.first;
185 std::vector<pid_t>& pid_list = tgid_it.second;
186
187 TaskStatistics tgid_stats_new;
188 TaskStatistics tgid_stats_delta;
189
190 if (processes) {
191 // If printing processes, collect stats for the tgid which will
192 // hold delay accounting data across all threads, including
193 // ones that have exited.
194 if (!taskstats_socket.GetTgidStats(tgid, tgid_stats_new)) {
195 continue;
196 }
197 tgid_stats_delta = tgid_stats[tgid].Update(tgid_stats_new);
198 }
199
200 // Collect per-thread stats
201 for (pid_t pid : pid_list) {
202 TaskStatistics pid_stats_new;
203 if (!taskstats_socket.GetPidStats(pid, pid_stats_new)) {
204 continue;
205 }
206
207 TaskStatistics pid_stats_delta = pid_stats[pid].Update(pid_stats_new);
208
209 if (processes) {
210 tgid_stats_delta.AddPidToTgid(pid_stats_delta);
211 } else {
212 stats.push_back(pid_stats_delta);
213 }
214 }
215
216 if (processes) {
217 stats.push_back(tgid_stats_delta);
218 }
219 }
220
221 if (!first) {
222 sorter(stats);
223 if (!second) {
224 printf("\n");
225 }
226 if (accumulated) {
227 printf("%6s %-16s %20s %14s %34s\n", "", "",
228 "---- IO (KiB) ----", "--- faults ---", "----------- delayed on ----------");
229 } else {
230 printf("%6s %-16s %20s %14s %34s\n", "", "",
231 "--- IO (KiB/s) ---", "--- faults ---", "----------- delayed on ----------");
232 }
233 printf("%6s %-16s %6s %6s %6s %6s %6s %-5s %-5s %-5s %-5s %-5s\n",
234 "PID",
235 "Command",
236 "read",
237 "write",
238 "total",
239 "major",
240 "minor",
241 "IO",
242 "swap",
243 "sched",
244 "mem",
245 "total");
246 int n = limit;
247 const int delay_div = accumulated ? 1 : delay;
248 uint64_t total_read = 0;
249 uint64_t total_write = 0;
250 uint64_t total_read_write = 0;
251 uint64_t total_minflt = 0;
252 uint64_t total_majflt = 0;
253 for (const TaskStatistics& statistics : stats) {
254 total_read += statistics.read();
255 total_write += statistics.write();
256 total_read_write += statistics.read_write();
257 total_minflt += statistics.minflt();
258 total_majflt += statistics.majflt();
259
260 if (n == 0) {
261 continue;
262 } else if (n > 0) {
263 n--;
264 }
265
266 printf("%6d %-16s %6" PRIu64 " %6" PRIu64 " %6" PRIu64 " %6" PRIu64 " %6" PRIu64" %5.2f%% %5.2f%% %5.2f%% %5.2f%% %5.2f%%\n",
267 statistics.pid(),
268 statistics.comm().c_str(),
269 BytesToKB(statistics.read()) / delay_div,
270 BytesToKB(statistics.write()) / delay_div,
271 BytesToKB(statistics.read_write()) / delay_div,
272 statistics.majflt(), statistics.minflt(),
273 TimeToTgidPercent(statistics.delay_io(), delay, statistics),
274 TimeToTgidPercent(statistics.delay_swap(), delay, statistics),
275 TimeToTgidPercent(statistics.delay_sched(), delay, statistics),
276 TimeToTgidPercent(statistics.delay_mem(), delay, statistics),
277 TimeToTgidPercent(statistics.delay_total(), delay, statistics));
278 }
279 printf("%6s %-16s %6" PRIu64 " %6" PRIu64 " %6" PRIu64 " %6" PRIu64 " %6" PRIu64"\n", "", "TOTAL",
280 BytesToKB(total_read) / delay_div,
281 BytesToKB(total_write) / delay_div,
282 BytesToKB(total_read_write) / delay_div,
283 total_majflt / delay_div, total_minflt / delay_div);
284
285 second = false;
286
287 if (cycles > 0 && --cycles == 0) break;
288 }
289 first = false;
290 sleep(delay);
291 }
292
293 return 0;
294 }
295