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