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