• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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