1 /* 2 * Copyright (c) 1997, 1998 Kenneth D. Merry. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD: src/usr.sbin/iostat/iostat.c,v 1.17.2.2 2001/07/19 04:15:42 kris Exp $ 29 * $DragonFly: src/usr.sbin/iostat/iostat.c,v 1.7 2005/09/01 19:08:38 swildner Exp $ 30 */ 31 /* 32 * Parts of this program are derived from the original FreeBSD iostat 33 * program: 34 */ 35 /*- 36 * Copyright (c) 1986, 1991, 1993 37 * The Regents of the University of California. All rights reserved. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 3. Neither the name of the University nor the names of its contributors 48 * may be used to endorse or promote products derived from this software 49 * without specific prior written permission. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 * SUCH DAMAGE. 62 */ 63 /* 64 * Ideas for the new iostat statistics output modes taken from the NetBSD 65 * version of iostat: 66 */ 67 /* 68 * Copyright (c) 1996 John M. Vinopal 69 * All rights reserved. 70 * 71 * Redistribution and use in source and binary forms, with or without 72 * modification, are permitted provided that the following conditions 73 * are met: 74 * 1. Redistributions of source code must retain the above copyright 75 * notice, this list of conditions and the following disclaimer. 76 * 2. Redistributions in binary form must reproduce the above copyright 77 * notice, this list of conditions and the following disclaimer in the 78 * documentation and/or other materials provided with the distribution. 79 * 3. All advertising materials mentioning features or use of this software 80 * must display the following acknowledgement: 81 * This product includes software developed for the NetBSD Project 82 * by John M. Vinopal. 83 * 4. The name of the author may not be used to endorse or promote products 84 * derived from this software without specific prior written permission. 85 * 86 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 87 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 88 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 89 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 90 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 91 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 92 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 93 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 94 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 95 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 96 * SUCH DAMAGE. 97 */ 98 99 #include <sys/user.h> 100 #include <sys/param.h> 101 #include <sys/errno.h> 102 103 #include <err.h> 104 #include <ctype.h> 105 #include <fcntl.h> 106 #include <kinfo.h> 107 #include <stdio.h> 108 #include <stdlib.h> 109 #include <string.h> 110 #include <unistd.h> 111 #include <limits.h> 112 #include <devstat.h> 113 114 struct statinfo cur, last; 115 uint64_t tk_nin, old_tk_nin, diff_tk_nin; 116 uint64_t tk_nout, old_tk_nout, diff_tk_nout; 117 struct kinfo_cputime cp_time, old_cp_time, diff_cp_time; 118 double cp_time_total; 119 int num_devices; 120 struct device_selection *dev_select; 121 int maxshowdevs; 122 int dflag = 0, Dflag=0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0; 123 124 /* local function declarations */ 125 static void usage(void); 126 static void phdr(int signo); 127 static void devstats(int perf_select); 128 static void cpustats(void); 129 130 static void 131 usage(void) 132 { 133 /* 134 * We also support the following 'traditional' syntax: 135 * iostat [drives] [wait [count]] 136 * This isn't mentioned in the man page, or the usage statement, 137 * but it is supported. 138 */ 139 fprintf(stderr, "usage: iostat [-CdhIKoT] [-c count]" 140 " [-n devs]\n" 141 "\t [-t type,if,pass] [-w wait] [drives]\n"); 142 } 143 144 int 145 main(int argc, char **argv) 146 { 147 int c; 148 int tflag = 0, hflag = 0, cflag = 0, wflag = 0, nflag = 0; 149 int count = 0, waittime = 0; 150 struct devstat_match *matches; 151 int num_matches = 0; 152 int hz; 153 int headercount; 154 long generation; 155 int num_devices_specified; 156 int num_selected, num_selections; 157 long select_generation; 158 char **specified_devices; 159 devstat_select_mode select_mode; 160 161 matches = NULL; 162 maxshowdevs = 3; 163 164 while ((c = getopt(argc, argv, "c:CdDhIKM:n:N:ot:Tw:")) != -1) { 165 switch(c) { 166 case 'c': 167 cflag++; 168 count = atoi(optarg); 169 if (count < 1) 170 errx(1, "count %d is < 1", count); 171 break; 172 case 'C': 173 Cflag++; 174 break; 175 case 'd': 176 dflag++; 177 break; 178 case 'D': 179 Dflag++; 180 break; 181 case 'h': 182 hflag++; 183 break; 184 case 'I': 185 Iflag++; 186 break; 187 case 'K': 188 Kflag++; 189 break; 190 case 'n': 191 nflag++; 192 maxshowdevs = atoi(optarg); 193 if (maxshowdevs < 0) 194 errx(1, "number of devices %d is < 0", 195 maxshowdevs); 196 break; 197 case 'o': 198 oflag++; 199 break; 200 case 't': 201 tflag++; 202 if (buildmatch(optarg, &matches, 203 &num_matches) != 0) 204 errx(1, "%s", devstat_errbuf); 205 break; 206 case 'T': 207 Tflag++; 208 break; 209 case 'w': 210 wflag++; 211 waittime = atoi(optarg); 212 if (waittime < 1) 213 errx(1, "wait time is < 1"); 214 break; 215 default: 216 usage(); 217 exit(1); 218 break; 219 } 220 } 221 222 argc -= optind; 223 argv += optind; 224 225 /* 226 * Make sure that the userland devstat version matches the kernel 227 * devstat version. If not, exit and print a message informing 228 * the user of his mistake. 229 */ 230 if (checkversion() < 0) 231 errx(1, "%s", devstat_errbuf); 232 233 /* 234 * Figure out how many devices we should display. 235 */ 236 if (nflag == 0) { 237 if (oflag > 0) { 238 if ((dflag > 0) && (Cflag == 0) && (Tflag == 0)) 239 maxshowdevs = 5; 240 else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0)) 241 maxshowdevs = 5; 242 else 243 maxshowdevs = 4; 244 } else { 245 if ((dflag > 0) && (Cflag == 0)) 246 maxshowdevs = 4; 247 else 248 maxshowdevs = 3; 249 } 250 } 251 252 /* find out how many devices we have */ 253 if ((num_devices = getnumdevs()) < 0) 254 err(1, "can't get number of devices"); 255 256 if ((cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo))) == 257 NULL) 258 err(1, "devinfo malloc failed"); 259 if ((last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo))) == 260 NULL) 261 err(1, "devinfo malloc failed"); 262 bzero(cur.dinfo, sizeof(struct devinfo)); 263 bzero(last.dinfo, sizeof(struct devinfo)); 264 265 /* 266 * Grab all the devices. We don't look to see if the list has 267 * changed here, since it almost certainly has. We only look for 268 * errors. 269 */ 270 if (getdevs(&cur) == -1) 271 errx(1, "%s", devstat_errbuf); 272 273 num_devices = cur.dinfo->numdevs; 274 generation = cur.dinfo->generation; 275 276 /* 277 * If the user specified any devices on the command line, see if 278 * they are in the list of devices we have now. 279 */ 280 if ((specified_devices = (char **)malloc(sizeof(char *))) == NULL) 281 err(1, "specified_devices malloc failed"); 282 for (num_devices_specified = 0; *argv; ++argv) { 283 if (isdigit(**argv)) 284 break; 285 num_devices_specified++; 286 specified_devices = (char **)realloc(specified_devices, 287 sizeof(char *) * 288 num_devices_specified); 289 specified_devices[num_devices_specified - 1] = *argv; 290 291 } 292 if (nflag == 0 && maxshowdevs < num_devices_specified) 293 maxshowdevs = num_devices_specified; 294 295 dev_select = NULL; 296 297 if ((num_devices_specified == 0) && (num_matches == 0)) 298 select_mode = DS_SELECT_ADD; 299 else 300 select_mode = DS_SELECT_ONLY; 301 302 /* 303 * At this point, selectdevs will almost surely indicate that the 304 * device list has changed, so we don't look for return values of 0 305 * or 1. If we get back -1, though, there is an error. 306 */ 307 if (selectdevs(&dev_select, &num_selected, 308 &num_selections, &select_generation, 309 generation, cur.dinfo->devices, num_devices, 310 matches, num_matches, 311 specified_devices, num_devices_specified, 312 select_mode, maxshowdevs, hflag) == -1) 313 errx(1, "%s", devstat_errbuf); 314 315 /* 316 * Look for the traditional wait time and count arguments. 317 */ 318 if (*argv) { 319 waittime = atoi(*argv); 320 321 /* Let the user know he goofed, but keep going anyway */ 322 if (wflag != 0) 323 warnx("discarding previous wait interval, using" 324 " %d instead", waittime); 325 wflag++; 326 327 if (*++argv) { 328 count = atoi(*argv); 329 if (cflag != 0) 330 warnx("discarding previous count, using %d" 331 " instead", count); 332 cflag++; 333 } else 334 count = -1; 335 } 336 337 /* 338 * If the user specified a count, but not an interval, we default 339 * to an interval of 1 second. 340 */ 341 if ((wflag == 0) && (cflag > 0)) 342 waittime = 1; 343 344 /* 345 * If the user specified a wait time, but not a count, we want to 346 * go on ad infinitum. This can be redundant if the user uses the 347 * traditional method of specifying the wait, since in that case we 348 * already set count = -1 above. Oh well. 349 */ 350 if ((wflag > 0) && (cflag == 0)) 351 count = -1; 352 353 if (kinfo_get_sched_hz(&hz)) 354 err(1, "kinfo_get_sched_hz"); 355 if (kinfo_get_sched_stathz(&hz)) 356 err(1, "kinfo_get_sched_stathz"); 357 358 /* 359 * If the user stops the program (control-Z) and then resumes it, 360 * print out the header again. 361 */ 362 signal(SIGCONT, phdr); 363 364 for (headercount = 1;;) { 365 struct devinfo *tmp_dinfo; 366 367 if (!--headercount) { 368 phdr(0); 369 headercount = 20; 370 } 371 if (kinfo_get_tty_tk_nin(&tk_nin)) 372 err(1, "kinfo_get_tty_tk_nin"); 373 if (kinfo_get_tty_tk_nout(&tk_nout)) 374 err(1, "kinfo_get_tty_tk_nout"); 375 if (kinfo_get_sched_cputime(&cp_time)) 376 err(1, "kinfo_get_sched_cputime"); 377 378 tmp_dinfo = last.dinfo; 379 last.dinfo = cur.dinfo; 380 cur.dinfo = tmp_dinfo; 381 382 last.busy_time = cur.busy_time; 383 384 /* 385 * Here what we want to do is refresh our device stats. 386 * getdevs() returns 1 when the device list has changed. 387 * If the device list has changed, we want to go through 388 * the selection process again, in case a device that we 389 * were previously displaying has gone away. 390 */ 391 switch (getdevs(&cur)) { 392 case -1: 393 errx(1, "%s", devstat_errbuf); 394 break; 395 case 1: { 396 int retval; 397 398 num_devices = cur.dinfo->numdevs; 399 generation = cur.dinfo->generation; 400 retval = selectdevs(&dev_select, &num_selected, 401 &num_selections, &select_generation, 402 generation, cur.dinfo->devices, 403 num_devices, matches, num_matches, 404 specified_devices, 405 num_devices_specified, 406 select_mode, maxshowdevs, hflag); 407 switch(retval) { 408 case -1: 409 errx(1, "%s", devstat_errbuf); 410 break; 411 case 1: 412 phdr(0); 413 headercount = 20; 414 break; 415 default: 416 break; 417 } 418 break; 419 } 420 default: 421 break; 422 } 423 424 /* 425 * We only want to re-select devices if we're in 'top' 426 * mode. This is the only mode where the devices selected 427 * could actually change. 428 */ 429 if (hflag > 0) { 430 int retval; 431 retval = selectdevs(&dev_select, &num_selected, 432 &num_selections, &select_generation, 433 generation, cur.dinfo->devices, 434 num_devices, matches, num_matches, 435 specified_devices, 436 num_devices_specified, 437 select_mode, maxshowdevs, hflag); 438 switch(retval) { 439 case -1: 440 errx(1,"%s", devstat_errbuf); 441 break; 442 case 1: 443 phdr(0); 444 headercount = 20; 445 break; 446 default: 447 break; 448 } 449 } 450 451 diff_tk_nin = tk_nin - old_tk_nin; 452 old_tk_nin = tk_nin; 453 diff_tk_nout = tk_nout - old_tk_nout; 454 old_tk_nout = tk_nout; 455 456 diff_cp_time.cp_user = cp_time.cp_user - old_cp_time.cp_user; 457 diff_cp_time.cp_nice = cp_time.cp_nice - old_cp_time.cp_nice; 458 diff_cp_time.cp_sys = cp_time.cp_sys - old_cp_time.cp_sys; 459 diff_cp_time.cp_intr = cp_time.cp_intr - old_cp_time.cp_intr; 460 diff_cp_time.cp_idle = cp_time.cp_idle - old_cp_time.cp_idle; 461 cp_time_total = diff_cp_time.cp_user + diff_cp_time.cp_nice + 462 diff_cp_time.cp_sys + diff_cp_time.cp_intr + 463 diff_cp_time.cp_idle; 464 old_cp_time = cp_time; 465 466 if (cp_time_total == 0.0) 467 cp_time_total = 1.0; 468 469 if ((dflag == 0) || (Tflag > 0)) 470 printf("%4.0f%5.0f", diff_tk_nin / cp_time_total * 1e6, 471 diff_tk_nout / cp_time_total * 1e6); 472 devstats(hflag); 473 if ((dflag == 0) || (Cflag > 0)) 474 cpustats(); 475 printf("\n"); 476 fflush(stdout); 477 478 if (count >= 0 && --count <= 0) 479 break; 480 481 sleep(waittime); 482 } 483 484 exit(0); 485 } 486 487 static void 488 phdr(__unused int signo) 489 { 490 int i; 491 int printed; 492 493 if ((dflag == 0) || (Tflag > 0)) 494 printf(" tty"); 495 for (i = 0, printed=0;(i < num_devices) && (printed < maxshowdevs);i++){ 496 int di; 497 if ((dev_select[i].selected != 0) 498 && (dev_select[i].selected <= maxshowdevs)) { 499 di = dev_select[i].position; 500 if (oflag > 0) 501 printf("%12.6s%d ", 502 cur.dinfo->devices[di].device_name, 503 cur.dinfo->devices[di].unit_number); 504 else 505 if (Dflag > 0) 506 printf("%19.6s%d ", 507 cur.dinfo->devices[di].device_name, 508 cur.dinfo->devices[di].unit_number); 509 else 510 printf("%15.6s%d ", 511 cur.dinfo->devices[di].device_name, 512 cur.dinfo->devices[di].unit_number); 513 printed++; 514 } 515 } 516 if ((dflag == 0) || (Cflag > 0)) 517 printf(" cpu\n"); 518 else 519 printf("\n"); 520 521 if ((dflag == 0) || (Tflag > 0)) 522 printf(" tin tout"); 523 524 for (i=0, printed = 0;(i < num_devices) && (printed < maxshowdevs);i++){ 525 if ((dev_select[i].selected != 0) 526 && (dev_select[i].selected <= maxshowdevs)) { 527 if (oflag > 0) { 528 if (Iflag == 0) 529 printf(" sps tps msps "); 530 else 531 printf(" blk xfr msps "); 532 } else { 533 if (Iflag == 0) { 534 if (Dflag > 0) 535 printf(" KB/t rtps MBr/s wtps MBw/s "); 536 else 537 printf(" KB/t tps MB/s "); 538 } 539 else 540 printf(" KB/t xfrs MB "); 541 } 542 printed++; 543 } 544 } 545 if ((dflag == 0) || (Cflag > 0)) 546 printf(" us ni sy in id\n"); 547 else 548 printf("\n"); 549 550 } 551 552 static void 553 devstats(int perf_select) 554 { 555 int dn; 556 long double kb_per_transfer; 557 long double transfers_per_second; 558 long double transfers_per_secondr, transfers_per_secondw; 559 long double mb_per_second; 560 long double mb_per_secondr, mb_per_secondw; 561 u_int64_t total_bytes, total_transfers, total_blocks; 562 long double busy_seconds; 563 long double total_mb; 564 long double blocks_per_second, ms_per_transaction; 565 566 /* 567 * Calculate elapsed time up front, since it's the same for all 568 * devices. 569 */ 570 busy_seconds = compute_etime(cur.busy_time, last.busy_time); 571 572 for (dn = 0; dn < num_devices; dn++) { 573 int di; 574 575 if (((perf_select == 0) && (dev_select[dn].selected == 0)) 576 || (dev_select[dn].selected > maxshowdevs)) 577 continue; 578 579 di = dev_select[dn].position; 580 581 if (compute_stats(&cur.dinfo->devices[di], 582 &last.dinfo->devices[di], busy_seconds, 583 &total_bytes, &total_transfers, 584 &total_blocks, &kb_per_transfer, 585 &transfers_per_second, &mb_per_second, 586 &blocks_per_second, &ms_per_transaction)!= 0) 587 errx(1, "%s", devstat_errbuf); 588 if (compute_stats_read(&cur.dinfo->devices[di], 589 &last.dinfo->devices[di], busy_seconds, 590 NULL, NULL, 591 NULL, NULL, 592 &transfers_per_secondr, &mb_per_secondr, 593 NULL, NULL)!= 0) 594 errx(1, "%s", devstat_errbuf); 595 if (compute_stats_write(&cur.dinfo->devices[di], 596 &last.dinfo->devices[di], busy_seconds, 597 NULL, NULL, 598 NULL, NULL, 599 &transfers_per_secondw, &mb_per_secondw, 600 NULL, NULL)!= 0) 601 errx(1, "%s", devstat_errbuf); 602 603 if (perf_select != 0) { 604 dev_select[dn].bytes = total_bytes; 605 if ((dev_select[dn].selected == 0) 606 || (dev_select[dn].selected > maxshowdevs)) 607 continue; 608 } 609 610 if (Kflag) { 611 int block_size = cur.dinfo->devices[di].block_size; 612 total_blocks = total_blocks * (block_size ? 613 block_size : 512) / 1024; 614 } 615 616 if (oflag > 0) { 617 int msdig = (ms_per_transaction < 100.0) ? 1 : 0; 618 619 if (Iflag == 0) 620 printf("%4.0Lf%4.0Lf%5.*Lf ", 621 blocks_per_second, 622 transfers_per_second, 623 msdig, 624 ms_per_transaction); 625 else 626 printf("%4.1ju%4.1ju%5.*Lf ", 627 (uintmax_t)total_blocks, 628 (uintmax_t)total_transfers, 629 msdig, 630 ms_per_transaction); 631 } else { 632 if (Iflag == 0) 633 if (Dflag > 0) { 634 printf(" %5.2Lf %4.0Lf %6.2Lf %4.0Lf %6.2Lf ", 635 kb_per_transfer, 636 transfers_per_secondr, 637 mb_per_secondr, 638 transfers_per_secondw, 639 mb_per_secondw); 640 } else { 641 printf(" %5.2Lf %4.0Lf %5.2Lf ", 642 kb_per_transfer, 643 transfers_per_second, 644 mb_per_second); 645 } 646 else { 647 total_mb = total_bytes; 648 total_mb /= 1024 * 1024; 649 650 printf(" %5.2Lf %3.1ju %5.2Lf ", 651 kb_per_transfer, 652 (uintmax_t)total_transfers, 653 total_mb); 654 } 655 } 656 } 657 } 658 659 static void 660 cpustats(void) 661 { 662 if (cp_time_total == 0.0) 663 cp_time_total = 1.0; 664 665 printf(" %2.0f", 100. * diff_cp_time.cp_user / cp_time_total); 666 printf(" %2.0f", 100. * diff_cp_time.cp_nice / cp_time_total); 667 printf(" %2.0f", 100. * diff_cp_time.cp_sys / cp_time_total); 668 printf(" %2.0f", 100. * diff_cp_time.cp_intr / cp_time_total); 669 printf(" %2.0f", 100. * diff_cp_time.cp_idle / cp_time_total); 670 } 671