OSDN Git Service

am 21054c64: am a0370d7b: Merge "Implement iotop"
[android-x86/system-extras.git] / iotop / iotop.cpp
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 <base/logging.h>
26
27 #include "tasklist.h"
28 #include "taskstats.h"
29
30 constexpr uint64_t NSEC_PER_SEC = 1000000000;
31
32 static uint64_t BytesToKB(uint64_t bytes) {
33   return (bytes + 1024-1) / 1024;
34 }
35
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
41 static void usage(char* myname) {
42   printf(
43       "Usage: %s [-h] [-P] [-d <delay>] [-n <cycles>] [-s <column>]\n"
44       "   -d  Set the delay between refreshes in seconds.\n"
45       "   -h  Display this help screen.\n"
46       "   -m  Set the number of processes or threads to show\n"
47       "   -n  Set the number of refreshes before exiting.\n"
48       "   -P  Show processes instead of the default threads.\n"
49       "   -s  Set the column to sort by:\n"
50       "       pid, read, write, total, io, swap, sched, mem or delay.\n",
51       myname);
52 }
53
54 using Sorter = std::function<void(std::vector<TaskStatistics>&)>;
55 static Sorter GetSorter(const std::string field) {
56   // Generic comparator
57   static auto comparator = [](auto& lhs, auto& rhs, auto field, bool ascending) -> bool {
58     auto a = (lhs.*field)();
59     auto b = (rhs.*field)();
60     if (a != b) {
61       // Sort by selected field
62       return ascending ^ (a < b);
63     } else {
64       // And then fall back to sorting by pid
65       return lhs.pid() < rhs.pid();
66     }
67   };
68
69   auto make_sorter = [](auto field, bool ascending) {
70     // Make closure for comparator on a specific field
71     using namespace std::placeholders;
72     auto bound_comparator = std::bind(comparator, _1, _2, field, ascending);
73
74     // Return closure to std::sort with specialized comparator
75     return [bound_comparator](auto& vector) {
76       return std::sort(vector.begin(), vector.end(), bound_comparator);
77     };
78   };
79
80   static const std::map<std::string, Sorter> sorters{
81       {"pid", make_sorter(&TaskStatistics::pid, false)},
82       {"read", make_sorter(&TaskStatistics::read, true)},
83       {"write", make_sorter(&TaskStatistics::write, true)},
84       {"total", make_sorter(&TaskStatistics::read_write, true)},
85       {"io", make_sorter(&TaskStatistics::delay_io, true)},
86       {"swap", make_sorter(&TaskStatistics::delay_swap, true)},
87       {"sched", make_sorter(&TaskStatistics::delay_sched, true)},
88       {"mem", make_sorter(&TaskStatistics::delay_mem, true)},
89       {"delay", make_sorter(&TaskStatistics::delay_total, true)},
90   };
91
92   auto it = sorters.find(field);
93   if (it == sorters.end()) {
94     return nullptr;
95   }
96   return it->second;
97 }
98
99 int main(int argc, char* argv[]) {
100   bool processes = false;
101   int delay = 1;
102   int cycles = -1;
103   int limit = -1;
104   Sorter sorter = GetSorter("total");
105
106   android::base::InitLogging(argv, android::base::StderrLogger);
107
108   while (1) {
109     int c;
110     static const option longopts[] = {
111         {"delay", required_argument, 0, 'd'},
112         {"help", 0, 0, 'h'},
113         {"limit", required_argument, 0, 'm'},
114         {"iter", required_argument, 0, 'n'},
115         {"sort", required_argument, 0, 's'},
116         {"processes", 0, 0, 'P'},
117         {0, 0, 0, 0},
118     };
119     c = getopt_long(argc, argv, "d:hm:n:Ps:", longopts, NULL);
120     if (c < 0) {
121       break;
122     }
123     switch (c) {
124     case 'd':
125       delay = atoi(optarg);
126       break;
127     case 'h':
128       usage(argv[0]);
129       return(EXIT_SUCCESS);
130     case 'm':
131       limit = atoi(optarg);
132       break;
133     case 'n':
134       cycles = atoi(optarg);
135       break;
136     case 's': {
137       sorter = GetSorter(optarg);
138       if (sorter == nullptr) {
139         LOG(ERROR) << "Invalid sort column \"" << optarg << "\"";
140         usage(argv[0]);
141         return(EXIT_FAILURE);
142       }
143       break;
144     }
145     case 'P':
146       processes = true;
147       break;
148     case '?':
149       usage(argv[0]);
150       return(EXIT_FAILURE);
151     default:
152       abort();
153     }
154   }
155
156   std::map<pid_t, std::vector<pid_t>> tgid_map;
157
158   TaskstatsSocket taskstats_socket;
159   taskstats_socket.Open();
160
161   std::unordered_map<pid_t, TaskStatistics> pid_stats;
162   std::unordered_map<pid_t, TaskStatistics> tgid_stats;
163   std::vector<TaskStatistics> stats;
164
165   bool first = true;
166   bool second = true;
167
168   while (true) {
169     stats.clear();
170     if (!TaskList::Scan(tgid_map)) {
171       LOG(FATAL) << "failed to scan tasks";
172     }
173     for (auto& tgid_it : tgid_map) {
174       pid_t tgid = tgid_it.first;
175       std::vector<pid_t>& pid_list = tgid_it.second;
176
177       TaskStatistics tgid_stats_new;
178       TaskStatistics tgid_stats_delta;
179
180       if (processes) {
181         // If printing processes, collect stats for the tgid which will
182         // hold delay accounting data across all threads, including
183         // ones that have exited.
184         if (!taskstats_socket.GetTgidStats(tgid, tgid_stats_new)) {
185           continue;
186         }
187         tgid_stats_delta = tgid_stats[tgid].Update(tgid_stats_new);
188       }
189
190       // Collect per-thread stats
191       for (pid_t pid : pid_list) {
192         TaskStatistics pid_stats_new;
193         if (!taskstats_socket.GetPidStats(pid, pid_stats_new)) {
194           continue;
195         }
196
197         TaskStatistics pid_stats_delta = pid_stats[pid].Update(pid_stats_new);
198
199         if (processes) {
200           tgid_stats_delta.AddPidToTgid(pid_stats_delta);
201         } else {
202           stats.push_back(pid_stats_delta);
203         }
204       }
205
206       if (processes) {
207         stats.push_back(tgid_stats_delta);
208       }
209     }
210
211     if (!first) {
212       sorter(stats);
213       if (!second) {
214         printf("\n");
215       }
216       printf("%6s %-16s %20s %34s\n", "", "",
217           "--- IO (KiB/s) ---", "----------- delayed on ----------");
218       printf("%6s %-16s %6s %6s %6s  %-5s  %-5s  %-5s  %-5s  %-5s\n",
219           "PID",
220           "Command",
221           "read",
222           "write",
223           "total",
224           "IO",
225           "swap",
226           "sched",
227           "mem",
228           "total");
229       int n = limit;
230       for (const TaskStatistics& statistics : stats) {
231         printf("%6d %-16s %6" PRIu64 " %6" PRIu64 " %6" PRIu64 " %5.2f%% %5.2f%% %5.2f%% %5.2f%% %5.2f%%\n",
232             statistics.pid(),
233             statistics.comm().c_str(),
234             BytesToKB(statistics.read()),
235             BytesToKB(statistics.write()),
236             BytesToKB(statistics.read_write()),
237             TimeToTgidPercent(statistics.delay_io(), delay, statistics),
238             TimeToTgidPercent(statistics.delay_swap(), delay, statistics),
239             TimeToTgidPercent(statistics.delay_sched(), delay, statistics),
240             TimeToTgidPercent(statistics.delay_mem(), delay, statistics),
241             TimeToTgidPercent(statistics.delay_total(), delay, statistics));
242         if (n > 0 && --n == 0) break;
243       }
244       second = false;
245
246       if (cycles > 0 && --cycles == 0) break;
247     }
248     first = false;
249     sleep(delay);
250   }
251
252   return 0;
253 }