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