1 // Copyright (C) 2015 The Android Open Source Project
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include <unordered_map>
25 #include <android-base/logging.h>
28 #include "taskstats.h"
30 constexpr uint64_t NSEC_PER_SEC = 1000000000;
32 static uint64_t BytesToKB(uint64_t bytes) {
33 return (bytes + 1024-1) / 1024;
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);
41 static void usage(char* myname) {
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",
55 using Sorter = std::function<void(std::vector<TaskStatistics>&)>;
56 static Sorter GetSorter(const std::string& field) {
58 static auto comparator = [](auto& lhs, auto& rhs, auto field, bool ascending) -> bool {
59 auto a = (lhs.*field)();
60 auto b = (rhs.*field)();
62 // Sort by selected field
63 return ascending ^ (a < b);
65 // And then fall back to sorting by pid
66 return lhs.pid() < rhs.pid();
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);
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);
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)},
93 auto it = sorters.find(field);
94 if (it == sorters.end()) {
100 int main(int argc, char* argv[]) {
101 bool accumulated = false;
102 bool processes = false;
106 Sorter sorter = GetSorter("total");
108 android::base::InitLogging(argv, android::base::StderrLogger);
112 static const option longopts[] = {
113 {"accumulated", 0, 0, 'a'},
114 {"delay", required_argument, 0, 'd'},
116 {"limit", required_argument, 0, 'm'},
117 {"iter", required_argument, 0, 'n'},
118 {"sort", required_argument, 0, 's'},
119 {"processes", 0, 0, 'P'},
122 c = getopt_long(argc, argv, "ad:hm:n:Ps:", longopts, NULL);
131 delay = atoi(optarg);
135 return(EXIT_SUCCESS);
137 limit = atoi(optarg);
140 cycles = atoi(optarg);
143 sorter = GetSorter(optarg);
144 if (sorter == nullptr) {
145 LOG(ERROR) << "Invalid sort column \"" << optarg << "\"";
147 return(EXIT_FAILURE);
156 return(EXIT_FAILURE);
162 std::map<pid_t, std::vector<pid_t>> tgid_map;
164 TaskstatsSocket taskstats_socket;
165 taskstats_socket.Open();
167 std::unordered_map<pid_t, TaskStatistics> pid_stats;
168 std::unordered_map<pid_t, TaskStatistics> tgid_stats;
169 std::vector<TaskStatistics> stats;
176 if (!TaskList::Scan(tgid_map)) {
177 LOG(FATAL) << "failed to scan tasks";
179 for (auto& tgid_it : tgid_map) {
180 pid_t tgid = tgid_it.first;
181 std::vector<pid_t>& pid_list = tgid_it.second;
183 TaskStatistics tgid_stats_new;
184 TaskStatistics tgid_stats_delta;
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)) {
193 tgid_stats_delta = tgid_stats[tgid].Update(tgid_stats_new);
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)) {
203 TaskStatistics pid_stats_delta = pid_stats[pid].Update(pid_stats_new);
206 tgid_stats_delta.AddPidToTgid(pid_stats_delta);
208 stats.push_back(pid_stats_delta);
213 stats.push_back(tgid_stats_delta);
223 printf("%6s %-16s %20s %34s\n", "", "",
224 "---- IO (KiB) ----", "----------- delayed on ----------");
226 printf("%6s %-16s %20s %34s\n", "", "",
227 "--- IO (KiB/s) ---", "----------- delayed on ----------");
229 printf("%6s %-16s %6s %6s %6s %-5s %-5s %-5s %-5s %-5s\n",
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();
256 printf("%6d %-16s %6" PRIu64 " %6" PRIu64 " %6" PRIu64 " %5.2f%% %5.2f%% %5.2f%% %5.2f%% %5.2f%%\n",
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));
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);
275 if (cycles > 0 && --cycles == 0) break;