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", "", "", "---- IO (KiB) ----", "--- faults ---",
228                "----------- delayed on ----------");
229       } else {
230         printf("%6s %-16s %20s %14s %34s\n", "", "", "--- IO (KiB/s) ---", "--- faults ---",
231                "----------- delayed on ----------");
232       }
233       printf("%6s %-16s %6s %6s %6s %6s %6s  %-5s  %-5s  %-5s  %-5s  %-5s\n", "PID", "Command",
234              "read", "write", "total", "major", "minor", "IO", "swap", "sched", "mem", "total");
235       int n = limit;
236       const int delay_div = accumulated ? 1 : delay;
237       uint64_t total_read = 0;
238       uint64_t total_write = 0;
239       uint64_t total_read_write = 0;
240       uint64_t total_minflt = 0;
241       uint64_t total_majflt = 0;
242       for (const TaskStatistics& statistics : stats) {
243         total_read += statistics.read();
244         total_write += statistics.write();
245         total_read_write += statistics.read_write();
246         total_minflt += statistics.minflt();
247         total_majflt += statistics.majflt();
248 
249         if (n == 0) {
250           continue;
251         } else if (n > 0) {
252           n--;
253         }
254 
255         printf("%6d %-16s %6" PRIu64 " %6" PRIu64 " %6" PRIu64 " %6" PRIu64 " %6" PRIu64
256                " %5.2f%% %5.2f%% %5.2f%% %5.2f%% %5.2f%%\n",
257                statistics.pid(), statistics.comm().c_str(),
258                BytesToKB(statistics.read()) / delay_div, BytesToKB(statistics.write()) / delay_div,
259                BytesToKB(statistics.read_write()) / delay_div, statistics.majflt(),
260                statistics.minflt(), TimeToTgidPercent(statistics.delay_io(), delay, statistics),
261                TimeToTgidPercent(statistics.delay_swap(), delay, statistics),
262                TimeToTgidPercent(statistics.delay_sched(), delay, statistics),
263                TimeToTgidPercent(statistics.delay_mem(), delay, statistics),
264                TimeToTgidPercent(statistics.delay_total(), delay, statistics));
265       }
266       printf("%6s %-16s %6" PRIu64 " %6" PRIu64 " %6" PRIu64 " %6" PRIu64 " %6" PRIu64 "\n", "",
267              "TOTAL", BytesToKB(total_read) / delay_div, BytesToKB(total_write) / delay_div,
268              BytesToKB(total_read_write) / delay_div, total_majflt / delay_div,
269              total_minflt / delay_div);
270 
271       second = false;
272 
273       if (cycles > 0 && --cycles == 0) break;
274     }
275     first = false;
276     sleep(delay);
277   }
278 
279   return 0;
280 }
281