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 30constexpr uint64_t NSEC_PER_SEC = 1000000000; 31 32static uint64_t BytesToKB(uint64_t bytes) { 33 return (bytes + 1024-1) / 1024; 34} 35 36static 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 41static 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 55using Sorter = std::function<void(std::vector<TaskStatistics>&)>; 56static 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 100int 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 if (!taskstats_socket.Open()) { 166 return EXIT_FAILURE; 167 } 168 169 std::unordered_map<pid_t, TaskStatistics> pid_stats; 170 std::unordered_map<pid_t, TaskStatistics> tgid_stats; 171 std::vector<TaskStatistics> stats; 172 173 bool first = true; 174 bool second = true; 175 176 while (true) { 177 stats.clear(); 178 if (!TaskList::Scan(tgid_map)) { 179 LOG(ERROR) << "failed to scan tasks"; 180 return EXIT_FAILURE; 181 } 182 for (auto& tgid_it : tgid_map) { 183 pid_t tgid = tgid_it.first; 184 std::vector<pid_t>& pid_list = tgid_it.second; 185 186 TaskStatistics tgid_stats_new; 187 TaskStatistics tgid_stats_delta; 188 189 if (processes) { 190 // If printing processes, collect stats for the tgid which will 191 // hold delay accounting data across all threads, including 192 // ones that have exited. 193 if (!taskstats_socket.GetTgidStats(tgid, tgid_stats_new)) { 194 continue; 195 } 196 tgid_stats_delta = tgid_stats[tgid].Update(tgid_stats_new); 197 } 198 199 // Collect per-thread stats 200 for (pid_t pid : pid_list) { 201 TaskStatistics pid_stats_new; 202 if (!taskstats_socket.GetPidStats(pid, pid_stats_new)) { 203 continue; 204 } 205 206 TaskStatistics pid_stats_delta = pid_stats[pid].Update(pid_stats_new); 207 208 if (processes) { 209 tgid_stats_delta.AddPidToTgid(pid_stats_delta); 210 } else { 211 stats.push_back(pid_stats_delta); 212 } 213 } 214 215 if (processes) { 216 stats.push_back(tgid_stats_delta); 217 } 218 } 219 220 if (!first) { 221 sorter(stats); 222 if (!second) { 223 printf("\n"); 224 } 225 if (accumulated) { 226 printf("%6s %-16s %20s %34s\n", "", "", 227 "---- IO (KiB) ----", "----------- delayed on ----------"); 228 } else { 229 printf("%6s %-16s %20s %34s\n", "", "", 230 "--- IO (KiB/s) ---", "----------- delayed on ----------"); 231 } 232 printf("%6s %-16s %6s %6s %6s %-5s %-5s %-5s %-5s %-5s\n", 233 "PID", 234 "Command", 235 "read", 236 "write", 237 "total", 238 "IO", 239 "swap", 240 "sched", 241 "mem", 242 "total"); 243 int n = limit; 244 const int delay_div = accumulated ? 1 : delay; 245 uint64_t total_read = 0; 246 uint64_t total_write = 0; 247 uint64_t total_read_write = 0; 248 for (const TaskStatistics& statistics : stats) { 249 total_read += statistics.read(); 250 total_write += statistics.write(); 251 total_read_write += statistics.read_write(); 252 253 if (n == 0) { 254 continue; 255 } else if (n > 0) { 256 n--; 257 } 258 259 printf("%6d %-16s %6" PRIu64 " %6" PRIu64 " %6" PRIu64 " %5.2f%% %5.2f%% %5.2f%% %5.2f%% %5.2f%%\n", 260 statistics.pid(), 261 statistics.comm().c_str(), 262 BytesToKB(statistics.read()) / delay_div, 263 BytesToKB(statistics.write()) / delay_div, 264 BytesToKB(statistics.read_write()) / delay_div, 265 TimeToTgidPercent(statistics.delay_io(), delay, statistics), 266 TimeToTgidPercent(statistics.delay_swap(), delay, statistics), 267 TimeToTgidPercent(statistics.delay_sched(), delay, statistics), 268 TimeToTgidPercent(statistics.delay_mem(), delay, statistics), 269 TimeToTgidPercent(statistics.delay_total(), delay, statistics)); 270 } 271 printf("%6s %-16s %6" PRIu64 " %6" PRIu64 " %6" PRIu64 "\n", "", "TOTAL", 272 BytesToKB(total_read) / delay_div, 273 BytesToKB(total_write) / delay_div, 274 BytesToKB(total_read_write) / delay_div); 275 276 second = false; 277 278 if (cycles > 0 && --cycles == 0) break; 279 } 280 first = false; 281 sleep(delay); 282 } 283 284 return 0; 285} 286