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