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 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