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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
43 static struct options
{
61 static const struct options
*opts
= (const struct options
*)&__options
;
66 * How many signals caught from terminal
67 * We bail out as soon as possible when interrupt is set
69 static int interrupt
= 0;
73 cputrack_errfn(const char *fn
, int subcode
, const char *fmt
, va_list ap
)
75 (void) fprintf(stderr
, "%s: ", opts
->pgmname
);
77 (void) fprintf(stderr
, "%s: ", fn
);
78 (void) vfprintf(stderr
, fmt
, ap
);
82 cputrack_pctx_errfn(const char *fn
, const char *fmt
, va_list ap
)
84 cputrack_errfn(fn
, -1, fmt
, ap
);
87 static int cputrack(int argc
, char *argv
[], int optind
);
88 static void intr(int);
91 static void p4_ht_error(void);
94 #if !defined(TEXT_DOMAIN)
95 #define TEXT_DOMAIN "SYS_TEST"
99 main(int argc
, char *argv
[])
101 struct options
*opts
= &__options
;
108 (void) setlocale(LC_ALL
, "");
109 (void) textdomain(TEXT_DOMAIN
);
111 if ((opts
->pgmname
= strrchr(argv
[0], '/')) == NULL
)
112 opts
->pgmname
= argv
[0];
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
);
123 (void) cpc_seterrhndlr(cpc
, cputrack_errfn
);
124 strtoset_errfn
= cputrack_errfn
;
127 * Establish (non-zero) defaults
129 opts
->mseconds
= 1000;
132 if ((opts
->master
= cpc_setgrp_new(cpc
, 0)) == NULL
) {
133 (void) fprintf(stderr
, gettext("%s: no memory available\n"),
138 while ((c
= getopt(argc
, argv
, "T:N:Defhntvo:r:c:p:")) != EOF
)
140 case 'T': /* sample time, seconds */
141 opts
->mseconds
= (uint_t
)(atof(optarg
) * 1000.0);
143 case 'N': /* number of samples */
144 nsamples
= atoi(optarg
);
148 opts
->nsamples
= (uint_t
)nsamples
;
150 case 'D': /* enable debugging */
153 case 'f': /* follow fork */
156 case 'e': /* follow exec */
159 case 'n': /* no titles */
162 case 't': /* print %tick */
166 opts
->verbose
= 1; /* more chatty */
169 if (optarg
== NULL
) {
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
);
180 case 'c': /* specify statistics */
181 if ((sgrp
= cpc_setgrp_newset(opts
->master
,
182 optarg
, &errcnt
)) != NULL
)
185 case 'p': /* grab given pid */
186 if ((opts
->pid
= atoi(optarg
)) <= 0)
198 if (opts
->nsamples
== 0)
199 opts
->nsamples
= UINT_MAX
;
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
);
224 (void) putchar('\n');
225 (void) capabilities(cpc
, stdout
);
232 * Catch signals from terminal, so they can be handled asynchronously
233 * when we're ready instead of when we're not (;-)
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
);
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
);
252 print_title(cpc_setgrp_t
*sgrp
)
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");
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
);
265 print_exec(float now
, pid_t pid
, char *name
)
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");
275 (void) fprintf(opts
->log
, "%9s ", "");
276 (void) fprintf(opts
->log
, "%9s %9s # '%s'\n", "", "", name
);
277 (void) fflush(opts
->log
);
281 print_fork(float now
, pid_t newpid
, id_t lwpid
, pid_t oldpid
)
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");
288 (void) fprintf(opts
->log
, "%9s ", "");
289 (void) fprintf(opts
->log
, "%9s %9s # %d\n", "", "", (int)newpid
);
290 (void) fflush(opts
->log
);
294 print_sample(pid_t pid
, id_t lwpid
,
295 char *pevent
, cpc_buf_t
*buf
, int nreq
, const char *evname
)
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
);
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
);
313 (void) fprintf(opts
->log
, " # %s\n", evname
);
315 (void) fputc('\n', opts
->log
);
320 cpc_setgrp_t
**sgrps
;
325 pinit_lwp(pctx_t
*pctx
, pid_t pid
, id_t lwpid
, void *arg
)
327 struct pstate
*state
= arg
;
330 cpc_buf_t
**data1
, **data2
, **scratch
;
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
);
345 while (state
->maxlwpid
< lwpid
) {
346 state
->sgrps
[state
->maxlwpid
] = NULL
;
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
);
357 state
->sgrps
[lwpid
-1] = sgrp
;
358 set
= cpc_setgrp_getset(sgrp
);
360 cpc_setgrp_reset(sgrp
);
361 set
= cpc_setgrp_getset(sgrp
);
364 nreq
= cpc_setgrp_getbufs(sgrp
, &data1
, &data2
, &scratch
);
366 if (cpc_bind_pctx(cpc
, pctx
, lwpid
, set
, 0) != 0 ||
367 cpc_set_sample(cpc
, set
, *data2
) != 0) {
368 errstr
= strerror(errno
);
370 (void) cpc_unbind(cpc
, set
);
376 (void) fprintf(stderr
, gettext(
377 "%6d: init_lwp: can't bind perf counters "
378 "to lwp%d - %s\n"), (int)pid
, (int)lwpid
,
384 print_sample(pid
, lwpid
, "init_lwp",
385 *data2
, nreq
, cpc_setgrp_getname(sgrp
));
391 pfini_lwp(pctx_t
*pctx
, pid_t pid
, id_t lwpid
, void *arg
)
393 struct pstate
*state
= arg
;
394 cpc_setgrp_t
*sgrp
= state
->sgrps
[lwpid
-1];
397 cpc_buf_t
**data1
, **data2
, **scratch
;
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) {
408 * When we only have one set of counts, the sample
409 * gives us the accumulated count.
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.
420 cpc_buf_add(cpc
, *data1
, *data1
, *scratch
);
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)
432 (void) fprintf(stderr
, gettext("%6d: fini_lwp: "
433 "lwp%d: perf counter contents invalidated\n"),
434 (int)pid
, (int)lwpid
);
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
);
448 plwp_create(pctx_t
*pctx
, pid_t pid
, id_t lwpid
, void *arg
)
450 cpc_setgrp_t
*sgrp
= opts
->master
;
451 cpc_buf_t
**data1
, **data2
, **scratch
;
457 nreq
= cpc_setgrp_getbufs(sgrp
, &data1
, &data2
, &scratch
);
459 print_sample(pid
, lwpid
, "lwp_create",
460 *data1
, nreq
, cpc_setgrp_getname(sgrp
));
467 plwp_exit(pctx_t
*pctx
, pid_t pid
, id_t lwpid
, void *arg
)
469 struct pstate
*state
= arg
;
470 cpc_setgrp_t
*sgrp
= state
->sgrps
[lwpid
-1];
473 cpc_buf_t
**data1
, **data2
, **scratch
;
478 start
= cpc_setgrp_getset(sgrp
);
480 nreq
= cpc_setgrp_getbufs(sgrp
, &data1
, &data2
, &scratch
);
481 if (cpc_buf_hrtime(cpc
, *data1
) == 0)
483 print_sample(pid
, lwpid
, "lwp_exit",
484 *data1
, nreq
, cpc_setgrp_getname(sgrp
));
485 } while (cpc_setgrp_nextset(sgrp
) != start
);
492 pexec(pctx_t
*pctx
, pid_t pid
, id_t lwpid
, char *name
, void *arg
)
494 struct pstate
*state
= arg
;
498 cpc_buf_t
**data1
, **data2
, **scratch
;
505 * Print the accumulated results from the previous program image
507 cpc_setgrp_reset(state
->accum
);
508 start
= cpc_setgrp_getset(state
->accum
);
510 nreq
= cpc_setgrp_getbufs(state
->accum
, &data1
, &data2
,
512 hrt
= cpc_buf_hrtime(cpc
, *data1
);
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
);
521 print_exec(now
, pid
, name
);
523 if (state
->accum
!= NULL
) {
524 cpc_setgrp_free(state
->accum
);
528 if (opts
->followexec
) {
529 state
->accum
= cpc_setgrp_clone(opts
->master
);
537 pexit(pctx_t
*pctx
, pid_t pid
, id_t lwpid
, int status
, void *arg
)
539 struct pstate
*state
= arg
;
542 cpc_buf_t
**data1
, **data2
, **scratch
;
547 cpc_setgrp_reset(state
->accum
);
548 start
= cpc_setgrp_getset(state
->accum
);
550 nreq
= cpc_setgrp_getbufs(state
->accum
, &data1
, &data2
,
552 if (cpc_buf_hrtime(cpc
, *data1
) == 0)
554 print_sample(pid
, lwpid
, "exit",
555 *data1
, nreq
, cpc_setgrp_getname(state
->accum
));
556 } while (cpc_setgrp_nextset(state
->accum
) != start
);
558 cpc_setgrp_free(state
->accum
);
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
;
571 ptick(pctx_t
*pctx
, pid_t pid
, id_t lwpid
, void *arg
)
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
;
584 nreqs
= cpc_setgrp_getbufs(sgrp
, &data1
, &data2
, &scratch
);
586 if (opts
->nsets
== 1) {
588 * If we're dealing with one set, buffer usage is:
590 * data1 = most recent data snapshot
591 * data2 = previous data snapshot
592 * scratch = used for diffing data1 and data2
594 * Save the snapshot from the previous sample in data2
595 * before putting the current sample in data1.
600 if (cpc_set_sample(cpc
, this, *data1
) != 0)
602 cpc_buf_sub(cpc
, *scratch
, *data1
, *data2
);
604 cpc_set_t
*next
= cpc_setgrp_nextset(sgrp
);
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).
611 * Buffer usage in this case is:
613 * data1 = total counts for this set since program began
615 * scratch = most recent data snapshot
618 if (cpc_set_sample(cpc
, this, *scratch
) != 0)
620 cpc_buf_add(cpc
, *data1
, *data1
, *scratch
);
623 * No need to unbind the previous set, as binding another set
624 * automatically unbinds the most recently bound set.
626 if (cpc_bind_pctx(cpc
, pctx
, lwpid
, next
, 0) != 0)
629 print_sample(pid
, lwpid
, "tick", *scratch
, nreqs
, name
);
636 (void) fprintf(stderr
, gettext(
637 "%6d: tick: lwp%d: perf counter contents invalidated\n"),
638 (int)pid
, (int)lwpid
);
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
);
647 (void) cpc_unbind(cpc
, this);
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.
657 pfork(pctx_t
*pctx
, pid_t oldpid
, pid_t pid
, id_t lwpid
, void *arg
)
659 struct pstate
*state
= arg
;
661 print_fork(mstimestamp(0), pid
, lwpid
, oldpid
);
663 if (!opts
->followfork
)
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
);
685 * Translate the incoming options into actions, and get the
686 * tool and the process to control running.
689 cputrack(int argc
, char *argv
[], int optind
)
691 struct pstate __state
, *state
= &__state
;
695 bzero(state
, sizeof (*state
));
697 if (opts
->pid
== 0) {
698 if (argc
<= optind
) {
699 (void) fprintf(stderr
, "%s: %s\n",
701 gettext("no program to start"));
704 pctx
= pctx_create(argv
[optind
],
705 &argv
[optind
], state
, 1, cputrack_pctx_errfn
);
707 (void) fprintf(stderr
, "%s: %s '%s'\n",
709 gettext("failed to start program"),
714 pctx
= pctx_capture(opts
->pid
, state
, 1, cputrack_pctx_errfn
);
716 (void) fprintf(stderr
, "%s: %s %d\n",
718 gettext("failed to capture pid"),
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
,
735 (void) fprintf(stderr
, "%s: %s\n",
737 gettext("can't bind process context ops to process"));
740 print_title(opts
->master
);
741 state
->accum
= cpc_setgrp_clone(opts
->master
);
743 err
= pctx_run(pctx
, opts
->mseconds
, opts
->nsamples
, ptick
);
745 cpc_setgrp_free(state
->accum
);
750 return (err
!= 0 ? 1 : 0);
755 #define OFFLINE_CMD "/usr/sbin/psradm -f "
756 #define BUFSIZE 5 /* enough for "n " where n is a cpuid */
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.
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"));
785 if ((kc
= kstat_open()) == NULL
)
788 max
= sysconf(_SC_CPUID_MAX
);
789 if ((designees
= malloc(max
* sizeof (*designees
))) == NULL
) {
790 (void) fprintf(stderr
, gettext("%s: no memory available\n"),
795 if ((must_offline
= malloc(max
* sizeof (*designees
))) == NULL
) {
796 (void) fprintf(stderr
, gettext("%s: no memory available\n"),
801 for (i
= 0; i
< max
; i
++) {
806 for (i
= 0; i
< max
; i
++) {
807 stat
= p_online(i
, P_STATUS
);
808 if (stat
!= P_ONLINE
&& stat
!= P_NOINTR
)
811 if ((ksp
= kstat_lookup(kc
, "cpu_info", i
, NULL
)) == NULL
) {
817 if (kstat_read(kc
, ksp
, NULL
) == -1) {
823 if ((k
= (kstat_named_t
*)kstat_data_lookup(ksp
, "chip_id"))
830 if (designees
[k
->value
.i32
] == -1)
832 * This chip doesn't yet have a CPU designated to remain
833 * online; let this one be it.
835 designees
[k
->value
.i32
] = i
;
838 * This chip already has a designated CPU; this CPU must
847 * Now construct a string containing the command line used to offline
848 * the appropriate processors.
851 if ((cmd
= malloc(strlen(OFFLINE_CMD
) + (noffline
* BUFSIZE
) + 1))
853 (void) fprintf(stderr
, gettext("%s: no memory available\n"),
858 (void) strcpy(cmd
, OFFLINE_CMD
);
860 for (i
= 0; i
< max
; i
++) {
861 if (must_offline
[i
] == 0)
865 (void) snprintf(buf
, BUFSIZE
, "%d", i
);
866 if (ndone
< noffline
)
867 (void) strcat(buf
, " ");
868 (void) strcat(cmd
, buf
);
871 (void) fprintf(stderr
, "%s:\n%s\n", gettext("The following command "
872 "will configure the system appropriately"), cmd
);
877 #endif /* defined(__i386) */