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