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
cputrack_errfn(const char * fn,int subcode,const char * fmt,va_list ap)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
cputrack_pctx_errfn(const char * fn,const char * fmt,va_list ap)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
main(int argc,char * argv[])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(1M) 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
print_title(cpc_setgrp_t * sgrp)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
print_exec(float now,pid_t pid,char * name)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
print_fork(float now,pid_t newpid,id_t lwpid,pid_t oldpid)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
print_sample(pid_t pid,id_t lwpid,char * pevent,cpc_buf_t * buf,int nreq,const char * evname)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
pinit_lwp(pctx_t * pctx,pid_t pid,id_t lwpid,void * arg)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
pfini_lwp(pctx_t * pctx,pid_t pid,id_t lwpid,void * arg)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
plwp_create(pctx_t * pctx,pid_t pid,id_t lwpid,void * arg)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
plwp_exit(pctx_t * pctx,pid_t pid,id_t lwpid,void * arg)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
pexec(pctx_t * pctx,pid_t pid,id_t lwpid,char * name,void * arg)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
pexit(pctx_t * pctx,pid_t pid,id_t lwpid,int status,void * arg)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
ptick(pctx_t * pctx,pid_t pid,id_t lwpid,void * arg)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
pfork(pctx_t * pctx,pid_t oldpid,pid_t pid,id_t lwpid,void * arg)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
cputrack(int argc,char * argv[],int optind)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
p4_ht_error(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
intr(int sig)881 intr(int sig)
882 {
883 interrupt++;
884 if (cpc != NULL)
885 cpc_terminate(cpc);
886 }
887