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 <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 " -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",
54 using Sorter = std::function<void(std::vector<TaskStatistics>&)>;
55 static Sorter GetSorter(const std::string field) {
57 static auto comparator = [](auto& lhs, auto& rhs, auto field, bool ascending) -> bool {
58 auto a = (lhs.*field)();
59 auto b = (rhs.*field)();
61 // Sort by selected field
62 return ascending ^ (a < b);
64 // And then fall back to sorting by pid
65 return lhs.pid() < rhs.pid();
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);
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);
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)},
92 auto it = sorters.find(field);
93 if (it == sorters.end()) {
99 int main(int argc, char* argv[]) {
100 bool processes = false;
104 Sorter sorter = GetSorter("total");
106 android::base::InitLogging(argv, android::base::StderrLogger);
110 static const option longopts[] = {
111 {"delay", required_argument, 0, 'd'},
113 {"limit", required_argument, 0, 'm'},
114 {"iter", required_argument, 0, 'n'},
115 {"sort", required_argument, 0, 's'},
116 {"processes", 0, 0, 'P'},
119 c = getopt_long(argc, argv, "d:hm:n:Ps:", longopts, NULL);
125 delay = atoi(optarg);
129 return(EXIT_SUCCESS);
131 limit = atoi(optarg);
134 cycles = atoi(optarg);
137 sorter = GetSorter(optarg);
138 if (sorter == nullptr) {
139 LOG(ERROR) << "Invalid sort column \"" << optarg << "\"";
141 return(EXIT_FAILURE);
150 return(EXIT_FAILURE);
156 std::map<pid_t, std::vector<pid_t>> tgid_map;
158 TaskstatsSocket taskstats_socket;
159 taskstats_socket.Open();
161 std::unordered_map<pid_t, TaskStatistics> pid_stats;
162 std::unordered_map<pid_t, TaskStatistics> tgid_stats;
163 std::vector<TaskStatistics> stats;
170 if (!TaskList::Scan(tgid_map)) {
171 LOG(FATAL) << "failed to scan tasks";
173 for (auto& tgid_it : tgid_map) {
174 pid_t tgid = tgid_it.first;
175 std::vector<pid_t>& pid_list = tgid_it.second;
177 TaskStatistics tgid_stats_new;
178 TaskStatistics tgid_stats_delta;
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)) {
187 tgid_stats_delta = tgid_stats[tgid].Update(tgid_stats_new);
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)) {
197 TaskStatistics pid_stats_delta = pid_stats[pid].Update(pid_stats_new);
200 tgid_stats_delta.AddPidToTgid(pid_stats_delta);
202 stats.push_back(pid_stats_delta);
207 stats.push_back(tgid_stats_delta);
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",
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",
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;
246 if (cycles > 0 && --cycles == 0) break;