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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 #include <sys/time.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <inttypes.h> 31 #include <unistd.h> 32 #include <string.h> 33 #include <strings.h> 34 #include <limits.h> 35 #include <libintl.h> 36 #include <locale.h> 37 #include <errno.h> 38 #include <kstat.h> 39 #include <libcpc.h> 40 41 #include "cpucmds.h" 42 43 static struct options { 44 int debug; 45 int verbose; 46 int dotitle; 47 int dohelp; 48 int dotick; 49 int cpuver; 50 char *pgmname; 51 uint_t mseconds; 52 uint_t nsamples; 53 uint_t nsets; 54 cpc_setgrp_t *master; 55 int followfork; 56 int followexec; 57 pid_t pid; 58 FILE *log; 59 } __options; 60 61 static const struct options *opts = (const struct options *)&__options; 62 63 static cpc_t *cpc; 64 65 /*ARGSUSED*/ 66 static void 67 cputrack_errfn(const char *fn, int subcode, const char *fmt, va_list ap) 68 { 69 (void) fprintf(stderr, "%s: ", opts->pgmname); 70 if (opts->debug) 71 (void) fprintf(stderr, "%s: ", fn); 72 (void) vfprintf(stderr, fmt, ap); 73 } 74 75 static void 76 cputrack_pctx_errfn(const char *fn, const char *fmt, va_list ap) 77 { 78 cputrack_errfn(fn, -1, fmt, ap); 79 } 80 81 static int cputrack(int argc, char *argv[], int optind); 82 #if defined(__i386) 83 static void p4_ht_error(void); 84 #endif 85 86 #if !defined(TEXT_DOMAIN) 87 #define TEXT_DOMAIN "SYS_TEST" 88 #endif 89 90 int 91 main(int argc, char *argv[]) 92 { 93 struct options *opts = &__options; 94 int c, errcnt = 0; 95 int nsamples; 96 cpc_setgrp_t *sgrp; 97 char *errstr; 98 int ret; 99 100 (void) setlocale(LC_ALL, ""); 101 (void) textdomain(TEXT_DOMAIN); 102 103 if ((opts->pgmname = strrchr(argv[0], '/')) == NULL) 104 opts->pgmname = argv[0]; 105 else 106 opts->pgmname++; 107 108 if ((cpc = cpc_open(CPC_VER_CURRENT)) == NULL) { 109 errstr = strerror(errno); 110 (void) fprintf(stderr, gettext("%s: cannot access performance " 111 "counter library - %s\n"), opts->pgmname, errstr); 112 return (1); 113 } 114 115 (void) cpc_seterrhndlr(cpc, cputrack_errfn); 116 strtoset_errfn = cputrack_errfn; 117 118 /* 119 * Establish (non-zero) defaults 120 */ 121 opts->mseconds = 1000; 122 opts->dotitle = 1; 123 opts->log = stdout; 124 if ((opts->master = cpc_setgrp_new(cpc, 0)) == NULL) { 125 (void) fprintf(stderr, gettext("%s: no memory available\n"), 126 opts->pgmname); 127 exit(1); 128 } 129 130 while ((c = getopt(argc, argv, "T:N:Defhntvo:r:c:p:")) != EOF) 131 switch (c) { 132 case 'T': /* sample time, seconds */ 133 opts->mseconds = (uint_t)(atof(optarg) * 1000.0); 134 break; 135 case 'N': /* number of samples */ 136 nsamples = atoi(optarg); 137 if (nsamples < 0) 138 errcnt++; 139 else 140 opts->nsamples = (uint_t)nsamples; 141 break; 142 case 'D': /* enable debugging */ 143 opts->debug++; 144 break; 145 case 'f': /* follow fork */ 146 opts->followfork++; 147 break; 148 case 'e': /* follow exec */ 149 opts->followexec++; 150 break; 151 case 'n': /* no titles */ 152 opts->dotitle = 0; 153 break; 154 case 't': /* print %tick */ 155 opts->dotick = 1; 156 break; 157 case 'v': 158 opts->verbose = 1; /* more chatty */ 159 break; 160 case 'o': 161 if (optarg == NULL) { 162 errcnt++; 163 break; 164 } 165 if ((opts->log = fopen(optarg, "w")) == NULL) { 166 (void) fprintf(stderr, gettext( 167 "%s: cannot open '%s' for writing\n"), 168 opts->pgmname, optarg); 169 return (1); 170 } 171 break; 172 case 'c': /* specify statistics */ 173 if ((sgrp = cpc_setgrp_newset(opts->master, 174 optarg, &errcnt)) != NULL) 175 opts->master = sgrp; 176 break; 177 case 'p': /* grab given pid */ 178 if ((opts->pid = atoi(optarg)) <= 0) 179 errcnt++; 180 break; 181 case 'h': 182 opts->dohelp = 1; 183 break; 184 case '?': 185 default: 186 errcnt++; 187 break; 188 } 189 190 if (opts->nsamples == 0) 191 opts->nsamples = UINT_MAX; 192 193 if (errcnt != 0 || 194 opts->dohelp || 195 (argc == optind && opts->pid == 0) || 196 (argc > optind && opts->pid != 0) || 197 (opts->nsets = cpc_setgrp_numsets(opts->master)) == 0) { 198 (void) fprintf(opts->dohelp ? stdout : stderr, gettext( 199 "Usage:\n\t%s [-T secs] [-N count] [-Defhnv] [-o file]\n" 200 "\t\t-c events [command [args] | -p pid]\n\n" 201 "\t-T secs\t seconds between samples, default 1\n" 202 "\t-N count number of samples, default unlimited\n" 203 "\t-D\t enable debug mode\n" 204 "\t-e\t follow exec(2), and execve(2)\n" 205 "\t-f\t follow fork(2), fork1(2), and vfork(2)\n" 206 "\t-h\t print extended usage information\n" 207 "\t-n\t suppress titles\n" 208 "\t-t\t include virtualized %s register\n" 209 "\t-v\t verbose mode\n" 210 "\t-o file\t write cpu statistics to this file\n" 211 "\t-c events specify processor events to be monitored\n" 212 "\t-p pid\t pid of existing process to capture\n\n" 213 "\tUse cpustat(1M) to monitor system-wide statistics.\n"), 214 opts->pgmname, CPC_TICKREG_NAME); 215 if (opts->dohelp) { 216 (void) putchar('\n'); 217 (void) capabilities(cpc, stdout); 218 exit(0); 219 } 220 exit(2); 221 } 222 223 cpc_setgrp_reset(opts->master); 224 (void) setvbuf(opts->log, NULL, _IOLBF, 0); 225 ret = cputrack(argc, argv, optind); 226 (void) cpc_close(cpc); 227 return (ret); 228 } 229 230 static void 231 print_title(cpc_setgrp_t *sgrp) 232 { 233 (void) fprintf(opts->log, "%7s ", "time"); 234 if (opts->followfork) 235 (void) fprintf(opts->log, "%6s ", "pid"); 236 (void) fprintf(opts->log, "%3s %10s ", "lwp", "event"); 237 if (opts->dotick) 238 (void) fprintf(opts->log, "%9s ", CPC_TICKREG_NAME); 239 (void) fprintf(opts->log, "%s\n", cpc_setgrp_gethdr(sgrp)); 240 (void) fflush(opts->log); 241 } 242 243 static void 244 print_exec(float now, pid_t pid, char *name) 245 { 246 if (name == NULL) 247 name = "(unknown)"; 248 249 (void) fprintf(opts->log, "%7.3f ", now); 250 if (opts->followfork) 251 (void) fprintf(opts->log, "%6d ", (int)pid); 252 (void) fprintf(opts->log, "%3d %10s ", 1, "exec"); 253 if (opts->dotick) 254 (void) fprintf(opts->log, "%9s ", ""); 255 (void) fprintf(opts->log, "%9s %9s # '%s'\n", "", "", name); 256 (void) fflush(opts->log); 257 } 258 259 static void 260 print_fork(float now, pid_t newpid, id_t lwpid, pid_t oldpid) 261 { 262 (void) fprintf(opts->log, "%7.3f ", now); 263 if (opts->followfork) 264 (void) fprintf(opts->log, "%6d ", (int)oldpid); 265 (void) fprintf(opts->log, "%3d %10s ", (int)lwpid, "fork"); 266 if (opts->dotick) 267 (void) fprintf(opts->log, "%9s ", ""); 268 (void) fprintf(opts->log, "%9s %9s # %d\n", "", "", (int)newpid); 269 (void) fflush(opts->log); 270 } 271 272 static void 273 print_sample(pid_t pid, id_t lwpid, 274 char *pevent, cpc_buf_t *buf, int nreq, const char *evname) 275 { 276 uint64_t val; 277 int i; 278 279 (void) fprintf(opts->log, "%7.3f ", 280 mstimestamp(cpc_buf_hrtime(cpc, buf))); 281 if (opts->followfork) 282 (void) fprintf(opts->log, "%6d ", (int)pid); 283 (void) fprintf(opts->log, "%3d %10s ", (int)lwpid, pevent); 284 if (opts->dotick) 285 (void) fprintf(opts->log, "%9" PRId64 " ", 286 cpc_buf_tick(cpc, buf)); 287 for (i = 0; i < nreq; i++) { 288 (void) cpc_buf_get(cpc, buf, i, &val); 289 (void) fprintf(opts->log, "%9" PRId64 " ", val); 290 } 291 if (opts->nsets > 1) 292 (void) fprintf(opts->log, " # %s\n", evname); 293 else 294 (void) fputc('\n', opts->log); 295 } 296 297 struct pstate { 298 cpc_setgrp_t *accum; 299 cpc_setgrp_t **sgrps; 300 int maxlwpid; 301 }; 302 303 static int 304 pinit_lwp(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg) 305 { 306 struct pstate *state = arg; 307 cpc_setgrp_t *sgrp; 308 cpc_set_t *set; 309 cpc_buf_t **data1, **data2, **scratch; 310 char *errstr; 311 int nreq; 312 313 if (state->maxlwpid < lwpid) { 314 state->sgrps = realloc(state->sgrps, 315 lwpid * sizeof (state->sgrps)); 316 if (state->sgrps == NULL) { 317 (void) fprintf(stderr, gettext( 318 "%6d: init_lwp: out of memory\n"), (int)pid); 319 return (-1); 320 } 321 while (state->maxlwpid < lwpid) { 322 state->sgrps[state->maxlwpid] = NULL; 323 state->maxlwpid++; 324 } 325 } 326 327 if ((sgrp = state->sgrps[lwpid-1]) == NULL) { 328 if ((sgrp = cpc_setgrp_clone(opts->master)) == NULL) { 329 (void) fprintf(stderr, gettext( 330 "%6d: init_lwp: out of memory\n"), (int)pid); 331 return (-1); 332 } 333 state->sgrps[lwpid-1] = sgrp; 334 set = cpc_setgrp_getset(sgrp); 335 } else { 336 cpc_setgrp_reset(sgrp); 337 set = cpc_setgrp_getset(sgrp); 338 } 339 340 nreq = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch); 341 342 if (cpc_bind_pctx(cpc, pctx, lwpid, set, 0) != 0 || 343 cpc_set_sample(cpc, set, *data2) != 0) { 344 errstr = strerror(errno); 345 if (errno == EAGAIN) 346 (void) cpc_unbind(cpc, set); 347 #if defined(__i386) 348 if (errno == EACCES) 349 p4_ht_error(); 350 else 351 #endif 352 (void) fprintf(stderr, gettext( 353 "%6d: init_lwp: can't bind perf counters " 354 "to lwp%d - %s\n"), (int)pid, (int)lwpid, 355 errstr); 356 return (-1); 357 } 358 359 if (opts->verbose) 360 print_sample(pid, lwpid, "init_lwp", 361 *data2, nreq, cpc_setgrp_getname(sgrp)); 362 return (0); 363 } 364 365 /*ARGSUSED*/ 366 static int 367 pfini_lwp(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg) 368 { 369 struct pstate *state = arg; 370 cpc_setgrp_t *sgrp = state->sgrps[lwpid-1]; 371 cpc_set_t *set; 372 char *errstr; 373 cpc_buf_t **data1, **data2, **scratch; 374 int nreq; 375 376 set = cpc_setgrp_getset(sgrp); 377 nreq = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch); 378 if (cpc_set_sample(cpc, set, *scratch) == 0) { 379 if (opts->nsets == 1) { 380 /* 381 * When we only have one set of counts, the sample 382 * gives us the accumulated count. 383 */ 384 *data1 = *scratch; 385 } else { 386 /* 387 * When we have more than one set of counts, the 388 * sample gives us the count for the latest sample 389 * period. *data1 contains the accumulated count but 390 * does not include the count for the latest sample 391 * period for this set of counters. 392 */ 393 cpc_buf_add(cpc, *data1, *data1, *scratch); 394 } 395 if (opts->verbose) 396 print_sample(pid, lwpid, "fini_lwp", 397 *data1, nreq, cpc_setgrp_getname(sgrp)); 398 cpc_setgrp_accum(state->accum, sgrp); 399 if (cpc_unbind(cpc, set) == 0) 400 return (0); 401 } 402 403 switch (errno) { 404 case EAGAIN: 405 (void) fprintf(stderr, gettext("%6d: fini_lwp: " 406 "lwp%d: perf counter contents invalidated\n"), 407 (int)pid, (int)lwpid); 408 break; 409 default: 410 errstr = strerror(errno); 411 (void) fprintf(stderr, gettext("%6d: fini_lwp: " 412 "lwp%d: can't access perf counters - %s\n"), 413 (int)pid, (int)lwpid, errstr); 414 break; 415 } 416 return (-1); 417 } 418 419 /*ARGSUSED*/ 420 static int 421 plwp_create(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg) 422 { 423 cpc_setgrp_t *sgrp = opts->master; 424 cpc_buf_t **data1, **data2, **scratch; 425 int nreq; 426 427 nreq = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch); 428 429 print_sample(pid, lwpid, "lwp_create", 430 *data1, nreq, cpc_setgrp_getname(sgrp)); 431 432 return (0); 433 } 434 435 /*ARGSUSED*/ 436 static int 437 plwp_exit(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg) 438 { 439 struct pstate *state = arg; 440 cpc_setgrp_t *sgrp = state->sgrps[lwpid-1]; 441 cpc_set_t *start; 442 int nreq; 443 cpc_buf_t **data1, **data2, **scratch; 444 445 start = cpc_setgrp_getset(sgrp); 446 do { 447 nreq = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch); 448 if (cpc_buf_hrtime(cpc, *data1) == 0) 449 continue; 450 print_sample(pid, lwpid, "lwp_exit", 451 *data1, nreq, cpc_setgrp_getname(sgrp)); 452 } while (cpc_setgrp_nextset(sgrp) != start); 453 454 return (0); 455 } 456 457 /*ARGSUSED*/ 458 static int 459 pexec(pctx_t *pctx, pid_t pid, id_t lwpid, char *name, void *arg) 460 { 461 struct pstate *state = arg; 462 float now = 0.0; 463 cpc_set_t *start; 464 int nreq; 465 cpc_buf_t **data1, **data2, **scratch; 466 hrtime_t hrt; 467 468 /* 469 * Print the accumulated results from the previous program image 470 */ 471 cpc_setgrp_reset(state->accum); 472 start = cpc_setgrp_getset(state->accum); 473 do { 474 nreq = cpc_setgrp_getbufs(state->accum, &data1, &data2, 475 &scratch); 476 hrt = cpc_buf_hrtime(cpc, *data1); 477 if (hrt == 0) 478 continue; 479 print_sample(pid, lwpid, "exec", 480 *data1, nreq, cpc_setgrp_getname(state->accum)); 481 if (now < mstimestamp(hrt)) 482 now = mstimestamp(hrt); 483 } while (cpc_setgrp_nextset(state->accum) != start); 484 485 print_exec(now, pid, name); 486 487 if (state->accum != NULL) { 488 cpc_setgrp_free(state->accum); 489 state->accum = NULL; 490 } 491 492 if (opts->followexec) { 493 state->accum = cpc_setgrp_clone(opts->master); 494 return (0); 495 } 496 return (-1); 497 } 498 499 /*ARGSUSED*/ 500 static void 501 pexit(pctx_t *pctx, pid_t pid, id_t lwpid, int status, void *arg) 502 { 503 struct pstate *state = arg; 504 cpc_set_t *start; 505 int nreq; 506 cpc_buf_t **data1, **data2, **scratch; 507 508 cpc_setgrp_reset(state->accum); 509 start = cpc_setgrp_getset(state->accum); 510 do { 511 nreq = cpc_setgrp_getbufs(state->accum, &data1, &data2, 512 &scratch); 513 if (cpc_buf_hrtime(cpc, *data1) == 0) 514 continue; 515 print_sample(pid, lwpid, "exit", 516 *data1, nreq, cpc_setgrp_getname(state->accum)); 517 } while (cpc_setgrp_nextset(state->accum) != start); 518 519 cpc_setgrp_free(state->accum); 520 state->accum = NULL; 521 522 for (lwpid = 1; lwpid < state->maxlwpid; lwpid++) 523 if (state->sgrps[lwpid-1] != NULL) { 524 cpc_setgrp_free(state->sgrps[lwpid-1]); 525 state->sgrps[lwpid-1] = NULL; 526 } 527 free(state->sgrps); 528 state->sgrps = NULL; 529 } 530 531 static int 532 ptick(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg) 533 { 534 struct pstate *state = arg; 535 cpc_setgrp_t *sgrp = state->sgrps[lwpid-1]; 536 cpc_set_t *this = cpc_setgrp_getset(sgrp); 537 const char *name = cpc_setgrp_getname(sgrp); 538 cpc_buf_t **data1, **data2, **scratch, *tmp; 539 char *errstr; 540 int nreqs; 541 542 nreqs = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch); 543 544 if (opts->nsets == 1) { 545 /* 546 * If we're dealing with one set, buffer usage is: 547 * 548 * data1 = most recent data snapshot 549 * data2 = previous data snapshot 550 * scratch = used for diffing data1 and data2 551 * 552 * Save the snapshot from the previous sample in data2 553 * before putting the current sample in data1. 554 */ 555 tmp = *data1; 556 *data1 = *data2; 557 *data2 = tmp; 558 if (cpc_set_sample(cpc, this, *data1) != 0) 559 goto broken; 560 cpc_buf_sub(cpc, *scratch, *data1, *data2); 561 } else { 562 cpc_set_t *next = cpc_setgrp_nextset(sgrp); 563 /* 564 * If there is more than set in use, we will need to 565 * unbind and re-bind on each go-around because each 566 * time a counter is bound, it is preset to 0 (as it was 567 * specified when the requests were added to the set). 568 * 569 * Buffer usage in this case is: 570 * 571 * data1 = total counts for this set since program began 572 * data2 = unused 573 * scratch = most recent data snapshot 574 */ 575 576 if (cpc_set_sample(cpc, this, *scratch) != 0) 577 goto broken; 578 cpc_buf_add(cpc, *data1, *data1, *scratch); 579 580 /* 581 * No need to unbind the previous set, as binding another set 582 * automatically unbinds the most recently bound set. 583 */ 584 if (cpc_bind_pctx(cpc, pctx, lwpid, next, 0) != 0) 585 goto broken; 586 } 587 print_sample(pid, lwpid, "tick", *scratch, nreqs, name); 588 589 return (0); 590 591 broken: 592 switch (errno) { 593 case EAGAIN: 594 (void) fprintf(stderr, gettext( 595 "%6d: tick: lwp%d: perf counter contents invalidated\n"), 596 (int)pid, (int)lwpid); 597 break; 598 default: 599 errstr = strerror(errno); 600 (void) fprintf(stderr, gettext( 601 "%6d: tick: lwp%d: can't access perf counter - %s\n"), 602 (int)pid, (int)lwpid, errstr); 603 break; 604 } 605 (void) cpc_unbind(cpc, this); 606 return (-1); 607 } 608 609 /* 610 * The system has just created a new address space that has a new pid. 611 * We're running in a child of the controlling process, with a new 612 * pctx handle already opened on the child of the original controlled process. 613 */ 614 static void 615 pfork(pctx_t *pctx, pid_t oldpid, pid_t pid, id_t lwpid, void *arg) 616 { 617 struct pstate *state = arg; 618 619 print_fork(mstimestamp(0), pid, lwpid, oldpid); 620 621 if (!opts->followfork) 622 return; 623 624 if (pctx_set_events(pctx, 625 PCTX_SYSC_EXEC_EVENT, pexec, 626 PCTX_SYSC_FORK_EVENT, pfork, 627 PCTX_SYSC_EXIT_EVENT, pexit, 628 PCTX_SYSC_LWP_CREATE_EVENT, plwp_create, 629 PCTX_INIT_LWP_EVENT, pinit_lwp, 630 PCTX_FINI_LWP_EVENT, pfini_lwp, 631 PCTX_SYSC_LWP_EXIT_EVENT, plwp_exit, 632 PCTX_NULL_EVENT) == 0) { 633 state->accum = cpc_setgrp_clone(opts->master); 634 (void) pctx_run(pctx, opts->mseconds, opts->nsamples, ptick); 635 if (state->accum) { 636 free(state->accum); 637 state->accum = NULL; 638 } 639 } 640 } 641 642 /* 643 * Translate the incoming options into actions, and get the 644 * tool and the process to control running. 645 */ 646 static int 647 cputrack(int argc, char *argv[], int optind) 648 { 649 struct pstate __state, *state = &__state; 650 pctx_t *pctx; 651 int err; 652 653 bzero(state, sizeof (*state)); 654 655 if (opts->pid == 0) { 656 if (argc <= optind) { 657 (void) fprintf(stderr, "%s: %s\n", 658 opts->pgmname, 659 gettext("no program to start")); 660 return (1); 661 } 662 pctx = pctx_create(argv[optind], 663 &argv[optind], state, 1, cputrack_pctx_errfn); 664 if (pctx == NULL) { 665 (void) fprintf(stderr, "%s: %s '%s'\n", 666 opts->pgmname, 667 gettext("failed to start program"), 668 argv[optind]); 669 return (1); 670 } 671 } else { 672 pctx = pctx_capture(opts->pid, state, 1, cputrack_pctx_errfn); 673 if (pctx == NULL) { 674 (void) fprintf(stderr, "%s: %s %d\n", 675 opts->pgmname, 676 gettext("failed to capture pid"), 677 (int)opts->pid); 678 return (1); 679 } 680 } 681 682 err = pctx_set_events(pctx, 683 PCTX_SYSC_EXEC_EVENT, pexec, 684 PCTX_SYSC_FORK_EVENT, pfork, 685 PCTX_SYSC_EXIT_EVENT, pexit, 686 PCTX_SYSC_LWP_CREATE_EVENT, plwp_create, 687 PCTX_INIT_LWP_EVENT, pinit_lwp, 688 PCTX_FINI_LWP_EVENT, pfini_lwp, 689 PCTX_SYSC_LWP_EXIT_EVENT, plwp_exit, 690 PCTX_NULL_EVENT); 691 692 if (err != 0) { 693 (void) fprintf(stderr, "%s: %s\n", 694 opts->pgmname, 695 gettext("can't bind process context ops to process")); 696 } else { 697 if (opts->dotitle) 698 print_title(opts->master); 699 state->accum = cpc_setgrp_clone(opts->master); 700 zerotime(); 701 err = pctx_run(pctx, opts->mseconds, opts->nsamples, ptick); 702 if (state->accum) { 703 cpc_setgrp_free(state->accum); 704 state->accum = NULL; 705 } 706 } 707 pctx_release(pctx); 708 709 return (err != 0 ? 1 : 0); 710 } 711 712 #if defined(__i386) 713 714 #define OFFLINE_CMD "/usr/sbin/psradm -f " 715 #define BUFSIZE 5 /* enough for "n " where n is a cpuid */ 716 717 /* 718 * cpc_bind_pctx() failed with EACCES, which means the user must first offline 719 * all but one logical processor on each physical processor. Print to stderr the 720 * psradm command string to do this. 721 */ 722 static void 723 p4_ht_error(void) 724 { 725 kstat_ctl_t *kc; 726 kstat_t *ksp; 727 kstat_named_t *k; 728 int i; 729 int max; 730 int stat; 731 int *designees; 732 int *must_offline; 733 char buf[BUFSIZE]; 734 char *cmd; 735 int noffline = 0; 736 int ndone = 0; 737 738 (void) fprintf(stderr, "%s\n", 739 gettext("Pentium 4 processors with HyperThreading present.\nOffline" 740 " all but one logical processor on each physical processor in" 741 " order to use\ncputrack.\n")); 742 743 744 if ((kc = kstat_open()) == NULL) 745 return; 746 747 max = sysconf(_SC_CPUID_MAX); 748 if ((designees = malloc(max * sizeof (*designees))) == NULL) { 749 (void) fprintf(stderr, gettext("%s: no memory available\n"), 750 opts->pgmname); 751 exit(0); 752 } 753 754 if ((must_offline = malloc(max * sizeof (*designees))) == NULL) { 755 (void) fprintf(stderr, gettext("%s: no memory available\n"), 756 opts->pgmname); 757 exit(0); 758 } 759 760 for (i = 0; i < max; i++) { 761 designees[i] = -1; 762 must_offline[i] = 0; 763 } 764 765 for (i = 0; i < max; i++) { 766 stat = p_online(i, P_STATUS); 767 if (stat != P_ONLINE && stat != P_NOINTR) 768 continue; 769 770 if ((ksp = kstat_lookup(kc, "cpu_info", i, NULL)) == NULL) { 771 free(designees); 772 free(must_offline); 773 return; 774 } 775 776 if (kstat_read(kc, ksp, NULL) == -1) { 777 free(designees); 778 free(must_offline); 779 return; 780 } 781 782 if ((k = (kstat_named_t *)kstat_data_lookup(ksp, "chip_id")) 783 == NULL) { 784 free(designees); 785 free(must_offline); 786 return; 787 } 788 789 if (designees[k->value.i32] == -1) 790 /* 791 * This chip doesn't yet have a CPU designated to remain 792 * online; let this one be it. 793 */ 794 designees[k->value.i32] = i; 795 else { 796 /* 797 * This chip already has a designated CPU; this CPU must 798 * go offline. 799 */ 800 must_offline[i] = 1; 801 noffline++; 802 } 803 } 804 805 /* 806 * Now construct a string containing the command line used to offline 807 * the appropriate processors. 808 */ 809 810 if ((cmd = malloc(strlen(OFFLINE_CMD) + (noffline * BUFSIZE) + 1)) 811 == NULL) { 812 (void) fprintf(stderr, gettext("%s: no memory available\n"), 813 opts->pgmname); 814 exit(0); 815 } 816 817 (void) strcpy(cmd, OFFLINE_CMD); 818 819 for (i = 0; i < max; i++) { 820 if (must_offline[i] == 0) 821 continue; 822 823 ndone++; 824 (void) snprintf(buf, BUFSIZE, "%d", i); 825 if (ndone < noffline) 826 (void) strcat(buf, " "); 827 (void) strcat(cmd, buf); 828 } 829 830 (void) fprintf(stderr, "%s:\n%s\n", gettext("The following command " 831 "will configure the system appropriately"), cmd); 832 833 exit(1); 834 } 835 836 #endif /* defined(__i386) */ 837