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