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