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.
26 #include <sys/types.h>
27 #include <sys/processor.h>
30 #include <sys/priocntl.h>
31 #include <sys/fxpriocntl.h>
47 #include <sys/resource.h>
50 #include "statcommon.h"
52 static struct options
{
68 * States for soaker threads.
84 static const struct options
*opts
= (const struct options
*)&__options
;
88 struct tstate
*gstate
;
90 static int max_chip_id
;
91 static int *chip_designees
; /* cpuid of CPU which counts for phs chip */
92 static int smt
= 0; /* If set, cpustat needs to be SMT-aware. */
93 static pcinfo_t fxinfo
= { 0, "FX", 0 }; /* FX scheduler class info */
95 static uint_t timestamp_fmt
= NODATE
;
99 cpustat_errfn(const char *fn
, int subcode
, const char *fmt
, va_list ap
)
101 (void) fprintf(stderr
, "%s: ", opts
->pgmname
);
103 (void) fprintf(stderr
, "%s: ", fn
);
104 (void) vfprintf(stderr
, fmt
, ap
);
107 static int cpustat(void);
108 static int get_chipid(kstat_ctl_t
*kc
, processorid_t cpuid
);
109 static void *soaker(void *arg
);
112 #if !defined(TEXT_DOMAIN)
113 #define TEXT_DOMAIN "SYS_TEST"
117 main(int argc
, char *argv
[])
119 struct options
*opts
= &__options
;
120 int c
, errcnt
= 0, ret
;
127 (void) setlocale(LC_ALL
, "");
128 (void) textdomain(TEXT_DOMAIN
);
130 if ((opts
->pgmname
= strrchr(argv
[0], '/')) == NULL
)
131 opts
->pgmname
= argv
[0];
135 /* Make sure we can open enough files */
136 rl
.rlim_max
= rl
.rlim_cur
= RLIM_INFINITY
;
137 if (setrlimit(RLIMIT_NOFILE
, &rl
) != 0) {
138 errstr
= strerror(errno
);
139 (void) fprintf(stderr
,
140 gettext("%s: setrlimit failed - %s\n"),
141 opts
->pgmname
, errstr
);
144 if ((cpc
= cpc_open(CPC_VER_CURRENT
)) == NULL
) {
145 errstr
= strerror(errno
);
146 (void) fprintf(stderr
, gettext("%s: cannot access performance "
147 "counters - %s\n"), opts
->pgmname
, errstr
);
151 (void) cpc_seterrhndlr(cpc
, cpustat_errfn
);
152 strtoset_errfn
= cpustat_errfn
;
155 * Check to see if cpustat needs to be SMT-aware.
157 smt
= smt_limited_cpc_hw(cpc
);
160 * Establish some defaults
162 opts
->mseconds
= 5000;
163 opts
->nsamples
= UINT_MAX
;
165 if ((opts
->master
= cpc_setgrp_new(cpc
, smt
)) == NULL
) {
166 (void) fprintf(stderr
, gettext("%s: out of heap\n"),
171 while ((c
= getopt(argc
, argv
, "Dc:hntT:sp:")) != EOF
&& errcnt
== 0)
173 case 'D': /* enable debugging */
176 case 'c': /* specify statistics */
177 if ((sgrp
= cpc_setgrp_newset(opts
->master
,
178 optarg
, &errcnt
)) != NULL
)
181 case 'n': /* no titles */
184 case 'p': /* periodic behavior */
186 period
= strtod(optarg
, &endp
);
188 (void) fprintf(stderr
, gettext("%s: invalid "
189 "parameter \"%s\"\n"), opts
->pgmname
,
194 case 's': /* run soaker thread */
197 case 't': /* print %tick */
203 timestamp_fmt
= UDATE
;
204 else if (*optarg
== 'd')
205 timestamp_fmt
= DDATE
;
221 switch (argc
- optind
) {
225 opts
->nsamples
= strtol(argv
[optind
+ 1], &endp
, 10);
227 (void) fprintf(stderr
,
228 gettext("%s: invalid argument \"%s\"\n"),
229 opts
->pgmname
, argv
[optind
+ 1]);
235 opts
->mseconds
= (uint_t
)(strtod(argv
[optind
], &endp
) * 1000.0);
237 (void) fprintf(stderr
,
238 gettext("%s: invalid argument \"%s\"\n"),
239 opts
->pgmname
, argv
[optind
]);
248 if (opts
->nsamples
== 0 || opts
->mseconds
== 0)
251 if (errcnt
!= 0 || opts
->dohelp
||
252 (opts
->nsets
= cpc_setgrp_numsets(opts
->master
)) == 0) {
253 (void) fprintf(opts
->dohelp
? stdout
: stderr
, gettext(
254 "Usage:\n\t%s -c spec [-c spec]... [-p period] [-T u|d]\n"
255 "\t\t[-sntD] [interval [count]]\n\n"
256 "\t-c spec\t specify processor events to be monitored\n"
257 "\t-n\t suppress titles\n"
258 "\t-p period cycle through event list periodically\n"
259 "\t-s\t run user soaker thread for system-only events\n"
260 "\t-t\t include %s register\n"
261 "\t-T d|u\t Display a timestamp in date (d) or unix "
263 "\t-D\t enable debug mode\n"
264 "\t-h\t print extended usage information\n\n"
265 "\tUse cputrack(1) to monitor per-process statistics.\n"),
266 opts
->pgmname
, CPC_TICKREG_NAME
);
268 (void) putchar('\n');
269 (void) capabilities(cpc
, stdout
);
276 * If the user requested periodic behavior, calculate the rest time
279 if (opts
->doperiod
) {
280 opts
->mseconds_rest
= (uint_t
)((period
* 1000.0) -
281 (opts
->mseconds
* opts
->nsets
));
282 if ((int)opts
->mseconds_rest
< 0)
283 opts
->mseconds_rest
= 0;
284 if (opts
->nsamples
!= UINT_MAX
)
285 opts
->nsamples
*= opts
->nsets
;
288 cpc_setgrp_reset(opts
->master
);
289 (void) setvbuf(stdout
, NULL
, _IOLBF
, 0);
292 * If no system-mode only sets were created, no soaker threads will be
295 if (opts
->dosoaker
== 1 && cpc_setgrp_has_sysonly(opts
->master
) == 0)
300 (void) cpc_close(cpc
);
306 print_title(cpc_setgrp_t
*sgrp
)
308 (void) printf("%7s %3s %5s ", "time", "cpu", "event");
310 (void) printf("%9s ", CPC_TICKREG_NAME
);
311 (void) printf("%s\n", cpc_setgrp_gethdr(sgrp
));
315 print_sample(processorid_t cpuid
, cpc_buf_t
*buf
, int nreq
, const char *setname
,
325 hrtime
= cpc_buf_hrtime(cpc
, buf
);
326 tick
= cpc_buf_tick(cpc
, buf
);
328 ccnt
= snprintf(line
, sizeof (line
), "%7.3f %3d %5s ",
329 mstimestamp(hrtime
), (int)cpuid
, "tick");
331 ccnt
+= snprintf(line
+ ccnt
, sizeof (line
) - ccnt
,
332 "%9" PRId64
" ", tick
);
333 for (i
= 0; i
< nreq
; i
++) {
334 (void) cpc_buf_get(cpc
, buf
, i
, &val
);
335 ccnt
+= snprintf(line
+ ccnt
, sizeof (line
) - ccnt
,
336 "%9" PRId64
" ", val
);
339 ccnt
+= snprintf(line
+ ccnt
, sizeof (line
) - ccnt
,
342 ccnt
+= snprintf(line
+ ccnt
, sizeof (line
) - ccnt
, "\n");
346 * This sample is being printed for a "sibling" CPU -- that is,
347 * a CPU which does not have its own CPC set bound. It is being
348 * measured via a set bound to another CPU sharing its physical
351 int designee
= chip_designees
[gstate
[cpuid
].chip_id
];
354 if ((p
= strrchr(line
, '#')) == NULL
)
355 p
= strrchr(line
, '\n');
360 ccnt
+= snprintf(line
+ ccnt
, sizeof (line
) - ccnt
,
361 "# counter shared with CPU %d\n", designee
);
365 if (timestamp_fmt
!= NODATE
)
366 print_timestamp(timestamp_fmt
);
367 if (ccnt
> sizeof (line
))
368 ccnt
= sizeof (line
);
370 (void) write(1, line
, ccnt
);
373 * If this CPU is the chip designee for any other CPUs, print a line for
376 if (smt
&& (sibling
== 0)) {
377 for (i
= 0; i
< ncpus
; i
++) {
378 if ((i
!= cpuid
) && (gstate
[i
].cpuid
!= -1) &&
379 (chip_designees
[gstate
[i
].chip_id
] == cpuid
))
380 print_sample(i
, buf
, nreq
, setname
, 1);
386 print_total(int ncpus
, cpc_buf_t
*buf
, int nreq
, const char *setname
)
391 (void) printf("%7.3f %3d %5s ", mstimestamp(cpc_buf_hrtime(cpc
, buf
)),
394 (void) printf("%9" PRId64
" ", cpc_buf_tick(cpc
, buf
));
395 for (i
= 0; i
< nreq
; i
++) {
396 (void) cpc_buf_get(cpc
, buf
, i
, &val
);
397 (void) printf("%9" PRId64
" ", val
);
400 (void) printf(" # %s", setname
);
401 (void) fputc('\n', stdout
);
404 #define NSECS_PER_MSEC 1000000ll
405 #define NSECS_PER_SEC 1000000000ll
410 struct tstate
*state
= arg
;
413 uint_t sample_cnt
= 1;
414 hrtime_t ht
, htdelta
, restdelta
;
415 cpc_setgrp_t
*sgrp
= state
->sgrp
;
416 cpc_set_t
*this = cpc_setgrp_getset(sgrp
);
417 const char *name
= cpc_setgrp_getname(sgrp
);
418 cpc_buf_t
**data1
, **data2
, **scratch
;
423 htdelta
= NSECS_PER_MSEC
* opts
->mseconds
;
424 restdelta
= NSECS_PER_MSEC
* opts
->mseconds_rest
;
428 * If this CPU is SMT, we run one gtick() thread per _physical_ CPU,
429 * instead of per cpu_t. The following check returns if it detects that
430 * this cpu_t has not been designated to do the counting for this
433 if (smt
&& chip_designees
[state
->chip_id
] != state
->cpuid
)
437 * If we need to run a soaker thread on this CPU, start it here.
439 if (opts
->dosoaker
) {
440 if (cond_init(&state
->soak_cv
, USYNC_THREAD
, NULL
) != 0)
442 if (mutex_init(&state
->soak_lock
, USYNC_THREAD
,
445 (void) mutex_lock(&state
->soak_lock
);
446 state
->soak_state
= SOAK_PAUSE
;
447 if (thr_create(NULL
, 0, soaker
, state
, 0, &tid
) != 0)
450 while (state
->soak_state
== SOAK_PAUSE
)
451 (void) cond_wait(&state
->soak_cv
,
453 (void) mutex_unlock(&state
->soak_lock
);
456 * If the soaker needs to pause for the first set, stop it now.
458 if (cpc_setgrp_sysonly(sgrp
) == 0) {
459 (void) mutex_lock(&state
->soak_lock
);
460 state
->soak_state
= SOAK_PAUSE
;
461 (void) mutex_unlock(&state
->soak_lock
);
464 if (cpc_bind_cpu(cpc
, state
->cpuid
, this, 0) == -1)
467 for (nsamples
= opts
->nsamples
; nsamples
; nsamples
--, sample_cnt
++) {
471 nreqs
= cpc_setgrp_getbufs(sgrp
, &data1
, &data2
, &scratch
);
477 ts
.tv_sec
= (time_t)((ht
- htnow
) / NSECS_PER_SEC
);
478 ts
.tv_nsec
= (suseconds_t
)((ht
- htnow
) % NSECS_PER_SEC
);
480 (void) nanosleep(&ts
, NULL
);
482 if (opts
->nsets
== 1) {
484 * If we're dealing with one set, buffer usage is:
486 * data1 = most recent data snapshot
487 * data2 = previous data snapshot
488 * scratch = used for diffing data1 and data2
490 * Save the snapshot from the previous sample in data2
491 * before putting the current sample in data1.
496 if (cpc_set_sample(cpc
, this, *data1
) != 0)
498 cpc_buf_sub(cpc
, *scratch
, *data1
, *data2
);
500 print_sample(state
->cpuid
, *scratch
, nreqs
, name
, 0);
503 * More than one set is in use (multiple -c options
504 * given). Buffer usage in this case is:
506 * data1 = total counts for this set since program began
508 * scratch = most recent data snapshot
510 name
= cpc_setgrp_getname(sgrp
);
511 nreqs
= cpc_setgrp_getbufs(sgrp
, &data1
, &data2
,
514 if (cpc_set_sample(cpc
, this, *scratch
) != 0)
517 cpc_buf_add(cpc
, *data1
, *data1
, *scratch
);
519 if (cpc_unbind(cpc
, this) != 0)
520 (void) fprintf(stderr
, gettext("%s: error "
521 "unbinding on cpu %d - %s\n"),
522 opts
->pgmname
, state
->cpuid
,
525 this = cpc_setgrp_nextset(sgrp
);
527 print_sample(state
->cpuid
, *scratch
, nreqs
, name
, 0);
530 * If periodic behavior was requested, rest here.
532 if (opts
->doperiod
&& opts
->mseconds_rest
> 0 &&
533 (sample_cnt
% opts
->nsets
) == 0) {
535 * Stop the soaker while the tool rests.
537 if (opts
->dosoaker
) {
538 (void) mutex_lock(&state
->soak_lock
);
539 if (state
->soak_state
== SOAK_RUN
)
540 state
->soak_state
= SOAK_PAUSE
;
541 (void) mutex_unlock(&state
->soak_lock
);
546 ts
.tv_sec
= (time_t)((ht
- htnow
) /
548 ts
.tv_nsec
= (suseconds_t
)((ht
- htnow
) %
551 (void) nanosleep(&ts
, NULL
);
555 * Start or stop the soaker if needed.
557 if (opts
->dosoaker
) {
558 (void) mutex_lock(&state
->soak_lock
);
559 if (cpc_setgrp_sysonly(sgrp
) &&
560 state
->soak_state
== SOAK_PAUSE
) {
562 * Soaker is paused but the next set is
563 * sysonly: start the soaker.
565 state
->soak_state
= SOAK_RUN
;
566 (void) cond_signal(&state
->soak_cv
);
567 } else if (cpc_setgrp_sysonly(sgrp
) == 0 &&
568 state
->soak_state
== SOAK_RUN
)
570 * Soaker is running but the next set
571 * counts user events: stop the soaker.
573 state
->soak_state
= SOAK_PAUSE
;
574 (void) mutex_unlock(&state
->soak_lock
);
577 if (cpc_bind_cpu(cpc
, state
->cpuid
, this, 0) != 0)
582 if (cpc_unbind(cpc
, this) != 0)
583 (void) fprintf(stderr
, gettext("%s: error unbinding on"
584 " cpu %d - %s\n"), opts
->pgmname
,
585 state
->cpuid
, strerror(errno
));
588 * We're done, so stop the soaker if needed.
590 if (opts
->dosoaker
) {
591 (void) mutex_lock(&state
->soak_lock
);
592 if (state
->soak_state
== SOAK_RUN
)
593 state
->soak_state
= SOAK_PAUSE
;
594 (void) mutex_unlock(&state
->soak_lock
);
600 errstr
= strerror(errno
);
601 (void) fprintf(stderr
, gettext("%s: cpu%d - %s\n"),
602 opts
->pgmname
, state
->cpuid
, errstr
);
613 psetid_t mypset
, cpupset
;
615 cpc_buf_t
**data1
, **data2
, **scratch
;
619 ncpus
= (int)sysconf(_SC_NPROCESSORS_CONF
);
620 if ((gstate
= calloc(ncpus
, sizeof (*gstate
))) == NULL
) {
621 (void) fprintf(stderr
, gettext(
622 "%s: out of heap\n"), opts
->pgmname
);
626 max_chip_id
= sysconf(_SC_CPUID_MAX
);
627 if ((chip_designees
= malloc(max_chip_id
* sizeof (int))) == NULL
) {
628 (void) fprintf(stderr
, gettext(
629 "%s: out of heap\n"), opts
->pgmname
);
632 for (i
= 0; i
< max_chip_id
; i
++)
633 chip_designees
[i
] = -1;
636 if ((kc
= kstat_open()) == NULL
) {
637 (void) fprintf(stderr
, gettext(
638 "%s: kstat_open() failed: %s\n"), opts
->pgmname
,
645 if (priocntl(0, 0, PC_GETCID
, &fxinfo
) == -1) {
646 (void) fprintf(stderr
, gettext(
647 "%s: couldn't get FX scheduler class: %s\n"),
648 opts
->pgmname
, strerror(errno
));
653 * Only include processors that are participating in the system
655 for (c
= 0, i
= 0; i
< ncpus
; c
++) {
656 switch (p_online(c
, P_STATUS
)) {
661 gstate
[i
].chip_id
= get_chipid(kc
, c
);
662 if (gstate
[i
].chip_id
!= -1 &&
663 chip_designees
[gstate
[i
].chip_id
] == -1)
664 chip_designees
[gstate
[i
].chip_id
] = c
;
667 gstate
[i
++].cpuid
= c
;
673 gstate
[i
++].cpuid
= -1;
676 gstate
[i
++].cpuid
= -1;
677 (void) fprintf(stderr
,
678 gettext("%s: cpu%d in unknown state\n"),
687 * Examine the processor sets; if we're in one, only attempt
688 * to report on the set we're in.
690 if (pset_bind(PS_QUERY
, P_PID
, P_MYID
, &mypset
) == -1) {
691 errstr
= strerror(errno
);
692 (void) fprintf(stderr
, gettext("%s: pset_bind - %s\n"),
693 opts
->pgmname
, errstr
);
695 for (i
= 0; i
< ncpus
; i
++) {
696 struct tstate
*this = &gstate
[i
];
698 if (this->cpuid
== -1)
701 if (pset_assign(PS_QUERY
,
702 this->cpuid
, &cpupset
) == -1) {
703 errstr
= strerror(errno
);
704 (void) fprintf(stderr
,
705 gettext("%s: pset_assign - %s\n"),
706 opts
->pgmname
, errstr
);
710 if (mypset
!= cpupset
)
716 print_title(opts
->master
);
719 for (i
= 0; i
< ncpus
; i
++) {
720 struct tstate
*this = &gstate
[i
];
722 if (this->cpuid
== -1)
724 this->sgrp
= cpc_setgrp_clone(opts
->master
);
725 if (this->sgrp
== NULL
) {
729 if (thr_create(NULL
, 0, gtick
, this,
730 THR_BOUND
|THR_NEW_LWP
, &this->tid
) == 0)
733 (void) fprintf(stderr
,
734 gettext("%s: cannot create thread for cpu%d\n"),
735 opts
->pgmname
, this->cpuid
);
741 for (i
= 0; i
< ncpus
; i
++)
742 (void) thr_join(gstate
[i
].tid
, NULL
, NULL
);
744 if ((accum
= cpc_setgrp_clone(opts
->master
)) == NULL
) {
745 (void) fprintf(stderr
, gettext("%s: out of heap\n"),
751 for (i
= 0; i
< ncpus
; i
++) {
752 struct tstate
*this = &gstate
[i
];
754 if (this->cpuid
== -1)
756 cpc_setgrp_accum(accum
, this->sgrp
);
757 cpc_setgrp_free(this->sgrp
);
759 if (this->status
!= 0)
763 cpc_setgrp_reset(accum
);
764 start
= cpc_setgrp_getset(accum
);
766 nreqs
= cpc_setgrp_getbufs(accum
, &data1
, &data2
, &scratch
);
767 print_total(lwps
, *data1
, nreqs
, cpc_setgrp_getname(accum
));
768 } while (cpc_setgrp_nextset(accum
) != start
);
770 cpc_setgrp_free(accum
);
778 get_chipid(kstat_ctl_t
*kc
, processorid_t cpuid
)
783 if ((ksp
= kstat_lookup(kc
, "cpu_info", cpuid
, NULL
)) == NULL
)
786 if (kstat_read(kc
, ksp
, NULL
) == -1) {
787 (void) fprintf(stderr
,
788 gettext("%s: kstat_read() failed for cpu %d: %s\n"),
789 opts
->pgmname
, cpuid
, strerror(errno
));
793 if ((k
= (kstat_named_t
*)kstat_data_lookup(ksp
, "chip_id")) == NULL
) {
794 (void) fprintf(stderr
,
795 gettext("%s: chip_id not found for cpu %d: %s\n"),
796 opts
->pgmname
, cpuid
, strerror(errno
));
800 return (k
->value
.i32
);
806 struct tstate
*state
= arg
;
808 fxparms_t
*fx
= (fxparms_t
*)pcparms
.pc_clparms
;
810 if (processor_bind(P_LWPID
, P_MYID
, state
->cpuid
, NULL
) != 0)
811 (void) fprintf(stderr
, gettext("%s: couldn't bind soaker "
812 "thread to cpu%d: %s\n"), opts
->pgmname
, state
->cpuid
,
816 * Put the soaker thread in the fixed priority (FX) class so it runs
817 * at the lowest possible global priority.
819 pcparms
.pc_cid
= fxinfo
.pc_cid
;
822 fx
->fx_tqsecs
= fx
->fx_tqnsecs
= FX_TQDEF
;
824 if (priocntl(P_LWPID
, P_MYID
, PC_SETPARMS
, &pcparms
) != 0)
825 (void) fprintf(stderr
, gettext("%s: couldn't put soaker "
826 "thread in FX sched class: %s\n"), opts
->pgmname
,
830 * Let the parent thread know we're ready to roll.
832 (void) mutex_lock(&state
->soak_lock
);
833 state
->soak_state
= SOAK_RUN
;
834 (void) cond_signal(&state
->soak_cv
);
835 (void) mutex_unlock(&state
->soak_lock
);
839 (void) mutex_lock(&state
->soak_lock
);
840 if (state
->soak_state
== SOAK_RUN
) {
841 (void) mutex_unlock(&state
->soak_lock
);
845 while (state
->soak_state
== SOAK_PAUSE
)
846 (void) cond_wait(&state
->soak_cv
,
848 (void) mutex_unlock(&state
->soak_lock
);