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