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 taskstats_socket.Open();
166
167 std::unordered_map<pid_t, TaskStatistics> pid_stats;
168 std::unordered_map<pid_t, TaskStatistics> tgid_stats;
169 std::vector<TaskStatistics> stats;
170
171 bool first = true;
172 bool second = true;
173
174 while (true) {
175 stats.clear();
176 if (!TaskList::Scan(tgid_map)) {
177 LOG(FATAL) << "failed to scan tasks";
178 }
179 for (auto& tgid_it : tgid_map) {
180 pid_t tgid = tgid_it.first;
181 std::vector<pid_t>& pid_list = tgid_it.second;
182
183 TaskStatistics tgid_stats_new;
184 TaskStatistics tgid_stats_delta;
185
186 if (processes) {
187 // If printing processes, collect stats for the tgid which will
188 // hold delay accounting data across all threads, including
189 // ones that have exited.
190 if (!taskstats_socket.GetTgidStats(tgid, tgid_stats_new)) {
191 continue;
192 }
193 tgid_stats_delta = tgid_stats[tgid].Update(tgid_stats_new);
194 }
195
196 // Collect per-thread stats
197 for (pid_t pid : pid_list) {
198 TaskStatistics pid_stats_new;
199 if (!taskstats_socket.GetPidStats(pid, pid_stats_new)) {
200 continue;
201 }
202
203 TaskStatistics pid_stats_delta = pid_stats[pid].Update(pid_stats_new);
204
205 if (processes) {
206 tgid_stats_delta.AddPidToTgid(pid_stats_delta);
207 } else {
208 stats.push_back(pid_stats_delta);
209 }
210 }
211
212 if (processes) {
213 stats.push_back(tgid_stats_delta);
214 }
215 }
216
217 if (!first) {
218 sorter(stats);
219 if (!second) {
220 printf("\n");
221 }
222 if (accumulated) {
223 printf("%6s %-16s %20s %34s\n", "", "",
224 "---- IO (KiB) ----", "----------- delayed on ----------");
225 } else {
226 printf("%6s %-16s %20s %34s\n", "", "",
227 "--- IO (KiB/s) ---", "----------- delayed on ----------");
228 }
229 printf("%6s %-16s %6s %6s %6s %-5s %-5s %-5s %-5s %-5s\n",
230 "PID",
231 "Command",
232 "read",
233 "write",
234 "total",
235 "IO",
236 "swap",
237 "sched",
238 "mem",
239 "total");
240 int n = limit;
241 const int delay_div = accumulated ? 1 : delay;
242 uint64_t total_read = 0;
243 uint64_t total_write = 0;
244 uint64_t total_read_write = 0;
245 for (const TaskStatistics& statistics : stats) {
246 total_read += statistics.read();
247 total_write += statistics.write();
248 total_read_write += statistics.read_write();
249
250 if (n == 0) {
251 continue;
252 } else if (n > 0) {
253 n--;
254 }
255
256 printf("%6d %-16s %6" PRIu64 " %6" PRIu64 " %6" PRIu64 " %5.2f%% %5.2f%% %5.2f%% %5.2f%% %5.2f%%\n",
257 statistics.pid(),
258 statistics.comm().c_str(),
259 BytesToKB(statistics.read()) / delay_div,
260 BytesToKB(statistics.write()) / delay_div,
261 BytesToKB(statistics.read_write()) / delay_div,
262 TimeToTgidPercent(statistics.delay_io(), delay, statistics),
263 TimeToTgidPercent(statistics.delay_swap(), delay, statistics),
264 TimeToTgidPercent(statistics.delay_sched(), delay, statistics),
265 TimeToTgidPercent(statistics.delay_mem(), delay, statistics),
266 TimeToTgidPercent(statistics.delay_total(), delay, statistics));
267 }
268 printf("%6s %-16s %6" PRIu64 " %6" PRIu64 " %6" PRIu64 "\n", "", "TOTAL",
269 BytesToKB(total_read) / delay_div,
270 BytesToKB(total_write) / delay_div,
271 BytesToKB(total_read_write) / delay_div);
272
273 second = false;
274
275 if (cycles > 0 && --cycles == 0) break;
276 }
277 first = false;
278 sleep(delay);
279 }
280
281 return 0;
282 }
283