1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /* 18 * Linux task stats reporting tool. Queries and prints out the kernel's 19 * taskstats structure for a given process or thread group id. See 20 * https://www.kernel.org/doc/Documentation/accounting/ for more information 21 * about the reported fields. 22 */ 23 24 #include <errno.h> 25 #include <getopt.h> 26 #include <netlink/attr.h> 27 #include <netlink/genl/genl.h> 28 #include <netlink/genl/ctrl.h> 29 #include <netlink/handlers.h> 30 #include <netlink/msg.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <sys/cdefs.h> 34 #include <time.h> 35 #include <unistd.h> 36 37 #include <linux/taskstats.h> 38 39 struct TaskStatistics { 40 int pid; 41 int tgid; 42 struct taskstats stats; 43 }; 44 45 int print_receive_error(struct sockaddr_nl* address __unused, 46 struct nlmsgerr* error, void* arg __unused) { 47 fprintf(stderr, "Netlink receive error: %s\n", strerror(-error->error)); 48 return NL_STOP; 49 } 50 51 void parse_aggregate_task_stats(struct nlattr* attr, int attr_size, 52 struct TaskStatistics* stats) { 53 nla_for_each_attr(attr, attr, attr_size, attr_size) { 54 switch (attr->nla_type) { 55 case TASKSTATS_TYPE_PID: 56 stats->pid = nla_get_u32(attr); 57 break; 58 case TASKSTATS_TYPE_TGID: 59 stats->tgid = nla_get_u32(attr); 60 break; 61 case TASKSTATS_TYPE_STATS: 62 nla_memcpy(&stats->stats, attr, sizeof(stats->stats)); 63 break; 64 default: 65 break; 66 } 67 } 68 } 69 70 int parse_task_stats(struct nl_msg* msg, void* arg) { 71 struct TaskStatistics* stats = (struct TaskStatistics*)arg; 72 struct genlmsghdr* gnlh = (struct genlmsghdr*)nlmsg_data(nlmsg_hdr(msg)); 73 struct nlattr* attr = genlmsg_attrdata(gnlh, 0); 74 int remaining = genlmsg_attrlen(gnlh, 0); 75 76 nla_for_each_attr(attr, attr, remaining, remaining) { 77 switch (attr->nla_type) { 78 case TASKSTATS_TYPE_AGGR_PID: 79 case TASKSTATS_TYPE_AGGR_TGID: 80 parse_aggregate_task_stats(nla_data(attr), nla_len(attr), 81 stats); 82 break; 83 default: 84 break; 85 } 86 } 87 return NL_STOP; 88 } 89 90 int query_task_stats(struct nl_sock* netlink_socket, int family_id, 91 int command_type, int parameter, 92 struct TaskStatistics* stats) { 93 memset(stats, 0, sizeof(*stats)); 94 95 struct nl_msg* message = nlmsg_alloc(); 96 genlmsg_put(message, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, 0, 97 TASKSTATS_CMD_GET, TASKSTATS_VERSION); 98 nla_put_u32(message, command_type, parameter); 99 100 int result = nl_send_auto_complete(netlink_socket, message); 101 nlmsg_free(message); 102 if (result < 0) { 103 return result; 104 } 105 106 struct nl_cb* callbacks = nl_cb_get(nl_cb_alloc(NL_CB_CUSTOM)); 107 nl_cb_set(callbacks, NL_CB_VALID, NL_CB_CUSTOM, &parse_task_stats, stats); 108 nl_cb_err(callbacks, NL_CB_CUSTOM, &print_receive_error, &family_id); 109 110 result = nl_recvmsgs(netlink_socket, callbacks); 111 nl_cb_put(callbacks); 112 if (result < 0) { 113 return result; 114 } 115 return stats->pid || stats->tgid; 116 } 117 118 double average_ms(unsigned long long total, unsigned long long count) { 119 if (!count) { 120 return 0; 121 } 122 return ((double)total) / count / 1e6; 123 } 124 125 unsigned long long average_ns(unsigned long long total, 126 unsigned long long count) { 127 if (!count) { 128 return 0; 129 } 130 return total / count; 131 } 132 133 void print_task_stats(const struct TaskStatistics* stats, 134 int human_readable) { 135 const struct taskstats* s = &stats->stats; 136 printf("Basic task statistics\n"); 137 printf("---------------------\n"); 138 printf("%-25s%d\n", "Stats version:", s->version); 139 printf("%-25s%d\n", "Exit code:", s->ac_exitcode); 140 printf("%-25s0x%x\n", "Flags:", s->ac_flag); 141 printf("%-25s%d\n", "Nice value:", s->ac_nice); 142 printf("%-25s%s\n", "Command name:", s->ac_comm); 143 printf("%-25s%d\n", "Scheduling discipline:", s->ac_sched); 144 printf("%-25s%d\n", "UID:", s->ac_uid); 145 printf("%-25s%d\n", "GID:", s->ac_gid); 146 printf("%-25s%d\n", "PID:", s->ac_pid); 147 printf("%-25s%d\n", "PPID:", s->ac_ppid); 148 149 if (human_readable) { 150 time_t begin_time = s->ac_btime; 151 printf("%-25s%s", "Begin time:", ctime(&begin_time)); 152 } else { 153 printf("%-25s%d sec\n", "Begin time:", s->ac_btime); 154 } 155 printf("%-25s%llu usec\n", "Elapsed time:", s->ac_etime); 156 printf("%-25s%llu usec\n", "User CPU time:", s->ac_utime); 157 printf("%-25s%llu\n", "Minor page faults:", s->ac_minflt); 158 printf("%-25s%llu\n", "Major page faults:", s->ac_majflt); 159 printf("%-25s%llu usec\n", "Scaled user time:", s->ac_utimescaled); 160 printf("%-25s%llu usec\n", "Scaled system time:", s->ac_stimescaled); 161 162 printf("\nDelay accounting\n"); 163 printf("----------------\n"); 164 printf(" %15s%15s%15s%15s%15s%15s\n", 165 "Count", 166 human_readable ? "Delay (ms)" : "Delay (ns)", 167 "Average delay", 168 "Real delay", 169 "Scaled real", 170 "Virtual delay"); 171 172 if (!human_readable) { 173 printf("CPU %15llu%15llu%15llu%15llu%15llu%15llu\n", 174 s->cpu_count, 175 s->cpu_delay_total, 176 average_ns(s->cpu_delay_total, s->cpu_count), 177 s->cpu_run_real_total, 178 s->cpu_scaled_run_real_total, 179 s->cpu_run_virtual_total); 180 printf("IO %15llu%15llu%15llu\n", 181 s->blkio_count, 182 s->blkio_delay_total, 183 average_ns(s->blkio_delay_total, s->blkio_count)); 184 printf("Swap %15llu%15llu%15llu\n", 185 s->swapin_count, 186 s->swapin_delay_total, 187 average_ns(s->swapin_delay_total, s->swapin_count)); 188 printf("Reclaim%15llu%15llu%15llu\n", 189 s->freepages_count, 190 s->freepages_delay_total, 191 average_ns(s->freepages_delay_total, s->freepages_count)); 192 } else { 193 const double ms_per_ns = 1e6; 194 printf("CPU %15llu%15.3f%15.3f%15.3f%15.3f%15.3f\n", 195 s->cpu_count, 196 s->cpu_delay_total / ms_per_ns, 197 average_ms(s->cpu_delay_total, s->cpu_count), 198 s->cpu_run_real_total / ms_per_ns, 199 s->cpu_scaled_run_real_total / ms_per_ns, 200 s->cpu_run_virtual_total / ms_per_ns); 201 printf("IO %15llu%15.3f%15.3f\n", 202 s->blkio_count, 203 s->blkio_delay_total / ms_per_ns, 204 average_ms(s->blkio_delay_total, s->blkio_count)); 205 printf("Swap %15llu%15.3f%15.3f\n", 206 s->swapin_count, 207 s->swapin_delay_total / ms_per_ns, 208 average_ms(s->swapin_delay_total, s->swapin_count)); 209 printf("Reclaim%15llu%15.3f%15.3f\n", 210 s->freepages_count, 211 s->freepages_delay_total / ms_per_ns, 212 average_ms(s->freepages_delay_total, s->freepages_count)); 213 } 214 215 printf("\nExtended accounting fields\n"); 216 printf("--------------------------\n"); 217 if (human_readable && s->ac_stime) { 218 printf("%-25s%.3f MB\n", "Average RSS usage:", 219 (double)s->coremem / s->ac_stime); 220 printf("%-25s%.3f MB\n", "Average VM usage:", 221 (double)s->virtmem / s->ac_stime); 222 } else { 223 printf("%-25s%llu MB\n", "Accumulated RSS usage:", s->coremem); 224 printf("%-25s%llu MB\n", "Accumulated VM usage:", s->virtmem); 225 } 226 printf("%-25s%llu KB\n", "RSS high water mark:", s->hiwater_rss); 227 printf("%-25s%llu KB\n", "VM high water mark:", s->hiwater_vm); 228 printf("%-25s%llu\n", "IO bytes read:", s->read_char); 229 printf("%-25s%llu\n", "IO bytes written:", s->write_char); 230 printf("%-25s%llu\n", "IO read syscalls:", s->read_syscalls); 231 printf("%-25s%llu\n", "IO write syscalls:", s->write_syscalls); 232 233 printf("\nPer-task/thread statistics\n"); 234 printf("--------------------------\n"); 235 printf("%-25s%llu\n", "Voluntary switches:", s->nvcsw); 236 printf("%-25s%llu\n", "Involuntary switches:", s->nivcsw); 237 238 #if TASKSTATS_VERSION > 8 239 if (s->version > 8) { 240 printf("%-25s%llu\n", "Thrashing count:", s->thrashing_count); 241 printf("%-25s%llu\n", "Thrashing delay total:", s->thrashing_delay_total); 242 } 243 #endif 244 } 245 246 void print_usage() { 247 printf("Linux task stats reporting tool\n" 248 "\n" 249 "Usage: taskstats [options]\n" 250 "\n" 251 "Options:\n" 252 " --help This text\n" 253 " --pid PID Print stats for the process id PID\n" 254 " --tgid TGID Print stats for the thread group id TGID\n" 255 " --raw Print raw numbers instead of human readable units\n" 256 "\n" 257 "Either PID or TGID must be specified. For more documentation about " 258 "the reported fields, see\n" 259 "https://www.kernel.org/doc/Documentation/accounting/" 260 "taskstats-struct.txt\n"); 261 } 262 263 int main(int argc, char** argv) { 264 int command_type = 0; 265 int pid = 0; 266 int human_readable = 1; 267 268 const struct option long_options[] = { 269 {"help", no_argument, 0, 0}, 270 {"pid", required_argument, 0, 0}, 271 {"tgid", required_argument, 0, 0}, 272 {"raw", no_argument, 0, 0}, 273 {0, 0, 0, 0} 274 }; 275 276 while (1) { 277 int option_index; 278 int option_char = getopt_long_only(argc, argv, "", long_options, 279 &option_index); 280 if (option_char == -1) { 281 break; 282 } 283 switch (option_index) { 284 case 0: 285 print_usage(); 286 return EXIT_SUCCESS; 287 case 1: 288 command_type = TASKSTATS_CMD_ATTR_PID; 289 pid = atoi(optarg); 290 break; 291 case 2: 292 command_type = TASKSTATS_CMD_ATTR_TGID; 293 pid = atoi(optarg); 294 break; 295 case 3: 296 human_readable = 0; 297 break; 298 default: 299 break; 300 }; 301 } 302 303 if (!pid) { 304 printf("Either PID or TGID must be specified\n"); 305 return EXIT_FAILURE; 306 } 307 308 struct nl_sock* netlink_socket = nl_socket_alloc(); 309 if (!netlink_socket) { 310 fprintf(stderr, "Unable to allocate netlink socket\n"); 311 goto error; 312 } 313 314 int ret = genl_connect(netlink_socket); 315 if (ret < 0) { 316 nl_perror(ret, "Unable to open netlink socket (are you root?)"); 317 goto error; 318 } 319 320 int family_id = genl_ctrl_resolve(netlink_socket, TASKSTATS_GENL_NAME); 321 if (family_id < 0) { 322 nl_perror(family_id, "Unable to determine taskstats family id " 323 "(does your kernel support taskstats?)"); 324 goto error; 325 } 326 struct TaskStatistics stats; 327 ret = query_task_stats(netlink_socket, family_id, command_type, pid, &stats); 328 if (ret < 0) { 329 nl_perror(ret, "Failed to query taskstats"); 330 goto error; 331 } 332 print_task_stats(&stats, human_readable); 333 334 nl_socket_free(netlink_socket); 335 return EXIT_SUCCESS; 336 337 error: 338 if (netlink_socket) { 339 nl_socket_free(netlink_socket); 340 } 341 return EXIT_FAILURE; 342 } 343