1 // SPDX-License-Identifier: GPL-2.0 2 /* getdelays.c 3 * 4 * Utility to get per-pid and per-tgid delay accounting statistics 5 * Also illustrates usage of the taskstats interface 6 * 7 * Copyright (C) Shailabh Nagar, IBM Corp. 2005 8 * Copyright (C) Balbir Singh, IBM Corp. 2006 9 * Copyright (c) Jay Lan, SGI. 2006 10 * 11 * Compile with 12 * gcc -I/usr/src/linux/include getdelays.c -o getdelays 13 */ 14 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <errno.h> 18 #include <unistd.h> 19 #include <poll.h> 20 #include <string.h> 21 #include <fcntl.h> 22 #include <sys/types.h> 23 #include <sys/stat.h> 24 #include <sys/socket.h> 25 #include <sys/wait.h> 26 #include <signal.h> 27 #include <time.h> 28 29 #include <linux/genetlink.h> 30 #include <linux/taskstats.h> 31 #include <linux/cgroupstats.h> 32 33 /* 34 * Generic macros for dealing with netlink sockets. Might be duplicated 35 * elsewhere. It is recommended that commercial grade applications use 36 * libnl or libnetlink and use the interfaces provided by the library 37 */ 38 #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) 39 #define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN) 40 #define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN)) 41 #define NLA_PAYLOAD(len) (len - NLA_HDRLEN) 42 43 #define err(code, fmt, arg...) \ 44 do { \ 45 fprintf(stderr, fmt, ##arg); \ 46 exit(code); \ 47 } while (0) 48 49 int rcvbufsz; 50 char name[100]; 51 int dbg; 52 int print_delays; 53 int print_io_accounting; 54 int print_task_context_switch_counts; 55 56 #define PRINTF(fmt, arg...) { \ 57 if (dbg) { \ 58 printf(fmt, ##arg); \ 59 } \ 60 } 61 62 /* Maximum size of response requested or message sent */ 63 #define MAX_MSG_SIZE 2048 64 /* Maximum number of cpus expected to be specified in a cpumask */ 65 #define MAX_CPUS 32 66 67 struct msgtemplate { 68 struct nlmsghdr n; 69 struct genlmsghdr g; 70 char buf[MAX_MSG_SIZE]; 71 }; 72 73 char cpumask[100+6*MAX_CPUS]; 74 75 static void usage(void) 76 { 77 fprintf(stderr, "getdelays [-dilv] [-w logfile] [-r bufsize] " 78 "[-m cpumask] [-t tgid] [-p pid]\n"); 79 fprintf(stderr, " -d: print delayacct stats\n"); 80 fprintf(stderr, " -i: print IO accounting (works only with -p)\n"); 81 fprintf(stderr, " -l: listen forever\n"); 82 fprintf(stderr, " -v: debug on\n"); 83 fprintf(stderr, " -C: container path\n"); 84 } 85 86 /* 87 * Create a raw netlink socket and bind 88 */ 89 static int create_nl_socket(int protocol) 90 { 91 int fd; 92 struct sockaddr_nl local; 93 94 fd = socket(AF_NETLINK, SOCK_RAW, protocol); 95 if (fd < 0) 96 return -1; 97 98 if (rcvbufsz) 99 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, 100 &rcvbufsz, sizeof(rcvbufsz)) < 0) { 101 fprintf(stderr, "Unable to set socket rcv buf size to %d\n", 102 rcvbufsz); 103 goto error; 104 } 105 106 memset(&local, 0, sizeof(local)); 107 local.nl_family = AF_NETLINK; 108 109 if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0) 110 goto error; 111 112 return fd; 113 error: 114 close(fd); 115 return -1; 116 } 117 118 static int recv_taskstats_msg(int sd, struct msgtemplate *msg) 119 { 120 struct sockaddr_nl nladdr; 121 struct iovec iov = { 122 .iov_base = msg, 123 .iov_len = sizeof(*msg), 124 }; 125 struct msghdr hdr = { 126 .msg_name = &nladdr, 127 .msg_namelen = sizeof(nladdr), 128 .msg_iov = &iov, 129 .msg_iovlen = 1, 130 }; 131 int ret; 132 133 ret = recvmsg(sd, &hdr, 0); 134 if (ret < 0) 135 return -1; 136 if (hdr.msg_flags & MSG_TRUNC) { 137 errno = EMSGSIZE; 138 return -1; 139 } 140 141 return ret; 142 } 143 144 145 static int send_cmd(int sd, __u16 nlmsg_type, __u32 nlmsg_pid, 146 __u8 genl_cmd, __u16 nla_type, 147 void *nla_data, int nla_len) 148 { 149 struct nlattr *na; 150 struct sockaddr_nl nladdr; 151 int r, buflen; 152 char *buf; 153 154 struct msgtemplate msg; 155 156 msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); 157 msg.n.nlmsg_type = nlmsg_type; 158 msg.n.nlmsg_flags = NLM_F_REQUEST; 159 msg.n.nlmsg_seq = 0; 160 msg.n.nlmsg_pid = nlmsg_pid; 161 msg.g.cmd = genl_cmd; 162 msg.g.version = 0x1; 163 na = (struct nlattr *) GENLMSG_DATA(&msg); 164 na->nla_type = nla_type; 165 na->nla_len = nla_len + NLA_HDRLEN; 166 memcpy(NLA_DATA(na), nla_data, nla_len); 167 msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); 168 169 buf = (char *) &msg; 170 buflen = msg.n.nlmsg_len ; 171 memset(&nladdr, 0, sizeof(nladdr)); 172 nladdr.nl_family = AF_NETLINK; 173 while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *) &nladdr, 174 sizeof(nladdr))) < buflen) { 175 if (r > 0) { 176 buf += r; 177 buflen -= r; 178 } else if (errno != EAGAIN) 179 return -1; 180 } 181 return 0; 182 } 183 184 185 /* 186 * Probe the controller in genetlink to find the family id 187 * for the TASKSTATS family 188 */ 189 static int get_family_id(int sd) 190 { 191 struct { 192 struct nlmsghdr n; 193 struct genlmsghdr g; 194 char buf[256]; 195 } ans; 196 197 int id = 0, rc; 198 struct nlattr *na; 199 int rep_len; 200 201 strcpy(name, TASKSTATS_GENL_NAME); 202 rc = send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY, 203 CTRL_ATTR_FAMILY_NAME, (void *)name, 204 strlen(TASKSTATS_GENL_NAME)+1); 205 if (rc < 0) 206 return 0; /* sendto() failure? */ 207 208 rep_len = recv(sd, &ans, sizeof(ans), 0); 209 if (ans.n.nlmsg_type == NLMSG_ERROR || 210 (rep_len < 0) || !NLMSG_OK((&ans.n), rep_len)) 211 return 0; 212 213 na = (struct nlattr *) GENLMSG_DATA(&ans); 214 na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len)); 215 if (na->nla_type == CTRL_ATTR_FAMILY_ID) { 216 id = *(__u16 *) NLA_DATA(na); 217 } 218 return id; 219 } 220 221 #define average_ms(t, c) (t / 1000000ULL / (c ? c : 1)) 222 #define delay_ms(t) (t / 1000000ULL) 223 224 /* 225 * Format __kernel_timespec to human readable string (YYYY-MM-DD HH:MM:SS) 226 * Returns formatted string or "N/A" if timestamp is zero 227 */ 228 static const char *format_timespec(struct __kernel_timespec *ts) 229 { 230 static char buffer[32]; 231 struct tm tm_info; 232 __kernel_time_t time_sec; 233 234 /* Check if timestamp is zero (not set) */ 235 if (ts->tv_sec == 0 && ts->tv_nsec == 0) 236 return "N/A"; 237 238 time_sec = ts->tv_sec; 239 240 /* Use thread-safe localtime_r */ 241 if (localtime_r(&time_sec, &tm_info) == NULL) 242 return "N/A"; 243 244 strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S", &tm_info); 245 246 return buffer; 247 } 248 249 /* 250 * Version compatibility note: 251 * Field availability depends on taskstats version (t->version), 252 * corresponding to TASKSTATS_VERSION in kernel headers 253 * see include/uapi/linux/taskstats.h 254 * 255 * Version feature mapping: 256 * version >= 11 - supports COMPACT statistics 257 * version >= 13 - supports WPCOPY statistics 258 * version >= 14 - supports IRQ statistics 259 * version >= 16 - supports *_max and *_min delay statistics 260 * version >= 17 - supports delay max timestamp statistics 261 * 262 * Always verify version before accessing version-dependent fields 263 * to maintain backward compatibility. 264 */ 265 #define PRINT_CPU_DELAY(version, t) \ 266 do { \ 267 if (version >= 17) { \ 268 printf("%-10s%15s%15s%15s%15s%15s%15s%15s%25s\n", \ 269 "CPU", "count", "real total", "virtual total", \ 270 "delay total", "delay average", "delay max", \ 271 "delay min", "delay max timestamp"); \ 272 printf(" %15llu%15llu%15llu%15llu%15.3fms%13.6fms%13.6fms%23s\n", \ 273 (unsigned long long)(t)->cpu_count, \ 274 (unsigned long long)(t)->cpu_run_real_total, \ 275 (unsigned long long)(t)->cpu_run_virtual_total, \ 276 (unsigned long long)(t)->cpu_delay_total, \ 277 average_ms((double)(t)->cpu_delay_total, (t)->cpu_count), \ 278 delay_ms((double)(t)->cpu_delay_max), \ 279 delay_ms((double)(t)->cpu_delay_min), \ 280 format_timespec(&(t)->cpu_delay_max_ts)); \ 281 } else if (version >= 16) { \ 282 printf("%-10s%15s%15s%15s%15s%15s%15s%15s\n", \ 283 "CPU", "count", "real total", "virtual total", \ 284 "delay total", "delay average", "delay max", "delay min"); \ 285 printf(" %15llu%15llu%15llu%15llu%15.3fms%13.6fms%13.6fms\n", \ 286 (unsigned long long)(t)->cpu_count, \ 287 (unsigned long long)(t)->cpu_run_real_total, \ 288 (unsigned long long)(t)->cpu_run_virtual_total, \ 289 (unsigned long long)(t)->cpu_delay_total, \ 290 average_ms((double)(t)->cpu_delay_total, (t)->cpu_count), \ 291 delay_ms((double)(t)->cpu_delay_max), \ 292 delay_ms((double)(t)->cpu_delay_min)); \ 293 } else { \ 294 printf("%-10s%15s%15s%15s%15s%15s\n", \ 295 "CPU", "count", "real total", "virtual total", \ 296 "delay total", "delay average"); \ 297 printf(" %15llu%15llu%15llu%15llu%15.3fms\n", \ 298 (unsigned long long)(t)->cpu_count, \ 299 (unsigned long long)(t)->cpu_run_real_total, \ 300 (unsigned long long)(t)->cpu_run_virtual_total, \ 301 (unsigned long long)(t)->cpu_delay_total, \ 302 average_ms((double)(t)->cpu_delay_total, (t)->cpu_count)); \ 303 } \ 304 } while (0) 305 #define PRINT_FILED_DELAY(name, version, t, count, total, max, min) \ 306 do { \ 307 if (version >= 16) { \ 308 printf("%-10s%15s%15s%15s%15s%15s\n", \ 309 name, "count", "delay total", "delay average", \ 310 "delay max", "delay min"); \ 311 printf(" %15llu%15llu%15.3fms%13.6fms%13.6fms\n", \ 312 (unsigned long long)(t)->count, \ 313 (unsigned long long)(t)->total, \ 314 average_ms((double)(t)->total, (t)->count), \ 315 delay_ms((double)(t)->max), \ 316 delay_ms((double)(t)->min)); \ 317 } else { \ 318 printf("%-10s%15s%15s%15s\n", \ 319 name, "count", "delay total", "delay average"); \ 320 printf(" %15llu%15llu%15.3fms\n", \ 321 (unsigned long long)(t)->count, \ 322 (unsigned long long)(t)->total, \ 323 average_ms((double)(t)->total, (t)->count)); \ 324 } \ 325 } while (0) 326 327 #define PRINT_FILED_DELAY_WITH_TS(name, version, t, count, total, max, min, max_ts) \ 328 do { \ 329 if (version >= 17) { \ 330 printf("%-10s%15s%15s%15s%15s%15s%25s\n", \ 331 name, "count", "delay total", "delay average", \ 332 "delay max", "delay min", "delay max timestamp"); \ 333 printf(" %15llu%15llu%15.3fms%13.6fms%13.6fms%23s\n", \ 334 (unsigned long long)(t)->count, \ 335 (unsigned long long)(t)->total, \ 336 average_ms((double)(t)->total, (t)->count), \ 337 delay_ms((double)(t)->max), \ 338 delay_ms((double)(t)->min), \ 339 format_timespec(&(t)->max_ts)); \ 340 } else if (version >= 16) { \ 341 printf("%-10s%15s%15s%15s%15s%15s\n", \ 342 name, "count", "delay total", "delay average", \ 343 "delay max", "delay min"); \ 344 printf(" %15llu%15llu%15.3fms%13.6fms%13.6fms\n", \ 345 (unsigned long long)(t)->count, \ 346 (unsigned long long)(t)->total, \ 347 average_ms((double)(t)->total, (t)->count), \ 348 delay_ms((double)(t)->max), \ 349 delay_ms((double)(t)->min)); \ 350 } else { \ 351 printf("%-10s%15s%15s%15s\n", \ 352 name, "count", "delay total", "delay average"); \ 353 printf(" %15llu%15llu%15.3fms\n", \ 354 (unsigned long long)(t)->count, \ 355 (unsigned long long)(t)->total, \ 356 average_ms((double)(t)->total, (t)->count)); \ 357 } \ 358 } while (0) 359 360 static void print_delayacct(struct taskstats *t) 361 { 362 printf("\n\n"); 363 364 PRINT_CPU_DELAY(t->version, t); 365 366 /* Use new macro with timestamp support for version >= 17 */ 367 if (t->version >= 17) { 368 PRINT_FILED_DELAY_WITH_TS("IO", t->version, t, 369 blkio_count, blkio_delay_total, 370 blkio_delay_max, blkio_delay_min, blkio_delay_max_ts); 371 372 PRINT_FILED_DELAY_WITH_TS("SWAP", t->version, t, 373 swapin_count, swapin_delay_total, 374 swapin_delay_max, swapin_delay_min, swapin_delay_max_ts); 375 376 PRINT_FILED_DELAY_WITH_TS("RECLAIM", t->version, t, 377 freepages_count, freepages_delay_total, 378 freepages_delay_max, freepages_delay_min, freepages_delay_max_ts); 379 380 PRINT_FILED_DELAY_WITH_TS("THRASHING", t->version, t, 381 thrashing_count, thrashing_delay_total, 382 thrashing_delay_max, thrashing_delay_min, thrashing_delay_max_ts); 383 384 if (t->version >= 11) { 385 PRINT_FILED_DELAY_WITH_TS("COMPACT", t->version, t, 386 compact_count, compact_delay_total, 387 compact_delay_max, compact_delay_min, compact_delay_max_ts); 388 } 389 390 if (t->version >= 13) { 391 PRINT_FILED_DELAY_WITH_TS("WPCOPY", t->version, t, 392 wpcopy_count, wpcopy_delay_total, 393 wpcopy_delay_max, wpcopy_delay_min, wpcopy_delay_max_ts); 394 } 395 396 if (t->version >= 14) { 397 PRINT_FILED_DELAY_WITH_TS("IRQ", t->version, t, 398 irq_count, irq_delay_total, 399 irq_delay_max, irq_delay_min, irq_delay_max_ts); 400 } 401 } else { 402 /* Use original macro for older versions */ 403 PRINT_FILED_DELAY("IO", t->version, t, 404 blkio_count, blkio_delay_total, 405 blkio_delay_max, blkio_delay_min); 406 407 PRINT_FILED_DELAY("SWAP", t->version, t, 408 swapin_count, swapin_delay_total, 409 swapin_delay_max, swapin_delay_min); 410 411 PRINT_FILED_DELAY("RECLAIM", t->version, t, 412 freepages_count, freepages_delay_total, 413 freepages_delay_max, freepages_delay_min); 414 415 PRINT_FILED_DELAY("THRASHING", t->version, t, 416 thrashing_count, thrashing_delay_total, 417 thrashing_delay_max, thrashing_delay_min); 418 419 if (t->version >= 11) { 420 PRINT_FILED_DELAY("COMPACT", t->version, t, 421 compact_count, compact_delay_total, 422 compact_delay_max, compact_delay_min); 423 } 424 425 if (t->version >= 13) { 426 PRINT_FILED_DELAY("WPCOPY", t->version, t, 427 wpcopy_count, wpcopy_delay_total, 428 wpcopy_delay_max, wpcopy_delay_min); 429 } 430 431 if (t->version >= 14) { 432 PRINT_FILED_DELAY("IRQ", t->version, t, 433 irq_count, irq_delay_total, 434 irq_delay_max, irq_delay_min); 435 } 436 } 437 } 438 439 static void task_context_switch_counts(struct taskstats *t) 440 { 441 printf("\n\nTask %15s%15s\n" 442 " %15llu%15llu\n", 443 "voluntary", "nonvoluntary", 444 (unsigned long long)t->nvcsw, (unsigned long long)t->nivcsw); 445 } 446 447 static void print_cgroupstats(struct cgroupstats *c) 448 { 449 printf("sleeping %llu, blocked %llu, running %llu, stopped %llu, " 450 "uninterruptible %llu\n", (unsigned long long)c->nr_sleeping, 451 (unsigned long long)c->nr_io_wait, 452 (unsigned long long)c->nr_running, 453 (unsigned long long)c->nr_stopped, 454 (unsigned long long)c->nr_uninterruptible); 455 } 456 457 458 static void print_ioacct(struct taskstats *t) 459 { 460 printf("%s: read=%llu, write=%llu, cancelled_write=%llu\n", 461 t->ac_comm, 462 (unsigned long long)t->read_bytes, 463 (unsigned long long)t->write_bytes, 464 (unsigned long long)t->cancelled_write_bytes); 465 } 466 467 int main(int argc, char *argv[]) 468 { 469 int c, rc, rep_len, aggr_len, len2; 470 int cmd_type = TASKSTATS_CMD_ATTR_UNSPEC; 471 __u16 id; 472 __u32 mypid; 473 474 struct nlattr *na; 475 int nl_sd = -1; 476 int len = 0; 477 pid_t tid = 0; 478 pid_t rtid = 0; 479 480 int fd = 0; 481 int write_file = 0; 482 int maskset = 0; 483 char *logfile = NULL; 484 int loop = 0; 485 int containerset = 0; 486 char *containerpath = NULL; 487 int cfd = 0; 488 int forking = 0; 489 sigset_t sigset; 490 491 struct msgtemplate msg; 492 493 while (!forking) { 494 c = getopt(argc, argv, "qdiw:r:m:t:p:vlC:c:"); 495 if (c < 0) 496 break; 497 498 switch (c) { 499 case 'd': 500 printf("print delayacct stats ON\n"); 501 print_delays = 1; 502 break; 503 case 'i': 504 printf("printing IO accounting\n"); 505 print_io_accounting = 1; 506 break; 507 case 'q': 508 printf("printing task/process context switch rates\n"); 509 print_task_context_switch_counts = 1; 510 break; 511 case 'C': 512 containerset = 1; 513 containerpath = optarg; 514 break; 515 case 'w': 516 logfile = strdup(optarg); 517 printf("write to file %s\n", logfile); 518 write_file = 1; 519 break; 520 case 'r': 521 rcvbufsz = atoi(optarg); 522 printf("receive buf size %d\n", rcvbufsz); 523 if (rcvbufsz < 0) 524 err(1, "Invalid rcv buf size\n"); 525 break; 526 case 'm': 527 strncpy(cpumask, optarg, sizeof(cpumask)); 528 cpumask[sizeof(cpumask) - 1] = '\0'; 529 maskset = 1; 530 printf("cpumask %s maskset %d\n", cpumask, maskset); 531 break; 532 case 't': 533 tid = atoi(optarg); 534 if (!tid) 535 err(1, "Invalid tgid\n"); 536 cmd_type = TASKSTATS_CMD_ATTR_TGID; 537 break; 538 case 'p': 539 tid = atoi(optarg); 540 if (!tid) 541 err(1, "Invalid pid\n"); 542 cmd_type = TASKSTATS_CMD_ATTR_PID; 543 break; 544 case 'c': 545 546 /* Block SIGCHLD for sigwait() later */ 547 if (sigemptyset(&sigset) == -1) 548 err(1, "Failed to empty sigset"); 549 if (sigaddset(&sigset, SIGCHLD)) 550 err(1, "Failed to set sigchld in sigset"); 551 sigprocmask(SIG_BLOCK, &sigset, NULL); 552 553 /* fork/exec a child */ 554 tid = fork(); 555 if (tid < 0) 556 err(1, "Fork failed\n"); 557 if (tid == 0) 558 if (execvp(argv[optind - 1], 559 &argv[optind - 1]) < 0) 560 exit(-1); 561 562 /* Set the command type and avoid further processing */ 563 cmd_type = TASKSTATS_CMD_ATTR_PID; 564 forking = 1; 565 break; 566 case 'v': 567 printf("debug on\n"); 568 dbg = 1; 569 break; 570 case 'l': 571 printf("listen forever\n"); 572 loop = 1; 573 break; 574 default: 575 usage(); 576 exit(-1); 577 } 578 } 579 580 if (write_file) { 581 fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC, 582 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 583 if (fd == -1) { 584 perror("Cannot open output file\n"); 585 exit(1); 586 } 587 } 588 589 nl_sd = create_nl_socket(NETLINK_GENERIC); 590 if (nl_sd < 0) 591 err(1, "error creating Netlink socket\n"); 592 593 594 mypid = getpid(); 595 id = get_family_id(nl_sd); 596 if (!id) { 597 fprintf(stderr, "Error getting family id, errno %d\n", errno); 598 goto err; 599 } 600 PRINTF("family id %d\n", id); 601 602 if (maskset) { 603 rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, 604 TASKSTATS_CMD_ATTR_REGISTER_CPUMASK, 605 &cpumask, strlen(cpumask) + 1); 606 PRINTF("Sent register cpumask, retval %d\n", rc); 607 if (rc < 0) { 608 fprintf(stderr, "error sending register cpumask\n"); 609 goto err; 610 } 611 } 612 613 if (tid && containerset) { 614 fprintf(stderr, "Select either -t or -C, not both\n"); 615 goto err; 616 } 617 618 /* 619 * If we forked a child, wait for it to exit. Cannot use waitpid() 620 * as all the delicious data would be reaped as part of the wait 621 */ 622 if (tid && forking) { 623 int sig_received; 624 sigwait(&sigset, &sig_received); 625 } 626 627 if (tid) { 628 rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, 629 cmd_type, &tid, sizeof(__u32)); 630 PRINTF("Sent pid/tgid, retval %d\n", rc); 631 if (rc < 0) { 632 fprintf(stderr, "error sending tid/tgid cmd\n"); 633 goto done; 634 } 635 } 636 637 if (containerset) { 638 cfd = open(containerpath, O_RDONLY); 639 if (cfd < 0) { 640 perror("error opening container file"); 641 goto err; 642 } 643 rc = send_cmd(nl_sd, id, mypid, CGROUPSTATS_CMD_GET, 644 CGROUPSTATS_CMD_ATTR_FD, &cfd, sizeof(__u32)); 645 if (rc < 0) { 646 perror("error sending cgroupstats command"); 647 goto err; 648 } 649 } 650 if (!maskset && !tid && !containerset) { 651 usage(); 652 goto err; 653 } 654 655 do { 656 rep_len = recv_taskstats_msg(nl_sd, &msg); 657 PRINTF("received %d bytes\n", rep_len); 658 659 if (rep_len < 0) { 660 if (errno == EMSGSIZE) 661 fprintf(stderr, 662 "dropped truncated taskstats netlink message, please increase MAX_MSG_SIZE\n"); 663 else 664 fprintf(stderr, "nonfatal reply error: errno %d\n", 665 errno); 666 continue; 667 } 668 if (msg.n.nlmsg_type == NLMSG_ERROR || 669 !NLMSG_OK((&msg.n), rep_len)) { 670 struct nlmsgerr *err = NLMSG_DATA(&msg); 671 fprintf(stderr, "fatal reply error, errno %d\n", 672 err->error); 673 goto done; 674 } 675 676 PRINTF("nlmsghdr size=%zu, nlmsg_len=%d, rep_len=%d\n", 677 sizeof(struct nlmsghdr), msg.n.nlmsg_len, rep_len); 678 679 680 rep_len = GENLMSG_PAYLOAD(&msg.n); 681 682 na = (struct nlattr *) GENLMSG_DATA(&msg); 683 len = 0; 684 while (len < rep_len) { 685 len += NLA_ALIGN(na->nla_len); 686 switch (na->nla_type) { 687 case TASKSTATS_TYPE_AGGR_TGID: 688 /* Fall through */ 689 case TASKSTATS_TYPE_AGGR_PID: 690 aggr_len = NLA_PAYLOAD(na->nla_len); 691 len2 = 0; 692 /* For nested attributes, na follows */ 693 na = (struct nlattr *) NLA_DATA(na); 694 while (len2 < aggr_len) { 695 switch (na->nla_type) { 696 case TASKSTATS_TYPE_PID: 697 rtid = *(int *) NLA_DATA(na); 698 if (print_delays) 699 printf("PID\t%d\n", rtid); 700 break; 701 case TASKSTATS_TYPE_TGID: 702 rtid = *(int *) NLA_DATA(na); 703 if (print_delays) 704 printf("TGID\t%d\n", rtid); 705 break; 706 case TASKSTATS_TYPE_STATS: 707 PRINTF("version %u\n", 708 ((struct taskstats *) 709 NLA_DATA(na))->version); 710 if (print_delays) 711 print_delayacct((struct taskstats *) NLA_DATA(na)); 712 if (print_io_accounting) 713 print_ioacct((struct taskstats *) NLA_DATA(na)); 714 if (print_task_context_switch_counts) 715 task_context_switch_counts((struct taskstats *) NLA_DATA(na)); 716 if (fd) { 717 if (write(fd, NLA_DATA(na), na->nla_len) < 0) { 718 err(1,"write error\n"); 719 } 720 } 721 if (!loop) 722 goto done; 723 break; 724 case TASKSTATS_TYPE_NULL: 725 break; 726 default: 727 fprintf(stderr, "Unknown nested" 728 " nla_type %d\n", 729 na->nla_type); 730 break; 731 } 732 len2 += NLA_ALIGN(na->nla_len); 733 na = (struct nlattr *)((char *)na + 734 NLA_ALIGN(na->nla_len)); 735 } 736 break; 737 738 case CGROUPSTATS_TYPE_CGROUP_STATS: 739 print_cgroupstats(NLA_DATA(na)); 740 break; 741 default: 742 fprintf(stderr, "Unknown nla_type %d\n", 743 na->nla_type); 744 case TASKSTATS_TYPE_NULL: 745 break; 746 } 747 na = (struct nlattr *) (GENLMSG_DATA(&msg) + len); 748 } 749 } while (loop); 750 done: 751 if (maskset) { 752 rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, 753 TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, 754 &cpumask, strlen(cpumask) + 1); 755 printf("Sent deregister mask, retval %d\n", rc); 756 if (rc < 0) 757 err(rc, "error sending deregister cpumask\n"); 758 } 759 err: 760 close(nl_sd); 761 if (fd) 762 close(fd); 763 if (cfd) 764 close(cfd); 765 return 0; 766 } 767