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, 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 {"sched", make_sorter(&TaskStatistics::delay_sched, true)},
89 {"mem", make_sorter(&TaskStatistics::delay_mem, true)},
90 {"delay", make_sorter(&TaskStatistics::delay_total, true)},
91 };
92
93 auto it = sorters.find(field);
94 if (it == sorters.end()) {
95 return nullptr;
96 }
97 return it->second;
98 }
99
main(int argc,char * argv[])100 int main(int argc, char* argv[]) {
101 bool accumulated = false;
102 bool processes = false;
103 int delay = 1;
104 int cycles = -1;
105 int limit = -1;
106 Sorter sorter = GetSorter("total");
107
108 android::base::InitLogging(argv, android::base::StderrLogger);
109
110 while (1) {
111 int c;
112 static const option longopts[] = {
113 {"accumulated", 0, 0, 'a'},
114 {"delay", required_argument, 0, 'd'},
115 {"help", 0, 0, 'h'},
116 {"limit", required_argument, 0, 'm'},
117 {"iter", required_argument, 0, 'n'},
118 {"sort", required_argument, 0, 's'},
119 {"processes", 0, 0, 'P'},
120 {0, 0, 0, 0},
121 };
122 c = getopt_long(argc, argv, "ad:hm:n:Ps:", longopts, NULL);
123 if (c < 0) {
124 break;
125 }
126 switch (c) {
127 case 'a':
128 accumulated = true;
129 break;
130 case 'd':
131 delay = atoi(optarg);
132 break;
133 case 'h':
134 usage(argv[0]);
135 return(EXIT_SUCCESS);
136 case 'm':
137 limit = atoi(optarg);
138 break;
139 case 'n':
140 cycles = atoi(optarg);
141 break;
142 case 's': {
143 sorter = GetSorter(optarg);
144 if (sorter == nullptr) {
145 LOG(ERROR) << "Invalid sort column \"" << optarg << "\"";
146 usage(argv[0]);
147 return EXIT_FAILURE;
148 }
149 break;
150 }
151 case 'P':
152 processes = true;
153 break;
154 case '?':
155 usage(argv[0]);
156 return EXIT_FAILURE;
157 default:
158 abort();
159 }
160 }
161
162 std::map<pid_t, std::vector<pid_t>> tgid_map;
163
164 TaskstatsSocket taskstats_socket;
165 if (!taskstats_socket.Open()) {
166 return EXIT_FAILURE;
167 }
168
169 std::unordered_map<pid_t, TaskStatistics> pid_stats;
170 std::unordered_map<pid_t, TaskStatistics> tgid_stats;
171 std::vector<TaskStatistics> stats;
172
173 bool first = true;
174 bool second = true;
175
176 while (true) {
177 stats.clear();
178 if (!TaskList::Scan(tgid_map)) {
179 LOG(ERROR) << "failed to scan tasks";
180 return EXIT_FAILURE;
181 }
182 for (auto& tgid_it : tgid_map) {
183 pid_t tgid = tgid_it.first;
184 std::vector<pid_t>& pid_list = tgid_it.second;
185
186 TaskStatistics tgid_stats_new;
187 TaskStatistics tgid_stats_delta;
188
189 if (processes) {
190 // If printing processes, collect stats for the tgid which will
191 // hold delay accounting data across all threads, including
192 // ones that have exited.
193 if (!taskstats_socket.GetTgidStats(tgid, tgid_stats_new)) {
194 continue;
195 }
196 tgid_stats_delta = tgid_stats[tgid].Update(tgid_stats_new);
197 }
198
199 // Collect per-thread stats
200 for (pid_t pid : pid_list) {
201 TaskStatistics pid_stats_new;
202 if (!taskstats_socket.GetPidStats(pid, pid_stats_new)) {
203 continue;
204 }
205
206 TaskStatistics pid_stats_delta = pid_stats[pid].Update(pid_stats_new);
207
208 if (processes) {
209 tgid_stats_delta.AddPidToTgid(pid_stats_delta);
210 } else {
211 stats.push_back(pid_stats_delta);
212 }
213 }
214
215 if (processes) {
216 stats.push_back(tgid_stats_delta);
217 }
218 }
219
220 if (!first) {
221 sorter(stats);
222 if (!second) {
223 printf("\n");
224 }
225 if (accumulated) {
226 printf("%6s %-16s %20s %34s\n", "", "",
227 "---- IO (KiB) ----", "----------- delayed on ----------");
228 } else {
229 printf("%6s %-16s %20s %34s\n", "", "",
230 "--- IO (KiB/s) ---", "----------- delayed on ----------");
231 }
232 printf("%6s %-16s %6s %6s %6s %-5s %-5s %-5s %-5s %-5s\n",
233 "PID",
234 "Command",
235 "read",
236 "write",
237 "total",
238 "IO",
239 "swap",
240 "sched",
241 "mem",
242 "total");
243 int n = limit;
244 const int delay_div = accumulated ? 1 : delay;
245 uint64_t total_read = 0;
246 uint64_t total_write = 0;
247 uint64_t total_read_write = 0;
248 for (const TaskStatistics& statistics : stats) {
249 total_read += statistics.read();
250 total_write += statistics.write();
251 total_read_write += statistics.read_write();
252
253 if (n == 0) {
254 continue;
255 } else if (n > 0) {
256 n--;
257 }
258
259 printf("%6d %-16s %6" PRIu64 " %6" PRIu64 " %6" PRIu64 " %5.2f%% %5.2f%% %5.2f%% %5.2f%% %5.2f%%\n",
260 statistics.pid(),
261 statistics.comm().c_str(),
262 BytesToKB(statistics.read()) / delay_div,
263 BytesToKB(statistics.write()) / delay_div,
264 BytesToKB(statistics.read_write()) / delay_div,
265 TimeToTgidPercent(statistics.delay_io(), delay, statistics),
266 TimeToTgidPercent(statistics.delay_swap(), delay, statistics),
267 TimeToTgidPercent(statistics.delay_sched(), delay, statistics),
268 TimeToTgidPercent(statistics.delay_mem(), delay, statistics),
269 TimeToTgidPercent(statistics.delay_total(), delay, statistics));
270 }
271 printf("%6s %-16s %6" PRIu64 " %6" PRIu64 " %6" PRIu64 "\n", "", "TOTAL",
272 BytesToKB(total_read) / delay_div,
273 BytesToKB(total_write) / delay_div,
274 BytesToKB(total_read_write) / delay_div);
275
276 second = false;
277
278 if (cycles > 0 && --cycles == 0) break;
279 }
280 first = false;
281 sleep(delay);
282 }
283
284 return 0;
285 }
286