Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 772 lines 21 kB view raw
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 49int rcvbufsz; 50char name[100]; 51int dbg; 52int print_delays; 53int print_io_accounting; 54int 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 67struct msgtemplate { 68 struct nlmsghdr n; 69 struct genlmsghdr g; 70 char buf[MAX_MSG_SIZE]; 71}; 72 73char cpumask[100+6*MAX_CPUS]; 74 75static 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 */ 89static 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; 113error: 114 close(fd); 115 return -1; 116} 117 118static 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 145static 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 */ 189static 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 */ 228static 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 snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02d", 245 tm_info.tm_year + 1900, 246 tm_info.tm_mon + 1, 247 tm_info.tm_mday, 248 tm_info.tm_hour, 249 tm_info.tm_min, 250 tm_info.tm_sec); 251 252 return buffer; 253} 254 255/* 256 * Version compatibility note: 257 * Field availability depends on taskstats version (t->version), 258 * corresponding to TASKSTATS_VERSION in kernel headers 259 * see include/uapi/linux/taskstats.h 260 * 261 * Version feature mapping: 262 * version >= 11 - supports COMPACT statistics 263 * version >= 13 - supports WPCOPY statistics 264 * version >= 14 - supports IRQ statistics 265 * version >= 16 - supports *_max and *_min delay statistics 266 * version >= 17 - supports delay max timestamp statistics 267 * 268 * Always verify version before accessing version-dependent fields 269 * to maintain backward compatibility. 270 */ 271#define PRINT_CPU_DELAY(version, t) \ 272 do { \ 273 if (version >= 17) { \ 274 printf("%-10s%15s%15s%15s%15s%15s%15s%15s%25s\n", \ 275 "CPU", "count", "real total", "virtual total", \ 276 "delay total", "delay average", "delay max", \ 277 "delay min", "delay max timestamp"); \ 278 printf(" %15llu%15llu%15llu%15llu%15.3fms%13.6fms%13.6fms%23s\n", \ 279 (unsigned long long)(t)->cpu_count, \ 280 (unsigned long long)(t)->cpu_run_real_total, \ 281 (unsigned long long)(t)->cpu_run_virtual_total, \ 282 (unsigned long long)(t)->cpu_delay_total, \ 283 average_ms((double)(t)->cpu_delay_total, (t)->cpu_count), \ 284 delay_ms((double)(t)->cpu_delay_max), \ 285 delay_ms((double)(t)->cpu_delay_min), \ 286 format_timespec(&(t)->cpu_delay_max_ts)); \ 287 } else if (version >= 16) { \ 288 printf("%-10s%15s%15s%15s%15s%15s%15s%15s\n", \ 289 "CPU", "count", "real total", "virtual total", \ 290 "delay total", "delay average", "delay max", "delay min"); \ 291 printf(" %15llu%15llu%15llu%15llu%15.3fms%13.6fms%13.6fms\n", \ 292 (unsigned long long)(t)->cpu_count, \ 293 (unsigned long long)(t)->cpu_run_real_total, \ 294 (unsigned long long)(t)->cpu_run_virtual_total, \ 295 (unsigned long long)(t)->cpu_delay_total, \ 296 average_ms((double)(t)->cpu_delay_total, (t)->cpu_count), \ 297 delay_ms((double)(t)->cpu_delay_max), \ 298 delay_ms((double)(t)->cpu_delay_min)); \ 299 } else { \ 300 printf("%-10s%15s%15s%15s%15s%15s\n", \ 301 "CPU", "count", "real total", "virtual total", \ 302 "delay total", "delay average"); \ 303 printf(" %15llu%15llu%15llu%15llu%15.3fms\n", \ 304 (unsigned long long)(t)->cpu_count, \ 305 (unsigned long long)(t)->cpu_run_real_total, \ 306 (unsigned long long)(t)->cpu_run_virtual_total, \ 307 (unsigned long long)(t)->cpu_delay_total, \ 308 average_ms((double)(t)->cpu_delay_total, (t)->cpu_count)); \ 309 } \ 310 } while (0) 311#define PRINT_FILED_DELAY(name, version, t, count, total, max, min) \ 312 do { \ 313 if (version >= 16) { \ 314 printf("%-10s%15s%15s%15s%15s%15s\n", \ 315 name, "count", "delay total", "delay average", \ 316 "delay max", "delay min"); \ 317 printf(" %15llu%15llu%15.3fms%13.6fms%13.6fms\n", \ 318 (unsigned long long)(t)->count, \ 319 (unsigned long long)(t)->total, \ 320 average_ms((double)(t)->total, (t)->count), \ 321 delay_ms((double)(t)->max), \ 322 delay_ms((double)(t)->min)); \ 323 } else { \ 324 printf("%-10s%15s%15s%15s\n", \ 325 name, "count", "delay total", "delay average"); \ 326 printf(" %15llu%15llu%15.3fms\n", \ 327 (unsigned long long)(t)->count, \ 328 (unsigned long long)(t)->total, \ 329 average_ms((double)(t)->total, (t)->count)); \ 330 } \ 331 } while (0) 332 333#define PRINT_FILED_DELAY_WITH_TS(name, version, t, count, total, max, min, max_ts) \ 334 do { \ 335 if (version >= 17) { \ 336 printf("%-10s%15s%15s%15s%15s%15s%25s\n", \ 337 name, "count", "delay total", "delay average", \ 338 "delay max", "delay min", "delay max timestamp"); \ 339 printf(" %15llu%15llu%15.3fms%13.6fms%13.6fms%23s\n", \ 340 (unsigned long long)(t)->count, \ 341 (unsigned long long)(t)->total, \ 342 average_ms((double)(t)->total, (t)->count), \ 343 delay_ms((double)(t)->max), \ 344 delay_ms((double)(t)->min), \ 345 format_timespec(&(t)->max_ts)); \ 346 } else if (version >= 16) { \ 347 printf("%-10s%15s%15s%15s%15s%15s\n", \ 348 name, "count", "delay total", "delay average", \ 349 "delay max", "delay min"); \ 350 printf(" %15llu%15llu%15.3fms%13.6fms%13.6fms\n", \ 351 (unsigned long long)(t)->count, \ 352 (unsigned long long)(t)->total, \ 353 average_ms((double)(t)->total, (t)->count), \ 354 delay_ms((double)(t)->max), \ 355 delay_ms((double)(t)->min)); \ 356 } else { \ 357 printf("%-10s%15s%15s%15s\n", \ 358 name, "count", "delay total", "delay average"); \ 359 printf(" %15llu%15llu%15.3fms\n", \ 360 (unsigned long long)(t)->count, \ 361 (unsigned long long)(t)->total, \ 362 average_ms((double)(t)->total, (t)->count)); \ 363 } \ 364 } while (0) 365 366static void print_delayacct(struct taskstats *t) 367{ 368 printf("\n\n"); 369 370 PRINT_CPU_DELAY(t->version, t); 371 372 /* Use new macro with timestamp support for version >= 17 */ 373 if (t->version >= 17) { 374 PRINT_FILED_DELAY_WITH_TS("IO", t->version, t, 375 blkio_count, blkio_delay_total, 376 blkio_delay_max, blkio_delay_min, blkio_delay_max_ts); 377 378 PRINT_FILED_DELAY_WITH_TS("SWAP", t->version, t, 379 swapin_count, swapin_delay_total, 380 swapin_delay_max, swapin_delay_min, swapin_delay_max_ts); 381 382 PRINT_FILED_DELAY_WITH_TS("RECLAIM", t->version, t, 383 freepages_count, freepages_delay_total, 384 freepages_delay_max, freepages_delay_min, freepages_delay_max_ts); 385 386 PRINT_FILED_DELAY_WITH_TS("THRASHING", t->version, t, 387 thrashing_count, thrashing_delay_total, 388 thrashing_delay_max, thrashing_delay_min, thrashing_delay_max_ts); 389 390 if (t->version >= 11) { 391 PRINT_FILED_DELAY_WITH_TS("COMPACT", t->version, t, 392 compact_count, compact_delay_total, 393 compact_delay_max, compact_delay_min, compact_delay_max_ts); 394 } 395 396 if (t->version >= 13) { 397 PRINT_FILED_DELAY_WITH_TS("WPCOPY", t->version, t, 398 wpcopy_count, wpcopy_delay_total, 399 wpcopy_delay_max, wpcopy_delay_min, wpcopy_delay_max_ts); 400 } 401 402 if (t->version >= 14) { 403 PRINT_FILED_DELAY_WITH_TS("IRQ", t->version, t, 404 irq_count, irq_delay_total, 405 irq_delay_max, irq_delay_min, irq_delay_max_ts); 406 } 407 } else { 408 /* Use original macro for older versions */ 409 PRINT_FILED_DELAY("IO", t->version, t, 410 blkio_count, blkio_delay_total, 411 blkio_delay_max, blkio_delay_min); 412 413 PRINT_FILED_DELAY("SWAP", t->version, t, 414 swapin_count, swapin_delay_total, 415 swapin_delay_max, swapin_delay_min); 416 417 PRINT_FILED_DELAY("RECLAIM", t->version, t, 418 freepages_count, freepages_delay_total, 419 freepages_delay_max, freepages_delay_min); 420 421 PRINT_FILED_DELAY("THRASHING", t->version, t, 422 thrashing_count, thrashing_delay_total, 423 thrashing_delay_max, thrashing_delay_min); 424 425 if (t->version >= 11) { 426 PRINT_FILED_DELAY("COMPACT", t->version, t, 427 compact_count, compact_delay_total, 428 compact_delay_max, compact_delay_min); 429 } 430 431 if (t->version >= 13) { 432 PRINT_FILED_DELAY("WPCOPY", t->version, t, 433 wpcopy_count, wpcopy_delay_total, 434 wpcopy_delay_max, wpcopy_delay_min); 435 } 436 437 if (t->version >= 14) { 438 PRINT_FILED_DELAY("IRQ", t->version, t, 439 irq_count, irq_delay_total, 440 irq_delay_max, irq_delay_min); 441 } 442 } 443} 444 445static void task_context_switch_counts(struct taskstats *t) 446{ 447 printf("\n\nTask %15s%15s\n" 448 " %15llu%15llu\n", 449 "voluntary", "nonvoluntary", 450 (unsigned long long)t->nvcsw, (unsigned long long)t->nivcsw); 451} 452 453static void print_cgroupstats(struct cgroupstats *c) 454{ 455 printf("sleeping %llu, blocked %llu, running %llu, stopped %llu, " 456 "uninterruptible %llu\n", (unsigned long long)c->nr_sleeping, 457 (unsigned long long)c->nr_io_wait, 458 (unsigned long long)c->nr_running, 459 (unsigned long long)c->nr_stopped, 460 (unsigned long long)c->nr_uninterruptible); 461} 462 463 464static void print_ioacct(struct taskstats *t) 465{ 466 printf("%s: read=%llu, write=%llu, cancelled_write=%llu\n", 467 t->ac_comm, 468 (unsigned long long)t->read_bytes, 469 (unsigned long long)t->write_bytes, 470 (unsigned long long)t->cancelled_write_bytes); 471} 472 473int main(int argc, char *argv[]) 474{ 475 int c, rc, rep_len, aggr_len, len2; 476 int cmd_type = TASKSTATS_CMD_ATTR_UNSPEC; 477 __u16 id; 478 __u32 mypid; 479 480 struct nlattr *na; 481 int nl_sd = -1; 482 int len = 0; 483 pid_t tid = 0; 484 pid_t rtid = 0; 485 486 int fd = 0; 487 int write_file = 0; 488 int maskset = 0; 489 char *logfile = NULL; 490 int loop = 0; 491 int containerset = 0; 492 char *containerpath = NULL; 493 int cfd = 0; 494 int forking = 0; 495 sigset_t sigset; 496 497 struct msgtemplate msg; 498 499 while (!forking) { 500 c = getopt(argc, argv, "qdiw:r:m:t:p:vlC:c:"); 501 if (c < 0) 502 break; 503 504 switch (c) { 505 case 'd': 506 printf("print delayacct stats ON\n"); 507 print_delays = 1; 508 break; 509 case 'i': 510 printf("printing IO accounting\n"); 511 print_io_accounting = 1; 512 break; 513 case 'q': 514 printf("printing task/process context switch rates\n"); 515 print_task_context_switch_counts = 1; 516 break; 517 case 'C': 518 containerset = 1; 519 containerpath = optarg; 520 break; 521 case 'w': 522 logfile = strdup(optarg); 523 printf("write to file %s\n", logfile); 524 write_file = 1; 525 break; 526 case 'r': 527 rcvbufsz = atoi(optarg); 528 printf("receive buf size %d\n", rcvbufsz); 529 if (rcvbufsz < 0) 530 err(1, "Invalid rcv buf size\n"); 531 break; 532 case 'm': 533 strncpy(cpumask, optarg, sizeof(cpumask)); 534 cpumask[sizeof(cpumask) - 1] = '\0'; 535 maskset = 1; 536 printf("cpumask %s maskset %d\n", cpumask, maskset); 537 break; 538 case 't': 539 tid = atoi(optarg); 540 if (!tid) 541 err(1, "Invalid tgid\n"); 542 cmd_type = TASKSTATS_CMD_ATTR_TGID; 543 break; 544 case 'p': 545 tid = atoi(optarg); 546 if (!tid) 547 err(1, "Invalid pid\n"); 548 cmd_type = TASKSTATS_CMD_ATTR_PID; 549 break; 550 case 'c': 551 552 /* Block SIGCHLD for sigwait() later */ 553 if (sigemptyset(&sigset) == -1) 554 err(1, "Failed to empty sigset"); 555 if (sigaddset(&sigset, SIGCHLD)) 556 err(1, "Failed to set sigchld in sigset"); 557 sigprocmask(SIG_BLOCK, &sigset, NULL); 558 559 /* fork/exec a child */ 560 tid = fork(); 561 if (tid < 0) 562 err(1, "Fork failed\n"); 563 if (tid == 0) 564 if (execvp(argv[optind - 1], 565 &argv[optind - 1]) < 0) 566 exit(-1); 567 568 /* Set the command type and avoid further processing */ 569 cmd_type = TASKSTATS_CMD_ATTR_PID; 570 forking = 1; 571 break; 572 case 'v': 573 printf("debug on\n"); 574 dbg = 1; 575 break; 576 case 'l': 577 printf("listen forever\n"); 578 loop = 1; 579 break; 580 default: 581 usage(); 582 exit(-1); 583 } 584 } 585 586 if (write_file) { 587 fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC, 588 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 589 if (fd == -1) { 590 perror("Cannot open output file\n"); 591 exit(1); 592 } 593 } 594 595 nl_sd = create_nl_socket(NETLINK_GENERIC); 596 if (nl_sd < 0) 597 err(1, "error creating Netlink socket\n"); 598 599 600 mypid = getpid(); 601 id = get_family_id(nl_sd); 602 if (!id) { 603 fprintf(stderr, "Error getting family id, errno %d\n", errno); 604 goto err; 605 } 606 PRINTF("family id %d\n", id); 607 608 if (maskset) { 609 rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, 610 TASKSTATS_CMD_ATTR_REGISTER_CPUMASK, 611 &cpumask, strlen(cpumask) + 1); 612 PRINTF("Sent register cpumask, retval %d\n", rc); 613 if (rc < 0) { 614 fprintf(stderr, "error sending register cpumask\n"); 615 goto err; 616 } 617 } 618 619 if (tid && containerset) { 620 fprintf(stderr, "Select either -t or -C, not both\n"); 621 goto err; 622 } 623 624 /* 625 * If we forked a child, wait for it to exit. Cannot use waitpid() 626 * as all the delicious data would be reaped as part of the wait 627 */ 628 if (tid && forking) { 629 int sig_received; 630 sigwait(&sigset, &sig_received); 631 } 632 633 if (tid) { 634 rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, 635 cmd_type, &tid, sizeof(__u32)); 636 PRINTF("Sent pid/tgid, retval %d\n", rc); 637 if (rc < 0) { 638 fprintf(stderr, "error sending tid/tgid cmd\n"); 639 goto done; 640 } 641 } 642 643 if (containerset) { 644 cfd = open(containerpath, O_RDONLY); 645 if (cfd < 0) { 646 perror("error opening container file"); 647 goto err; 648 } 649 rc = send_cmd(nl_sd, id, mypid, CGROUPSTATS_CMD_GET, 650 CGROUPSTATS_CMD_ATTR_FD, &cfd, sizeof(__u32)); 651 if (rc < 0) { 652 perror("error sending cgroupstats command"); 653 goto err; 654 } 655 } 656 if (!maskset && !tid && !containerset) { 657 usage(); 658 goto err; 659 } 660 661 do { 662 rep_len = recv_taskstats_msg(nl_sd, &msg); 663 PRINTF("received %d bytes\n", rep_len); 664 665 if (rep_len < 0) { 666 if (errno == EMSGSIZE) 667 fprintf(stderr, 668 "dropped truncated taskstats netlink message, please increase MAX_MSG_SIZE\n"); 669 else 670 fprintf(stderr, "nonfatal reply error: errno %d\n", 671 errno); 672 continue; 673 } 674 if (msg.n.nlmsg_type == NLMSG_ERROR || 675 !NLMSG_OK((&msg.n), rep_len)) { 676 struct nlmsgerr *err = NLMSG_DATA(&msg); 677 fprintf(stderr, "fatal reply error, errno %d\n", 678 err->error); 679 goto done; 680 } 681 682 PRINTF("nlmsghdr size=%zu, nlmsg_len=%d, rep_len=%d\n", 683 sizeof(struct nlmsghdr), msg.n.nlmsg_len, rep_len); 684 685 686 rep_len = GENLMSG_PAYLOAD(&msg.n); 687 688 na = (struct nlattr *) GENLMSG_DATA(&msg); 689 len = 0; 690 while (len < rep_len) { 691 len += NLA_ALIGN(na->nla_len); 692 switch (na->nla_type) { 693 case TASKSTATS_TYPE_AGGR_TGID: 694 /* Fall through */ 695 case TASKSTATS_TYPE_AGGR_PID: 696 aggr_len = NLA_PAYLOAD(na->nla_len); 697 len2 = 0; 698 /* For nested attributes, na follows */ 699 na = (struct nlattr *) NLA_DATA(na); 700 while (len2 < aggr_len) { 701 switch (na->nla_type) { 702 case TASKSTATS_TYPE_PID: 703 rtid = *(int *) NLA_DATA(na); 704 if (print_delays) 705 printf("PID\t%d\n", rtid); 706 break; 707 case TASKSTATS_TYPE_TGID: 708 rtid = *(int *) NLA_DATA(na); 709 if (print_delays) 710 printf("TGID\t%d\n", rtid); 711 break; 712 case TASKSTATS_TYPE_STATS: 713 PRINTF("version %u\n", 714 ((struct taskstats *) 715 NLA_DATA(na))->version); 716 if (print_delays) 717 print_delayacct((struct taskstats *) NLA_DATA(na)); 718 if (print_io_accounting) 719 print_ioacct((struct taskstats *) NLA_DATA(na)); 720 if (print_task_context_switch_counts) 721 task_context_switch_counts((struct taskstats *) NLA_DATA(na)); 722 if (fd) { 723 if (write(fd, NLA_DATA(na), na->nla_len) < 0) { 724 err(1,"write error\n"); 725 } 726 } 727 if (!loop) 728 goto done; 729 break; 730 case TASKSTATS_TYPE_NULL: 731 break; 732 default: 733 fprintf(stderr, "Unknown nested" 734 " nla_type %d\n", 735 na->nla_type); 736 break; 737 } 738 len2 += NLA_ALIGN(na->nla_len); 739 na = (struct nlattr *)((char *)na + 740 NLA_ALIGN(na->nla_len)); 741 } 742 break; 743 744 case CGROUPSTATS_TYPE_CGROUP_STATS: 745 print_cgroupstats(NLA_DATA(na)); 746 break; 747 default: 748 fprintf(stderr, "Unknown nla_type %d\n", 749 na->nla_type); 750 case TASKSTATS_TYPE_NULL: 751 break; 752 } 753 na = (struct nlattr *) (GENLMSG_DATA(&msg) + len); 754 } 755 } while (loop); 756done: 757 if (maskset) { 758 rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, 759 TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, 760 &cpumask, strlen(cpumask) + 1); 761 printf("Sent deregister mask, retval %d\n", rc); 762 if (rc < 0) 763 err(rc, "error sending deregister cpumask\n"); 764 } 765err: 766 close(nl_sd); 767 if (fd) 768 close(fd); 769 if (cfd) 770 close(cfd); 771 return 0; 772}