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 spec [-c spec]... [-p period] [-T u|d]\n"
 255                     "\t\t[-sntD] [interval [count]]\n\n"
 256                     "\t-c spec\t  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 }