1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/processor.h> 28 #include <sys/pset.h> 29 #include <sys/lwp.h> 30 #include <sys/priocntl.h> 31 #include <sys/fxpriocntl.h> 32 #include <time.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <inttypes.h> 36 #include <unistd.h> 37 #include <limits.h> 38 #include <string.h> 39 #include <strings.h> 40 #include <thread.h> 41 #include <errno.h> 42 #include <libintl.h> 43 #include <locale.h> 44 #include <kstat.h> 45 #include <synch.h> 46 #include <libcpc.h> 47 #include <sys/resource.h> 48 49 #include "cpucmds.h" 50 #include "statcommon.h" 51 52 static struct options { 53 int debug; 54 int dotitle; 55 int dohelp; 56 int dotick; 57 int dosoaker; 58 int doperiod; 59 char *pgmname; 60 uint_t mseconds; 61 uint_t nsamples; 62 uint_t nsets; 63 uint_t mseconds_rest; 64 cpc_setgrp_t *master; 65 } __options; 66 67 /* 68 * States for soaker threads. 69 */ 70 #define SOAK_PAUSE 0 71 #define SOAK_RUN 1 72 73 struct tstate { 74 processorid_t cpuid; 75 int chip_id; 76 cpc_setgrp_t *sgrp; 77 int status; 78 thread_t tid; 79 int soak_state; 80 mutex_t soak_lock; 81 cond_t soak_cv; 82 }; 83 84 static const struct options *opts = (const struct options *)&__options; 85 86 static cpc_t *cpc; 87 88 struct tstate *gstate; 89 static int ncpus; 90 static int max_chip_id; 91 static int *chip_designees; /* cpuid of CPU which counts for phs chip */ 92 static int smt = 0; /* If set, cpustat needs to be SMT-aware. */ 93 static pcinfo_t fxinfo = { 0, "FX", NULL }; /* FX scheduler class info */ 94 95 static uint_t timestamp_fmt = NODATE; 96 97 /*ARGSUSED*/ 98 static void 99 cpustat_errfn(const char *fn, int subcode, const char *fmt, va_list ap) 100 { 101 (void) fprintf(stderr, "%s: ", opts->pgmname); 102 if (opts->debug) 103 (void) fprintf(stderr, "%s: ", fn); 104 (void) vfprintf(stderr, fmt, ap); 105 } 106 107 static int cpustat(void); 108 static int get_chipid(kstat_ctl_t *kc, processorid_t cpuid); 109 static void *soaker(void *arg); 110 111 112 #if !defined(TEXT_DOMAIN) 113 #define TEXT_DOMAIN "SYS_TEST" 114 #endif 115 116 int 117 main(int argc, char *argv[]) 118 { 119 struct options *opts = &__options; 120 int c, errcnt = 0, ret; 121 cpc_setgrp_t *sgrp; 122 char *errstr; 123 double period; 124 char *endp; 125 struct rlimit rl; 126 127 (void) setlocale(LC_ALL, ""); 128 (void) textdomain(TEXT_DOMAIN); 129 130 if ((opts->pgmname = strrchr(argv[0], '/')) == NULL) 131 opts->pgmname = argv[0]; 132 else 133 opts->pgmname++; 134 135 /* Make sure we can open enough files */ 136 rl.rlim_max = rl.rlim_cur = RLIM_INFINITY; 137 if (setrlimit(RLIMIT_NOFILE, &rl) != 0) { 138 errstr = strerror(errno); 139 (void) fprintf(stderr, 140 gettext("%s: setrlimit failed - %s\n"), 141 opts->pgmname, errstr); 142 } 143 144 if ((cpc = cpc_open(CPC_VER_CURRENT)) == NULL) { 145 errstr = strerror(errno); 146 (void) fprintf(stderr, gettext("%s: cannot access performance " 147 "counters - %s\n"), opts->pgmname, errstr); 148 return (1); 149 } 150 151 (void) cpc_seterrhndlr(cpc, cpustat_errfn); 152 strtoset_errfn = cpustat_errfn; 153 154 /* 155 * Check to see if cpustat needs to be SMT-aware. 156 */ 157 smt = smt_limited_cpc_hw(cpc); 158 159 /* 160 * Establish some defaults 161 */ 162 opts->mseconds = 5000; 163 opts->nsamples = UINT_MAX; 164 opts->dotitle = 1; 165 if ((opts->master = cpc_setgrp_new(cpc, smt)) == NULL) { 166 (void) fprintf(stderr, gettext("%s: out of heap\n"), 167 opts->pgmname); 168 return (1); 169 } 170 171 while ((c = getopt(argc, argv, "Dc:hntT:sp:")) != EOF && errcnt == 0) 172 switch (c) { 173 case 'D': /* enable debugging */ 174 opts->debug++; 175 break; 176 case 'c': /* specify statistics */ 177 if ((sgrp = cpc_setgrp_newset(opts->master, 178 optarg, &errcnt)) != NULL) 179 opts->master = sgrp; 180 break; 181 case 'n': /* no titles */ 182 opts->dotitle = 0; 183 break; 184 case 'p': /* periodic behavior */ 185 opts->doperiod = 1; 186 period = strtod(optarg, &endp); 187 if (*endp != '\0') { 188 (void) fprintf(stderr, gettext("%s: invalid " 189 "parameter \"%s\"\n"), opts->pgmname, 190 optarg); 191 errcnt++; 192 } 193 break; 194 case 's': /* run soaker thread */ 195 opts->dosoaker = 1; 196 break; 197 case 't': /* print %tick */ 198 opts->dotick = 1; 199 break; 200 case 'T': 201 if (optarg) { 202 if (*optarg == 'u') 203 timestamp_fmt = UDATE; 204 else if (*optarg == 'd') 205 timestamp_fmt = DDATE; 206 else 207 errcnt++; 208 } else { 209 errcnt++; 210 } 211 break; 212 case 'h': /* help */ 213 opts->dohelp = 1; 214 break; 215 case '?': 216 default: 217 errcnt++; 218 break; 219 } 220 221 switch (argc - optind) { 222 case 0: 223 break; 224 case 2: 225 opts->nsamples = strtol(argv[optind + 1], &endp, 10); 226 if (*endp != '\0') { 227 (void) fprintf(stderr, 228 gettext("%s: invalid argument \"%s\"\n"), 229 opts->pgmname, argv[optind + 1]); 230 errcnt++; 231 break; 232 } 233 /*FALLTHROUGH*/ 234 case 1: 235 opts->mseconds = (uint_t)(strtod(argv[optind], &endp) * 1000.0); 236 if (*endp != '\0') { 237 (void) fprintf(stderr, 238 gettext("%s: invalid argument \"%s\"\n"), 239 opts->pgmname, argv[optind]); 240 errcnt++; 241 } 242 break; 243 default: 244 errcnt++; 245 break; 246 } 247 248 if (opts->nsamples == 0 || opts->mseconds == 0) 249 errcnt++; 250 251 if (errcnt != 0 || opts->dohelp || 252 (opts->nsets = cpc_setgrp_numsets(opts->master)) == 0) { 253 (void) fprintf(opts->dohelp ? stdout : stderr, gettext( 254 "Usage:\n\t%s [-c events] [-p period] [-nstD] " 255 "[-T d|u] [interval [count]]\n\n" 256 "\t-c events specify processor events to be monitored\n" 257 "\t-n\t suppress titles\n" 258 "\t-p period cycle through event list periodically\n" 259 "\t-s\t run user soaker thread for system-only events\n" 260 "\t-t\t include %s register\n" 261 "\t-T d|u\t Display a timestamp in date (d) or unix " 262 "time_t (u)\n" 263 "\t-D\t enable debug mode\n" 264 "\t-h\t print extended usage information\n\n" 265 "\tUse cputrack(1) to monitor per-process statistics.\n"), 266 opts->pgmname, CPC_TICKREG_NAME); 267 if (opts->dohelp) { 268 (void) putchar('\n'); 269 (void) capabilities(cpc, stdout); 270 exit(0); 271 } 272 exit(2); 273 } 274 275 /* 276 * If the user requested periodic behavior, calculate the rest time 277 * between cycles. 278 */ 279 if (opts->doperiod) { 280 opts->mseconds_rest = (uint_t)((period * 1000.0) - 281 (opts->mseconds * opts->nsets)); 282 if ((int)opts->mseconds_rest < 0) 283 opts->mseconds_rest = 0; 284 if (opts->nsamples != UINT_MAX) 285 opts->nsamples *= opts->nsets; 286 } 287 288 cpc_setgrp_reset(opts->master); 289 (void) setvbuf(stdout, NULL, _IOLBF, 0); 290 291 /* 292 * If no system-mode only sets were created, no soaker threads will be 293 * needed. 294 */ 295 if (opts->dosoaker == 1 && cpc_setgrp_has_sysonly(opts->master) == 0) 296 opts->dosoaker = 0; 297 298 ret = cpustat(); 299 300 (void) cpc_close(cpc); 301 302 return (ret); 303 } 304 305 static void 306 print_title(cpc_setgrp_t *sgrp) 307 { 308 (void) printf("%7s %3s %5s ", "time", "cpu", "event"); 309 if (opts->dotick) 310 (void) printf("%9s ", CPC_TICKREG_NAME); 311 (void) printf("%s\n", cpc_setgrp_gethdr(sgrp)); 312 } 313 314 static void 315 print_sample(processorid_t cpuid, cpc_buf_t *buf, int nreq, const char *setname, 316 int sibling) 317 { 318 char line[1024]; 319 int ccnt; 320 int i; 321 uint64_t val; 322 uint64_t tick; 323 hrtime_t hrtime; 324 325 hrtime = cpc_buf_hrtime(cpc, buf); 326 tick = cpc_buf_tick(cpc, buf); 327 328 ccnt = snprintf(line, sizeof (line), "%7.3f %3d %5s ", 329 mstimestamp(hrtime), (int)cpuid, "tick"); 330 if (opts->dotick) 331 ccnt += snprintf(line + ccnt, sizeof (line) - ccnt, 332 "%9" PRId64 " ", tick); 333 for (i = 0; i < nreq; i++) { 334 (void) cpc_buf_get(cpc, buf, i, &val); 335 ccnt += snprintf(line + ccnt, sizeof (line) - ccnt, 336 "%9" PRId64 " ", val); 337 } 338 if (opts->nsets > 1) 339 ccnt += snprintf(line + ccnt, sizeof (line) - ccnt, 340 " # %s\n", setname); 341 else 342 ccnt += snprintf(line + ccnt, sizeof (line) - ccnt, "\n"); 343 344 if (sibling) { 345 /* 346 * This sample is being printed for a "sibling" CPU -- that is, 347 * a CPU which does not have its own CPC set bound. It is being 348 * measured via a set bound to another CPU sharing its physical 349 * processor. 350 */ 351 int designee = chip_designees[gstate[cpuid].chip_id]; 352 char *p; 353 354 if ((p = strrchr(line, '#')) == NULL) 355 p = strrchr(line, '\n'); 356 357 if (p != NULL) { 358 *p = '\0'; 359 ccnt = strlen(line); 360 ccnt += snprintf(line + ccnt, sizeof (line) - ccnt, 361 "# counter shared with CPU %d\n", designee); 362 } 363 } 364 365 if (timestamp_fmt != NODATE) 366 print_timestamp(timestamp_fmt); 367 if (ccnt > sizeof (line)) 368 ccnt = sizeof (line); 369 if (ccnt > 0) 370 (void) write(1, line, ccnt); 371 372 /* 373 * If this CPU is the chip designee for any other CPUs, print a line for 374 * them here. 375 */ 376 if (smt && (sibling == 0)) { 377 for (i = 0; i < ncpus; i++) { 378 if ((i != cpuid) && (gstate[i].cpuid != -1) && 379 (chip_designees[gstate[i].chip_id] == cpuid)) 380 print_sample(i, buf, nreq, setname, 1); 381 } 382 } 383 } 384 385 static void 386 print_total(int ncpus, cpc_buf_t *buf, int nreq, const char *setname) 387 { 388 int i; 389 uint64_t val; 390 391 (void) printf("%7.3f %3d %5s ", mstimestamp(cpc_buf_hrtime(cpc, buf)), 392 ncpus, "total"); 393 if (opts->dotick) 394 (void) printf("%9" PRId64 " ", cpc_buf_tick(cpc, buf)); 395 for (i = 0; i < nreq; i++) { 396 (void) cpc_buf_get(cpc, buf, i, &val); 397 (void) printf("%9" PRId64 " ", val); 398 } 399 if (opts->nsets > 1) 400 (void) printf(" # %s", setname); 401 (void) fputc('\n', stdout); 402 } 403 404 #define NSECS_PER_MSEC 1000000ll 405 #define NSECS_PER_SEC 1000000000ll 406 407 static void * 408 gtick(void *arg) 409 { 410 struct tstate *state = arg; 411 char *errstr; 412 uint_t nsamples; 413 uint_t sample_cnt = 1; 414 hrtime_t ht, htdelta, restdelta; 415 cpc_setgrp_t *sgrp = state->sgrp; 416 cpc_set_t *this = cpc_setgrp_getset(sgrp); 417 const char *name = cpc_setgrp_getname(sgrp); 418 cpc_buf_t **data1, **data2, **scratch; 419 cpc_buf_t *tmp; 420 int nreqs; 421 thread_t tid; 422 423 htdelta = NSECS_PER_MSEC * opts->mseconds; 424 restdelta = NSECS_PER_MSEC * opts->mseconds_rest; 425 ht = gethrtime(); 426 427 /* 428 * If this CPU is SMT, we run one gtick() thread per _physical_ CPU, 429 * instead of per cpu_t. The following check returns if it detects that 430 * this cpu_t has not been designated to do the counting for this 431 * physical CPU. 432 */ 433 if (smt && chip_designees[state->chip_id] != state->cpuid) 434 return (NULL); 435 436 /* 437 * If we need to run a soaker thread on this CPU, start it here. 438 */ 439 if (opts->dosoaker) { 440 if (cond_init(&state->soak_cv, USYNC_THREAD, NULL) != 0) 441 goto bad; 442 if (mutex_init(&state->soak_lock, USYNC_THREAD, 443 NULL) != 0) 444 goto bad; 445 (void) mutex_lock(&state->soak_lock); 446 state->soak_state = SOAK_PAUSE; 447 if (thr_create(NULL, 0, soaker, state, NULL, &tid) != 0) 448 goto bad; 449 450 while (state->soak_state == SOAK_PAUSE) 451 (void) cond_wait(&state->soak_cv, 452 &state->soak_lock); 453 (void) mutex_unlock(&state->soak_lock); 454 455 /* 456 * If the soaker needs to pause for the first set, stop it now. 457 */ 458 if (cpc_setgrp_sysonly(sgrp) == 0) { 459 (void) mutex_lock(&state->soak_lock); 460 state->soak_state = SOAK_PAUSE; 461 (void) mutex_unlock(&state->soak_lock); 462 } 463 } 464 if (cpc_bind_cpu(cpc, state->cpuid, this, 0) == -1) 465 goto bad; 466 467 for (nsamples = opts->nsamples; nsamples; nsamples--, sample_cnt++) { 468 hrtime_t htnow; 469 struct timespec ts; 470 471 nreqs = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch); 472 473 ht += htdelta; 474 htnow = gethrtime(); 475 if (ht <= htnow) 476 continue; 477 ts.tv_sec = (time_t)((ht - htnow) / NSECS_PER_SEC); 478 ts.tv_nsec = (suseconds_t)((ht - htnow) % NSECS_PER_SEC); 479 480 (void) nanosleep(&ts, NULL); 481 482 if (opts->nsets == 1) { 483 /* 484 * If we're dealing with one set, buffer usage is: 485 * 486 * data1 = most recent data snapshot 487 * data2 = previous data snapshot 488 * scratch = used for diffing data1 and data2 489 * 490 * Save the snapshot from the previous sample in data2 491 * before putting the current sample in data1. 492 */ 493 tmp = *data1; 494 *data1 = *data2; 495 *data2 = tmp; 496 if (cpc_set_sample(cpc, this, *data1) != 0) 497 goto bad; 498 cpc_buf_sub(cpc, *scratch, *data1, *data2); 499 500 print_sample(state->cpuid, *scratch, nreqs, name, 0); 501 } else { 502 /* 503 * More than one set is in use (multiple -c options 504 * given). Buffer usage in this case is: 505 * 506 * data1 = total counts for this set since program began 507 * data2 = unused 508 * scratch = most recent data snapshot 509 */ 510 name = cpc_setgrp_getname(sgrp); 511 nreqs = cpc_setgrp_getbufs(sgrp, &data1, &data2, 512 &scratch); 513 514 if (cpc_set_sample(cpc, this, *scratch) != 0) 515 goto bad; 516 517 cpc_buf_add(cpc, *data1, *data1, *scratch); 518 519 if (cpc_unbind(cpc, this) != 0) 520 (void) fprintf(stderr, gettext("%s: error " 521 "unbinding on cpu %d - %s\n"), 522 opts->pgmname, state->cpuid, 523 strerror(errno)); 524 525 this = cpc_setgrp_nextset(sgrp); 526 527 print_sample(state->cpuid, *scratch, nreqs, name, 0); 528 529 /* 530 * If periodic behavior was requested, rest here. 531 */ 532 if (opts->doperiod && opts->mseconds_rest > 0 && 533 (sample_cnt % opts->nsets) == 0) { 534 /* 535 * Stop the soaker while the tool rests. 536 */ 537 if (opts->dosoaker) { 538 (void) mutex_lock(&state->soak_lock); 539 if (state->soak_state == SOAK_RUN) 540 state->soak_state = SOAK_PAUSE; 541 (void) mutex_unlock(&state->soak_lock); 542 } 543 544 htnow = gethrtime(); 545 ht += restdelta; 546 ts.tv_sec = (time_t)((ht - htnow) / 547 NSECS_PER_SEC); 548 ts.tv_nsec = (suseconds_t)((ht - htnow) % 549 NSECS_PER_SEC); 550 551 (void) nanosleep(&ts, NULL); 552 } 553 554 /* 555 * Start or stop the soaker if needed. 556 */ 557 if (opts->dosoaker) { 558 (void) mutex_lock(&state->soak_lock); 559 if (cpc_setgrp_sysonly(sgrp) && 560 state->soak_state == SOAK_PAUSE) { 561 /* 562 * Soaker is paused but the next set is 563 * sysonly: start the soaker. 564 */ 565 state->soak_state = SOAK_RUN; 566 (void) cond_signal(&state->soak_cv); 567 } else if (cpc_setgrp_sysonly(sgrp) == 0 && 568 state->soak_state == SOAK_RUN) 569 /* 570 * Soaker is running but the next set 571 * counts user events: stop the soaker. 572 */ 573 state->soak_state = SOAK_PAUSE; 574 (void) mutex_unlock(&state->soak_lock); 575 } 576 577 if (cpc_bind_cpu(cpc, state->cpuid, this, 0) != 0) 578 goto bad; 579 } 580 } 581 582 if (cpc_unbind(cpc, this) != 0) 583 (void) fprintf(stderr, gettext("%s: error unbinding on" 584 " cpu %d - %s\n"), opts->pgmname, 585 state->cpuid, strerror(errno)); 586 587 /* 588 * We're done, so stop the soaker if needed. 589 */ 590 if (opts->dosoaker) { 591 (void) mutex_lock(&state->soak_lock); 592 if (state->soak_state == SOAK_RUN) 593 state->soak_state = SOAK_PAUSE; 594 (void) mutex_unlock(&state->soak_lock); 595 } 596 597 return (NULL); 598 bad: 599 state->status = 3; 600 errstr = strerror(errno); 601 (void) fprintf(stderr, gettext("%s: cpu%d - %s\n"), 602 opts->pgmname, state->cpuid, errstr); 603 return (NULL); 604 } 605 606 static int 607 cpustat(void) 608 { 609 cpc_setgrp_t *accum; 610 cpc_set_t *start; 611 int c, i, retval; 612 int lwps = 0; 613 psetid_t mypset, cpupset; 614 char *errstr; 615 cpc_buf_t **data1, **data2, **scratch; 616 int nreqs; 617 kstat_ctl_t *kc; 618 619 ncpus = (int)sysconf(_SC_NPROCESSORS_CONF); 620 if ((gstate = calloc(ncpus, sizeof (*gstate))) == NULL) { 621 (void) fprintf(stderr, gettext( 622 "%s: out of heap\n"), opts->pgmname); 623 return (1); 624 } 625 626 max_chip_id = sysconf(_SC_CPUID_MAX); 627 if ((chip_designees = malloc(max_chip_id * sizeof (int))) == NULL) { 628 (void) fprintf(stderr, gettext( 629 "%s: out of heap\n"), opts->pgmname); 630 return (1); 631 } 632 for (i = 0; i < max_chip_id; i++) 633 chip_designees[i] = -1; 634 635 if (smt) { 636 if ((kc = kstat_open()) == NULL) { 637 (void) fprintf(stderr, gettext( 638 "%s: kstat_open() failed: %s\n"), opts->pgmname, 639 strerror(errno)); 640 return (1); 641 } 642 } 643 644 if (opts->dosoaker) 645 if (priocntl(0, 0, PC_GETCID, &fxinfo) == -1) { 646 (void) fprintf(stderr, gettext( 647 "%s: couldn't get FX scheduler class: %s\n"), 648 opts->pgmname, strerror(errno)); 649 return (1); 650 } 651 652 /* 653 * Only include processors that are participating in the system 654 */ 655 for (c = 0, i = 0; i < ncpus; c++) { 656 switch (p_online(c, P_STATUS)) { 657 case P_ONLINE: 658 case P_NOINTR: 659 if (smt) { 660 661 gstate[i].chip_id = get_chipid(kc, c); 662 if (gstate[i].chip_id != -1 && 663 chip_designees[gstate[i].chip_id] == -1) 664 chip_designees[gstate[i].chip_id] = c; 665 } 666 667 gstate[i++].cpuid = c; 668 break; 669 case P_OFFLINE: 670 case P_POWEROFF: 671 case P_FAULTED: 672 case P_SPARE: 673 gstate[i++].cpuid = -1; 674 break; 675 default: 676 gstate[i++].cpuid = -1; 677 (void) fprintf(stderr, 678 gettext("%s: cpu%d in unknown state\n"), 679 opts->pgmname, c); 680 break; 681 case -1: 682 break; 683 } 684 } 685 686 /* 687 * Examine the processor sets; if we're in one, only attempt 688 * to report on the set we're in. 689 */ 690 if (pset_bind(PS_QUERY, P_PID, P_MYID, &mypset) == -1) { 691 errstr = strerror(errno); 692 (void) fprintf(stderr, gettext("%s: pset_bind - %s\n"), 693 opts->pgmname, errstr); 694 } else { 695 for (i = 0; i < ncpus; i++) { 696 struct tstate *this = &gstate[i]; 697 698 if (this->cpuid == -1) 699 continue; 700 701 if (pset_assign(PS_QUERY, 702 this->cpuid, &cpupset) == -1) { 703 errstr = strerror(errno); 704 (void) fprintf(stderr, 705 gettext("%s: pset_assign - %s\n"), 706 opts->pgmname, errstr); 707 continue; 708 } 709 710 if (mypset != cpupset) 711 this->cpuid = -1; 712 } 713 } 714 715 if (opts->dotitle) 716 print_title(opts->master); 717 zerotime(); 718 719 for (i = 0; i < ncpus; i++) { 720 struct tstate *this = &gstate[i]; 721 722 if (this->cpuid == -1) 723 continue; 724 this->sgrp = cpc_setgrp_clone(opts->master); 725 if (this->sgrp == NULL) { 726 this->cpuid = -1; 727 continue; 728 } 729 if (thr_create(NULL, 0, gtick, this, 730 THR_BOUND|THR_NEW_LWP, &this->tid) == 0) 731 lwps++; 732 else { 733 (void) fprintf(stderr, 734 gettext("%s: cannot create thread for cpu%d\n"), 735 opts->pgmname, this->cpuid); 736 this->status = 4; 737 } 738 } 739 740 if (lwps != 0) 741 for (i = 0; i < ncpus; i++) 742 (void) thr_join(gstate[i].tid, NULL, NULL); 743 744 if ((accum = cpc_setgrp_clone(opts->master)) == NULL) { 745 (void) fprintf(stderr, gettext("%s: out of heap\n"), 746 opts->pgmname); 747 return (1); 748 } 749 750 retval = 0; 751 for (i = 0; i < ncpus; i++) { 752 struct tstate *this = &gstate[i]; 753 754 if (this->cpuid == -1) 755 continue; 756 cpc_setgrp_accum(accum, this->sgrp); 757 cpc_setgrp_free(this->sgrp); 758 this->sgrp = NULL; 759 if (this->status != 0) 760 retval = 1; 761 } 762 763 cpc_setgrp_reset(accum); 764 start = cpc_setgrp_getset(accum); 765 do { 766 nreqs = cpc_setgrp_getbufs(accum, &data1, &data2, &scratch); 767 print_total(lwps, *data1, nreqs, cpc_setgrp_getname(accum)); 768 } while (cpc_setgrp_nextset(accum) != start); 769 770 cpc_setgrp_free(accum); 771 accum = NULL; 772 773 free(gstate); 774 return (retval); 775 } 776 777 static int 778 get_chipid(kstat_ctl_t *kc, processorid_t cpuid) 779 { 780 kstat_t *ksp; 781 kstat_named_t *k; 782 783 if ((ksp = kstat_lookup(kc, "cpu_info", cpuid, NULL)) == NULL) 784 return (-1); 785 786 if (kstat_read(kc, ksp, NULL) == -1) { 787 (void) fprintf(stderr, 788 gettext("%s: kstat_read() failed for cpu %d: %s\n"), 789 opts->pgmname, cpuid, strerror(errno)); 790 return (-1); 791 } 792 793 if ((k = (kstat_named_t *)kstat_data_lookup(ksp, "chip_id")) == NULL) { 794 (void) fprintf(stderr, 795 gettext("%s: chip_id not found for cpu %d: %s\n"), 796 opts->pgmname, cpuid, strerror(errno)); 797 return (-1); 798 } 799 800 return (k->value.i32); 801 } 802 803 static void * 804 soaker(void *arg) 805 { 806 struct tstate *state = arg; 807 pcparms_t pcparms; 808 fxparms_t *fx = (fxparms_t *)pcparms.pc_clparms; 809 810 if (processor_bind(P_LWPID, P_MYID, state->cpuid, NULL) != 0) 811 (void) fprintf(stderr, gettext("%s: couldn't bind soaker " 812 "thread to cpu%d: %s\n"), opts->pgmname, state->cpuid, 813 strerror(errno)); 814 815 /* 816 * Put the soaker thread in the fixed priority (FX) class so it runs 817 * at the lowest possible global priority. 818 */ 819 pcparms.pc_cid = fxinfo.pc_cid; 820 fx->fx_upri = 0; 821 fx->fx_uprilim = 0; 822 fx->fx_tqsecs = fx->fx_tqnsecs = FX_TQDEF; 823 824 if (priocntl(P_LWPID, P_MYID, PC_SETPARMS, &pcparms) != 0) 825 (void) fprintf(stderr, gettext("%s: couldn't put soaker " 826 "thread in FX sched class: %s\n"), opts->pgmname, 827 strerror(errno)); 828 829 /* 830 * Let the parent thread know we're ready to roll. 831 */ 832 (void) mutex_lock(&state->soak_lock); 833 state->soak_state = SOAK_RUN; 834 (void) cond_signal(&state->soak_cv); 835 (void) mutex_unlock(&state->soak_lock); 836 837 for (;;) { 838 spin: 839 (void) mutex_lock(&state->soak_lock); 840 if (state->soak_state == SOAK_RUN) { 841 (void) mutex_unlock(&state->soak_lock); 842 goto spin; 843 } 844 845 while (state->soak_state == SOAK_PAUSE) 846 (void) cond_wait(&state->soak_cv, 847 &state->soak_lock); 848 (void) mutex_unlock(&state->soak_lock); 849 } 850 851 /*NOTREACHED*/ 852 return (NULL); 853 } 854