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