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 2004 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/time.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <inttypes.h> 33 #include <unistd.h> 34 #include <string.h> 35 #include <strings.h> 36 #include <limits.h> 37 #include <libintl.h> 38 #include <locale.h> 39 #include <errno.h> 40 #include <kstat.h> 41 #include <libcpc.h> 42 43 #include "cpucmds.h" 44 45 static struct options { 46 int debug; 47 int verbose; 48 int dotitle; 49 int dohelp; 50 int dotick; 51 int cpuver; 52 char *pgmname; 53 uint_t mseconds; 54 uint_t nsamples; 55 uint_t nsets; 56 cpc_setgrp_t *master; 57 int followfork; 58 int followexec; 59 pid_t pid; 60 FILE *log; 61 } __options; 62 63 static const struct options *opts = (const struct options *)&__options; 64 65 static cpc_t *cpc; 66 67 /*ARGSUSED*/ 68 static void 69 cputrack_errfn(const char *fn, int subcode, const char *fmt, va_list ap) 70 { 71 (void) fprintf(stderr, "%s: ", opts->pgmname); 72 if (opts->debug) 73 (void) fprintf(stderr, "%s: ", fn); 74 (void) vfprintf(stderr, fmt, ap); 75 } 76 77 static void 78 cputrack_pctx_errfn(const char *fn, const char *fmt, va_list ap) 79 { 80 cputrack_errfn(fn, -1, fmt, ap); 81 } 82 83 static int cputrack(int argc, char *argv[], int optind); 84 static void p4_ht_error(void); 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 (errno == EACCES) 348 p4_ht_error(); 349 else 350 (void) fprintf(stderr, gettext( 351 "%6d: init_lwp: can't bind perf counters " 352 "to lwp%d - %s\n"), (int)pid, (int)lwpid, 353 errstr); 354 return (-1); 355 } 356 357 if (opts->verbose) 358 print_sample(pid, lwpid, "init_lwp", 359 *data2, nreq, cpc_setgrp_getname(sgrp)); 360 return (0); 361 } 362 363 /*ARGSUSED*/ 364 static int 365 pfini_lwp(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg) 366 { 367 struct pstate *state = arg; 368 cpc_setgrp_t *sgrp = state->sgrps[lwpid-1]; 369 cpc_set_t *set; 370 char *errstr; 371 cpc_buf_t **data1, **data2, **scratch; 372 int nreq; 373 374 set = cpc_setgrp_getset(sgrp); 375 nreq = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch); 376 if (cpc_set_sample(cpc, set, *data1) == 0) { 377 if (opts->verbose) 378 print_sample(pid, lwpid, "fini_lwp", 379 *data1, nreq, cpc_setgrp_getname(sgrp)); 380 cpc_setgrp_accum(state->accum, sgrp); 381 if (cpc_unbind(cpc, set) == 0) 382 return (0); 383 } 384 385 switch (errno) { 386 case EAGAIN: 387 (void) fprintf(stderr, gettext("%6d: fini_lwp: " 388 "lwp%d: perf counter contents invalidated\n"), 389 (int)pid, (int)lwpid); 390 break; 391 default: 392 errstr = strerror(errno); 393 (void) fprintf(stderr, gettext("%6d: fini_lwp: " 394 "lwp%d: can't access perf counters - %s\n"), 395 (int)pid, (int)lwpid, errstr); 396 break; 397 } 398 return (0); 399 } 400 401 /*ARGSUSED*/ 402 static int 403 plwp_create(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg) 404 { 405 cpc_setgrp_t *sgrp = opts->master; 406 cpc_buf_t **data1, **data2, **scratch; 407 int nreq; 408 409 nreq = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch); 410 411 print_sample(pid, lwpid, "lwp_create", 412 *data1, nreq, cpc_setgrp_getname(sgrp)); 413 414 return (0); 415 } 416 417 /*ARGSUSED*/ 418 static int 419 plwp_exit(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg) 420 { 421 struct pstate *state = arg; 422 cpc_setgrp_t *sgrp = state->sgrps[lwpid-1]; 423 cpc_set_t *start; 424 int nreq; 425 cpc_buf_t **data1, **data2, **scratch; 426 427 start = cpc_setgrp_getset(sgrp); 428 do { 429 nreq = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch); 430 if (cpc_buf_hrtime(cpc, *data1) == 0) 431 continue; 432 print_sample(pid, lwpid, "lwp_exit", 433 *data1, nreq, cpc_setgrp_getname(sgrp)); 434 } while (cpc_setgrp_nextset(sgrp) != start); 435 436 return (0); 437 } 438 439 /*ARGSUSED*/ 440 static int 441 pexec(pctx_t *pctx, pid_t pid, id_t lwpid, char *name, void *arg) 442 { 443 struct pstate *state = arg; 444 float now = 0.0; 445 cpc_set_t *start; 446 int nreq; 447 cpc_buf_t **data1, **data2, **scratch; 448 hrtime_t hrt; 449 450 /* 451 * Print the accumulated results from the previous program image 452 */ 453 cpc_setgrp_reset(state->accum); 454 start = cpc_setgrp_getset(state->accum); 455 do { 456 nreq = cpc_setgrp_getbufs(state->accum, &data1, &data2, 457 &scratch); 458 hrt = cpc_buf_hrtime(cpc, *data1); 459 if (hrt == 0) 460 continue; 461 print_sample(pid, lwpid, "exec", 462 *data1, nreq, cpc_setgrp_getname(state->accum)); 463 if (now < mstimestamp(hrt)) 464 now = mstimestamp(hrt); 465 } while (cpc_setgrp_nextset(state->accum) != start); 466 467 print_exec(now, pid, name); 468 469 if (state->accum != NULL) { 470 cpc_setgrp_free(state->accum); 471 state->accum = NULL; 472 } 473 474 if (opts->followexec) { 475 state->accum = cpc_setgrp_clone(opts->master); 476 return (0); 477 } 478 return (-1); 479 } 480 481 /*ARGSUSED*/ 482 static void 483 pexit(pctx_t *pctx, pid_t pid, id_t lwpid, int status, void *arg) 484 { 485 struct pstate *state = arg; 486 cpc_set_t *start; 487 int nreq; 488 cpc_buf_t **data1, **data2, **scratch; 489 490 cpc_setgrp_reset(state->accum); 491 start = cpc_setgrp_getset(state->accum); 492 do { 493 nreq = cpc_setgrp_getbufs(state->accum, &data1, &data2, 494 &scratch); 495 if (cpc_buf_hrtime(cpc, *data1) == 0) 496 continue; 497 print_sample(pid, lwpid, "exit", 498 *data1, nreq, cpc_setgrp_getname(state->accum)); 499 } while (cpc_setgrp_nextset(state->accum) != start); 500 501 cpc_setgrp_free(state->accum); 502 state->accum = NULL; 503 504 for (lwpid = 1; lwpid < state->maxlwpid; lwpid++) 505 if (state->sgrps[lwpid-1] != NULL) { 506 cpc_setgrp_free(state->sgrps[lwpid-1]); 507 state->sgrps[lwpid-1] = NULL; 508 } 509 free(state->sgrps); 510 state->sgrps = NULL; 511 } 512 513 static int 514 ptick(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg) 515 { 516 struct pstate *state = arg; 517 cpc_setgrp_t *sgrp = state->sgrps[lwpid-1]; 518 cpc_set_t *this = cpc_setgrp_getset(sgrp); 519 const char *name = cpc_setgrp_getname(sgrp); 520 cpc_buf_t **data1, **data2, **scratch, *tmp; 521 char *errstr; 522 int nreqs; 523 524 nreqs = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch); 525 526 if (opts->nsets == 1) { 527 /* 528 * If we're dealing with one set, buffer usage is: 529 * 530 * data1 = most recent data snapshot 531 * data2 = previous data snapshot 532 * scratch = used for diffing data1 and data2 533 * 534 * Save the snapshot from the previous sample in data2 535 * before putting the current sample in data1. 536 */ 537 tmp = *data1; 538 *data1 = *data2; 539 *data2 = tmp; 540 if (cpc_set_sample(cpc, this, *data1) != 0) 541 goto broken; 542 cpc_buf_sub(cpc, *scratch, *data1, *data2); 543 } else { 544 cpc_set_t *next = cpc_setgrp_nextset(sgrp); 545 /* 546 * If there is more than set in use, we will need to 547 * unbind and re-bind on each go-around because each 548 * time a counter is bound, it is preset to 0 (as it was 549 * specified when the requests were added to the set). 550 * 551 * Buffer usage in this case is: 552 * 553 * data1 = total counts for this set since program began 554 * data2 = unused 555 * scratch = most recent data snapshot 556 */ 557 558 if (cpc_set_sample(cpc, this, *scratch) != 0) 559 goto broken; 560 cpc_buf_add(cpc, *data1, *data1, *scratch); 561 562 /* 563 * No need to unbind the previous set, as binding another set 564 * automatically unbinds the most recently bound set. 565 */ 566 if (cpc_bind_pctx(cpc, pctx, lwpid, next, 0) != 0) 567 goto broken; 568 } 569 print_sample(pid, lwpid, "tick", *scratch, nreqs, name); 570 571 return (0); 572 573 broken: 574 switch (errno) { 575 case EAGAIN: 576 (void) fprintf(stderr, gettext( 577 "%6d: tick: lwp%d: perf counter contents invalidated\n"), 578 (int)pid, (int)lwpid); 579 break; 580 default: 581 errstr = strerror(errno); 582 (void) fprintf(stderr, gettext( 583 "%6d: tick: lwp%d: can't access perf counter - %s\n"), 584 (int)pid, (int)lwpid, errstr); 585 break; 586 } 587 (void) cpc_unbind(cpc, this); 588 return (-1); 589 } 590 591 /* 592 * The system has just created a new address space that has a new pid. 593 * We're running in a child of the controlling process, with a new 594 * pctx handle already opened on the child of the original controlled process. 595 */ 596 static void 597 pfork(pctx_t *pctx, pid_t oldpid, pid_t pid, id_t lwpid, void *arg) 598 { 599 struct pstate *state = arg; 600 601 print_fork(mstimestamp(0), pid, lwpid, oldpid); 602 603 if (!opts->followfork) 604 return; 605 606 if (pctx_set_events(pctx, 607 PCTX_SYSC_EXEC_EVENT, pexec, 608 PCTX_SYSC_FORK_EVENT, pfork, 609 PCTX_SYSC_EXIT_EVENT, pexit, 610 PCTX_SYSC_LWP_CREATE_EVENT, plwp_create, 611 PCTX_INIT_LWP_EVENT, pinit_lwp, 612 PCTX_FINI_LWP_EVENT, pfini_lwp, 613 PCTX_SYSC_LWP_EXIT_EVENT, plwp_exit, 614 PCTX_NULL_EVENT) == 0) { 615 state->accum = cpc_setgrp_clone(opts->master); 616 (void) pctx_run(pctx, opts->mseconds, opts->nsamples, ptick); 617 if (state->accum) { 618 free(state->accum); 619 state->accum = NULL; 620 } 621 } 622 } 623 624 /* 625 * Translate the incoming options into actions, and get the 626 * tool and the process to control running. 627 */ 628 static int 629 cputrack(int argc, char *argv[], int optind) 630 { 631 struct pstate __state, *state = &__state; 632 pctx_t *pctx; 633 int err; 634 635 bzero(state, sizeof (*state)); 636 637 if (opts->pid == 0) { 638 if (argc <= optind) { 639 (void) fprintf(stderr, "%s: %s\n", 640 opts->pgmname, 641 gettext("no program to start")); 642 return (1); 643 } 644 pctx = pctx_create(argv[optind], 645 &argv[optind], state, 1, cputrack_pctx_errfn); 646 if (pctx == NULL) { 647 (void) fprintf(stderr, "%s: %s '%s'\n", 648 opts->pgmname, 649 gettext("failed to start program"), 650 argv[optind]); 651 return (1); 652 } 653 } else { 654 pctx = pctx_capture(opts->pid, state, 1, cputrack_pctx_errfn); 655 if (pctx == NULL) { 656 (void) fprintf(stderr, "%s: %s %d\n", 657 opts->pgmname, 658 gettext("failed to capture pid"), 659 (int)opts->pid); 660 return (1); 661 } 662 } 663 664 err = pctx_set_events(pctx, 665 PCTX_SYSC_EXEC_EVENT, pexec, 666 PCTX_SYSC_FORK_EVENT, pfork, 667 PCTX_SYSC_EXIT_EVENT, pexit, 668 PCTX_SYSC_LWP_CREATE_EVENT, plwp_create, 669 PCTX_INIT_LWP_EVENT, pinit_lwp, 670 PCTX_FINI_LWP_EVENT, pfini_lwp, 671 PCTX_SYSC_LWP_EXIT_EVENT, plwp_exit, 672 PCTX_NULL_EVENT); 673 674 if (err != 0) { 675 (void) fprintf(stderr, "%s: %s\n", 676 opts->pgmname, 677 gettext("can't bind process context ops to process")); 678 } else { 679 if (opts->dotitle) 680 print_title(opts->master); 681 state->accum = cpc_setgrp_clone(opts->master); 682 zerotime(); 683 err = pctx_run(pctx, opts->mseconds, opts->nsamples, ptick); 684 if (state->accum) { 685 cpc_setgrp_free(state->accum); 686 state->accum = NULL; 687 } 688 } 689 pctx_release(pctx); 690 691 return (err != 0 ? 1 : 0); 692 } 693 694 #define OFFLINE_CMD "/usr/sbin/psradm -f " 695 #define BUFSIZE 5 /* enough for "n " where n is a cpuid */ 696 697 /* 698 * cpc_bind_pctx() failed with EACCES, which means the user must first offline 699 * all but one logical processor on each physical processor. Print to stderr the 700 * psradm command string to do this. 701 */ 702 static void 703 p4_ht_error(void) 704 { 705 kstat_ctl_t *kc; 706 kstat_t *ksp; 707 kstat_named_t *k; 708 int i; 709 int max; 710 int stat; 711 int *designees; 712 int *must_offline; 713 char buf[BUFSIZE]; 714 char *cmd; 715 int noffline = 0; 716 int ndone = 0; 717 718 (void) fprintf(stderr, "%s\n", 719 gettext("Pentium 4 processors with HyperThreading present.\nOffline" 720 " all but one logical processor on each physical processor in" 721 " order to use\ncputrack.\n")); 722 723 724 if ((kc = kstat_open()) == NULL) 725 return; 726 727 max = sysconf(_SC_CPUID_MAX); 728 if ((designees = malloc(max * sizeof (*designees))) == NULL) { 729 (void) fprintf(stderr, gettext("%s: no memory available\n"), 730 opts->pgmname); 731 exit(0); 732 } 733 734 if ((must_offline = malloc(max * sizeof (*designees))) == NULL) { 735 (void) fprintf(stderr, gettext("%s: no memory available\n"), 736 opts->pgmname); 737 exit(0); 738 } 739 740 for (i = 0; i < max; i++) { 741 designees[i] = -1; 742 must_offline[i] = 0; 743 } 744 745 for (i = 0; i < max; i++) { 746 stat = p_online(i, P_STATUS); 747 if (stat != P_ONLINE && stat != P_NOINTR) 748 continue; 749 750 if ((ksp = kstat_lookup(kc, "cpu_info", i, NULL)) == NULL) { 751 free(designees); 752 free(must_offline); 753 return; 754 } 755 756 if (kstat_read(kc, ksp, NULL) == -1) { 757 free(designees); 758 free(must_offline); 759 return; 760 } 761 762 if ((k = (kstat_named_t *)kstat_data_lookup(ksp, "chip_id")) 763 == NULL) { 764 free(designees); 765 free(must_offline); 766 return; 767 } 768 769 if (designees[k->value.i32] == -1) 770 /* 771 * This chip doesn't yet have a CPU designated to remain 772 * online; let this one be it. 773 */ 774 designees[k->value.i32] = i; 775 else { 776 /* 777 * This chip already has a designated CPU; this CPU must 778 * go offline. 779 */ 780 must_offline[i] = 1; 781 noffline++; 782 } 783 } 784 785 /* 786 * Now construct a string containing the command line used to offline 787 * the appropriate processors. 788 */ 789 790 if ((cmd = malloc(strlen(OFFLINE_CMD) + (noffline * BUFSIZE) + 1)) 791 == NULL) { 792 (void) fprintf(stderr, gettext("%s: no memory available\n"), 793 opts->pgmname); 794 exit(0); 795 } 796 797 (void) strcpy(cmd, OFFLINE_CMD); 798 799 for (i = 0; i < max; i++) { 800 if (must_offline[i] == 0) 801 continue; 802 803 ndone++; 804 (void) snprintf(buf, BUFSIZE, "%d", i); 805 if (ndone < noffline) 806 (void) strcat(buf, " "); 807 (void) strcat(cmd, buf); 808 } 809 810 (void) fprintf(stderr, "%s:\n%s\n", gettext("The following command " 811 "will configure the system appropriately"), cmd); 812 813 exit(1); 814 } 815