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]
23 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
37 #include <sys/varargs.h>
38 #include <sys/trapstat.h>
42 #include "_trapstat.h"
44 #define TSTAT_DEVICE "/dev/trapstat"
45 #define TSTAT_COMMAND "trapstat"
46 #define TSTAT_DELTA(data, old, member) g_absolute ? (data)->member : \
47 (uint64_t)(0.5 + (g_interval / (double)((data)->tdata_snapts - \
48 (old)->tdata_snapts)) * (double)((data)->member - (old)->member))
50 #define TSTAT_PRINT_MISSDATA(diff, time) \
51 (void) printf(" %9lld %4.1f", (diff), (time));
53 #define TSTAT_PAGESIZE_MODIFIERS " kmgtp"
54 #define TSTAT_PAGESIZE_STRLEN 10
55 #define TSTAT_MAX_RATE 5000
56 #define TSTAT_COLUMN_OFFS 26
57 #define TSTAT_COLUMNS_PER_CPU 9
59 static tstat_data_t
*g_data
[2];
60 static tstat_data_t
*g_ndata
, *g_odata
;
61 static processorid_t g_max_cpus
;
62 static int8_t *g_selected
;
64 static int g_interval
= NANOSEC
;
65 static int g_peffect
= 1;
66 static int g_absolute
= 0;
67 static sigset_t g_oset
;
69 static psetid_t g_pset
= PS_NONE
;
70 static processorid_t
*g_pset_cpus
;
71 static uint_t g_pset_ncpus
;
73 static int g_cpus_per_line
= (80 - TSTAT_COLUMN_OFFS
) / TSTAT_COLUMNS_PER_CPU
;
77 static size_t *g_pgsize
;
78 static char **g_pgnames
;
79 static size_t g_datasize
;
83 static uint8_t g_active
[TSTAT_NENT
];
85 static hrtime_t g_start
;
87 static int g_exec_errno
;
88 static int g_child_exited
;
89 static int g_child_status
;
91 static void (*g_process
)(void *, uint64_t, double);
94 typedef struct tstat_sum
{
100 * Define a dummy g_traps reader to establish a symbol capabilities lead.
101 * This routine should never be called, as the sun4u and sun4v variants
102 * will be used as appropriate.
106 get_trap_ent(int ndx
)
114 (void) fprintf(stderr
,
115 "\nusage: trapstat [ -t | -T | -e entrylist ]\n"
116 " [ -C psrset | -c cpulist ]\n"
117 " [ -P ] [ -a ] [ -r rate ] [[ interval [ count ] ] | "
118 "command [ args ] ]\n\n"
119 "Trap selection options:\n\n"
120 " -t TLB statistics\n"
121 " -T TLB statistics, with pagesize information\n"
122 " -e entrylist Enable statistics only for entries specified "
124 "CPU selection options:\n\n"
125 " -c cpulist Enable statistics only for specified CPU list\n"
126 " -C psrset Enable statistics only for specified processor "
129 " -a Display trap values as accumulating values "
131 " -l List trap table entries and exit\n"
132 " -P Display output in parsable format\n"
133 " -r hz Set sampling rate to be hz samples "
140 fatal(char *fmt
, ...)
147 (void) fprintf(stderr
, TSTAT_COMMAND
": ");
148 (void) vfprintf(stderr
, fmt
, ap
);
150 if (fmt
[strlen(fmt
) - 1] != '\n')
151 (void) fprintf(stderr
, ": %s\n", strerror(error
));
161 if (!isatty(fileno(stdout
)))
164 if (ioctl(fileno(stdout
), TIOCGWINSZ
, &win
) == -1)
167 if (win
.ws_col
== 0) {
169 * If TIOCGWINSZ returned 0 for the columns, just return --
170 * thereby using the default value of g_cpus_per_line. (This
171 * happens, e.g., when running over a tip line.)
176 g_cpus_per_line
= (win
.ws_col
- TSTAT_COLUMN_OFFS
) /
177 TSTAT_COLUMNS_PER_CPU
;
179 if (g_cpus_per_line
< 1)
197 while (wait(&g_child_status
) == -1 && errno
== EINTR
)
211 struct sigaction act
;
216 for (i
= 0; i
< TSTAT_NENT
; i
++) {
219 if ((gtp
= get_trap_ent(i
)) == NULL
)
222 if (gtp
->tent_type
== TSTAT_ENT_RESERVED
)
223 gtp
->tent_name
= "reserved";
225 if (gtp
->tent_type
== TSTAT_ENT_UNUSED
)
226 gtp
->tent_name
= "unused";
229 g_max_cpus
= (processorid_t
)sysconf(_SC_CPUID_MAX
) + 1;
231 if ((g_selected
= malloc(sizeof (int8_t) * g_max_cpus
)) == NULL
)
232 fatal("could not allocate g_selected");
234 bzero(g_selected
, sizeof (int8_t) * g_max_cpus
);
236 g_pset_cpus
= malloc(sizeof (processorid_t
) * g_max_cpus
);
237 if (g_pset_cpus
== NULL
)
238 fatal("could not allocate g_pset_cpus");
240 bzero(g_pset_cpus
, sizeof (processorid_t
) * g_max_cpus
);
242 if ((g_pgsizes
= getpagesizes(NULL
, 0)) == -1)
243 fatal("getpagesizes()");
245 if ((g_pgsize
= malloc(sizeof (size_t) * g_pgsizes
)) == NULL
)
246 fatal("could not allocate g_pgsize array");
248 if (getpagesizes(g_pgsize
, g_pgsizes
) == -1)
249 fatal("getpagesizes(%d)", g_pgsizes
);
251 if ((g_pgnames
= malloc(sizeof (char *) * g_pgsizes
)) == NULL
)
252 fatal("could not allocate g_pgnames");
254 for (i
= 0; i
< g_pgsizes
; i
++) {
256 size_t sz
= g_pgsize
[i
];
258 if ((g_pgnames
[i
] = malloc(TSTAT_PAGESIZE_STRLEN
)) == NULL
)
259 fatal("could not allocate g_pgnames[%d]", i
);
261 for (j
= 0, mul
= 10; (1 << mul
) <= sz
; j
++, mul
+= 10)
264 (void) snprintf(g_pgnames
[i
], TSTAT_PAGESIZE_STRLEN
,
265 "%d%c", sz
>> (mul
- 10), " kmgtpe"[j
]);
269 sizeof (tstat_data_t
) + (g_pgsizes
- 1) * sizeof (tstat_pgszdata_t
);
271 if ((g_data
[0] = malloc(g_datasize
* g_max_cpus
)) == NULL
)
272 fatal("could not allocate data buffer 0");
274 if ((g_data
[1] = malloc(g_datasize
* g_max_cpus
)) == NULL
)
275 fatal("could not allocate data buffer 1");
277 (void) sigemptyset(&act
.sa_mask
);
279 act
.sa_handler
= intr
;
280 (void) sigaction(SIGUSR1
, &act
, NULL
);
281 (void) sigaction(SIGCHLD
, &act
, NULL
);
283 (void) sigaddset(&act
.sa_mask
, SIGCHLD
);
284 (void) sigaddset(&act
.sa_mask
, SIGUSR1
);
285 (void) sigaction(SIGWINCH
, &act
, NULL
);
288 (void) sigemptyset(&set
);
289 (void) sigaddset(&set
, SIGCHLD
);
290 (void) sigaddset(&set
, SIGUSR1
);
291 (void) sigaddset(&set
, SIGWINCH
);
292 (void) sigprocmask(SIG_BLOCK
, &set
, &g_oset
);
294 ev
.sigev_notify
= SIGEV_SIGNAL
;
295 ev
.sigev_signo
= SIGUSR1
;
297 if (timer_create(CLOCK_HIGHRES
, &ev
, &g_tid
) == -1)
298 fatal("cannot create CLOCK_HIGHRES timer");
302 set_interval(hrtime_t nsec
)
304 struct itimerspec ts
;
307 * If the interval is less than one second, we'll report the
308 * numbers in terms of rate-per-interval. If the interval is
309 * greater than one second, we'll report numbers in terms of
312 g_interval
= nsec
< NANOSEC
? nsec
: NANOSEC
;
314 ts
.it_value
.tv_sec
= nsec
/ NANOSEC
;
315 ts
.it_value
.tv_nsec
= nsec
% NANOSEC
;
316 ts
.it_interval
.tv_sec
= nsec
/ NANOSEC
;
317 ts
.it_interval
.tv_nsec
= nsec
% NANOSEC
;
319 if (timer_settime(g_tid
, TIMER_RELTIME
, &ts
, NULL
) == -1)
320 fatal("cannot set time on CLOCK_HIGHRES timer");
324 print_entries(FILE *stream
, int parsable
)
329 (void) fprintf(stream
, " %3s %3s | %-20s | %s\n", "hex",
330 "dec", "entry name", "description");
332 (void) fprintf(stream
, "----------+----------------------"
333 "+-----------------------\n");
336 for (entno
= 0; entno
< TSTAT_NENT
; entno
++) {
339 if ((gtp
= get_trap_ent(entno
)) == NULL
)
342 if (gtp
->tent_type
!= TSTAT_ENT_USED
)
345 (void) fprintf(stream
, "0x%03x %3d %s%-20s %s%s\n",
347 parsable
? "" : "| ", gtp
->tent_name
,
348 parsable
? "" : "| ", gtp
->tent_descr
);
353 select_entry(char *entry
)
359 * The entry may be specified as a number (e.g., "0x68", "104") or
360 * as a name ("dtlb-miss").
362 entno
= strtoul(entry
, &end
, 0);
365 if (entno
>= TSTAT_NENT
)
368 for (entno
= 0; entno
< TSTAT_NENT
; entno
++) {
371 if ((gtp
= get_trap_ent(entno
)) == NULL
)
374 if (gtp
->tent_type
!= TSTAT_ENT_USED
)
377 if (strcmp(entry
, gtp
->tent_name
) == 0)
381 if (entno
== TSTAT_NENT
)
385 if (ioctl(g_fd
, TSTATIOC_ENTRY
, entno
) == -1)
386 fatal("TSTATIOC_ENTRY failed for entry 0x%x", entno
);
392 (void) fprintf(stderr
, TSTAT_COMMAND
": invalid entry '%s'", entry
);
393 (void) fprintf(stderr
, "; valid entries:\n\n");
394 print_entries(stderr
, 0);
399 select_cpu(processorid_t cpu
)
401 if (g_pset
!= PS_NONE
)
402 fatal("cannot specify both a processor set and a processor\n");
404 if (cpu
< 0 || cpu
>= g_max_cpus
)
405 fatal("cpu %d out of range\n", cpu
);
407 if (p_online(cpu
, P_STATUS
) == -1) {
409 fatal("could not get status for cpu %d", cpu
);
410 fatal("cpu %d not present\n", cpu
);
417 select_cpus(processorid_t low
, processorid_t high
)
419 if (g_pset
!= PS_NONE
)
420 fatal("cannot specify both a processor set and processors\n");
422 if (low
< 0 || low
>= g_max_cpus
)
423 fatal("invalid cpu '%d'\n", low
);
425 if (high
< 0 || high
>= g_max_cpus
)
426 fatal("invalid cpu '%d'\n", high
);
429 fatal("invalid range '%d' to '%d'\n", low
, high
);
432 if (p_online(low
, P_STATUS
) != -1)
434 } while (++low
<= high
);
438 select_pset(psetid_t pset
)
443 fatal("processor set %d is out of range\n", pset
);
446 * Only one processor set can be specified.
448 if (g_pset
!= PS_NONE
)
449 fatal("at most one processor set may be specified\n");
452 * One cannot select processors _and_ a processor set.
454 for (i
= 0; i
< g_max_cpus
; i
++)
459 fatal("cannot specify both a processor and a processor set\n");
462 g_pset_ncpus
= g_max_cpus
;
464 if (pset_info(g_pset
, NULL
, &g_pset_ncpus
, g_pset_cpus
) == -1)
465 fatal("invalid processor set: %d\n", g_pset
);
467 if (g_pset_ncpus
== 0)
468 fatal("processor set %d empty\n", g_pset
);
470 if (ioctl(g_fd
, TSTATIOC_NOCPU
) == -1)
471 fatal("TSTATIOC_NOCPU failed");
473 for (i
= 0; i
< g_pset_ncpus
; i
++)
474 g_selected
[g_pset_cpus
[i
]] = 1;
480 uint_t ncpus
= g_max_cpus
;
483 if (g_pset
== PS_NONE
)
486 if (pset_info(g_pset
, NULL
, &ncpus
, g_pset_cpus
) == -1) {
488 fatal("processor set %d destroyed\n", g_pset
);
490 fatal("couldn't get info for processor set %d", g_pset
);
494 fatal("processor set %d empty\n", g_pset
);
496 if (ncpus
== g_pset_ncpus
) {
497 for (i
= 0; i
< g_pset_ncpus
; i
++) {
498 if (!g_selected
[g_pset_cpus
[i
]])
503 * If the number of CPUs hasn't changed, and every CPU
504 * in the processor set is also selected, we know that the
505 * processor set itself hasn't changed.
507 if (i
== g_pset_ncpus
)
512 * If we're here, we have a new processor set. First, we need
513 * to zero out the selection array.
515 bzero(g_selected
, sizeof (int8_t) * g_max_cpus
);
517 g_pset_ncpus
= ncpus
;
519 if (ioctl(g_fd
, TSTATIOC_STOP
) == -1)
520 fatal("TSTATIOC_STOP failed");
522 if (ioctl(g_fd
, TSTATIOC_NOCPU
) == -1)
523 fatal("TSATIOC_NOCPU failed");
525 for (i
= 0; i
< g_pset_ncpus
; i
++) {
526 g_selected
[g_pset_cpus
[i
]] = 1;
527 if (ioctl(g_fd
, TSTATIOC_CPU
, g_pset_cpus
[i
]) == -1)
528 fatal("TSTATIOC_CPU failed for cpu %d", i
);
532 * Now that we have selected the CPUs, we're going to reenable
533 * trapstat, and reread the data for the current generation.
535 if (ioctl(g_fd
, TSTATIOC_GO
) == -1)
536 fatal("TSTATIOC_GO failed");
538 if (ioctl(g_fd
, TSTATIOC_READ
, g_data
[g_gen
]) == -1)
539 fatal("TSTATIOC_READ failed");
543 missdata(tstat_missdata_t
*miss
, tstat_missdata_t
*omiss
)
545 hrtime_t ts
= g_ndata
->tdata_snapts
- g_odata
->tdata_snapts
;
546 hrtime_t tick
= g_ndata
->tdata_snaptick
- g_odata
->tdata_snaptick
;
547 uint64_t raw
= miss
->tmiss_count
- omiss
->tmiss_count
;
548 uint64_t diff
= g_absolute
? miss
->tmiss_count
:
549 (uint64_t)(0.5 + g_interval
/
550 (double)ts
* (double)(miss
->tmiss_count
- omiss
->tmiss_count
));
551 hrtime_t peffect
= raw
* g_ndata
->tdata_peffect
* g_peffect
, time
;
555 * Now we need to account for the trapstat probe effect. Take
556 * the amount of time spent in the handler, and add the
557 * amount of time known to be due to the trapstat probe effect.
559 time
= miss
->tmiss_time
- omiss
->tmiss_time
+ peffect
;
563 * This really shouldn't happen unless our calculation of
564 * the probe effect was vastly incorrect. In any case,
565 * print 99.9 for the time instead of printing negative
568 time
= tick
/ 1000 * 999;
571 p
= (double)time
/ (double)tick
* (double)100.0;
573 (*g_process
)(g_arg
, diff
, p
);
577 tlbdata(tstat_tlbdata_t
*tlb
, tstat_tlbdata_t
*otlb
)
579 missdata(&tlb
->ttlb_tlb
, &otlb
->ttlb_tlb
);
580 missdata(&tlb
->ttlb_tsb
, &otlb
->ttlb_tsb
);
584 print_missdata(double *ttl
, uint64_t diff
, double p
)
586 TSTAT_PRINT_MISSDATA(diff
, p
);
593 print_modepgsz(char *prefix
, tstat_modedata_t
*data
, tstat_modedata_t
*odata
)
596 size_t incr
= sizeof (tstat_pgszdata_t
);
598 for (ps
= 0; ps
< g_pgsizes
; ps
++) {
601 g_process
= (void(*)(void *, uint64_t, double))print_missdata
;
604 (void) printf("%s %4s|", prefix
, g_pgnames
[ps
]);
605 tlbdata(&data
->tmode_itlb
, &odata
->tmode_itlb
);
607 tlbdata(&data
->tmode_dtlb
, &odata
->tmode_dtlb
);
609 (void) printf(" |%4.1f\n", ttl
);
611 data
= (tstat_modedata_t
*)((uintptr_t)data
+ incr
);
612 odata
= (tstat_modedata_t
*)((uintptr_t)odata
+ incr
);
617 parsable_modepgsz(char *prefix
, tstat_modedata_t
*data
, tstat_modedata_t
*odata
)
620 size_t incr
= sizeof (tstat_pgszdata_t
);
622 g_process
= (void(*)(void *, uint64_t, double))print_missdata
;
625 for (ps
= 0; ps
< g_pgsizes
; ps
++) {
626 (void) printf("%s %7d", prefix
, g_pgsize
[ps
]);
627 tlbdata(&data
->tmode_itlb
, &odata
->tmode_itlb
);
628 tlbdata(&data
->tmode_dtlb
, &odata
->tmode_dtlb
);
631 data
= (tstat_modedata_t
*)((uintptr_t)data
+ incr
);
632 odata
= (tstat_modedata_t
*)((uintptr_t)odata
+ incr
);
637 sum_missdata(void *sump
, uint64_t diff
, double p
)
639 tstat_sum_t
*sum
= *((tstat_sum_t
**)sump
);
641 sum
->tsum_diff
+= diff
;
644 (*(tstat_sum_t
**)sump
)++;
648 sum_modedata(tstat_modedata_t
*data
, tstat_modedata_t
*odata
, tstat_sum_t
*sum
)
650 int ps
, incr
= sizeof (tstat_pgszdata_t
);
653 for (ps
= 0; ps
< g_pgsizes
; ps
++) {
656 g_process
= sum_missdata
;
659 tlbdata(&data
->tmode_itlb
, &odata
->tmode_itlb
);
660 tlbdata(&data
->tmode_dtlb
, &odata
->tmode_dtlb
);
662 data
= (tstat_modedata_t
*)((uintptr_t)data
+ incr
);
663 odata
= (tstat_modedata_t
*)((uintptr_t)odata
+ incr
);
668 print_sum(tstat_sum_t
*sum
, int divisor
)
673 for (i
= 0; i
< 4; i
++) {
677 sum
[i
].tsum_time
/= divisor
;
679 TSTAT_PRINT_MISSDATA(sum
[i
].tsum_diff
, sum
[i
].tsum_time
);
680 ttl
+= sum
[i
].tsum_time
;
683 (void) printf(" |%4.1f\n", ttl
);
687 print_tlbpgsz(tstat_data_t
*data
, tstat_data_t
*odata
)
689 int i
, cpu
, ncpus
= 0;
693 (void) printf("cpu m size| %9s %4s %9s %4s | %9s %4s %9s %4s |%4s\n"
694 "----------+-------------------------------+-----------------------"
695 "--------+----\n", "itlb-miss", "%tim", "itsb-miss", "%tim",
696 "dtlb-miss", "%tim", "dtsb-miss", "%tim", "%tim");
698 bzero(sum
, sizeof (sum
));
700 for (i
= 0; i
< g_max_cpus
; i
++) {
701 tstat_pgszdata_t
*pgsz
= data
->tdata_pgsz
;
702 tstat_pgszdata_t
*opgsz
= odata
->tdata_pgsz
;
704 if ((cpu
= data
->tdata_cpuid
) == -1)
708 (void) printf("----------+-----------------------------"
709 "--+-------------------------------+----\n");
714 (void) sprintf(pre
, "%3d u", cpu
);
715 print_modepgsz(pre
, &pgsz
->tpgsz_user
, &opgsz
->tpgsz_user
);
716 sum_modedata(&pgsz
->tpgsz_user
, &opgsz
->tpgsz_user
, sum
);
718 (void) printf("- - - - - + - - - - - - - - - - - - - -"
719 " - + - - - - - - - - - - - - - - - + - -\n");
721 (void) sprintf(pre
, "%3d k", cpu
);
722 print_modepgsz(pre
, &pgsz
->tpgsz_kernel
, &opgsz
->tpgsz_kernel
);
723 sum_modedata(&pgsz
->tpgsz_kernel
, &opgsz
->tpgsz_kernel
, sum
);
725 data
= (tstat_data_t
*)((uintptr_t)data
+ g_datasize
);
726 odata
= (tstat_data_t
*)((uintptr_t)odata
+ g_datasize
);
730 (void) printf("==========+===============================+========="
731 "======================+====\n");
732 (void) printf(" ttl |");
733 print_sum(sum
, ncpus
);
738 parsable_tlbpgsz(tstat_data_t
*data
, tstat_data_t
*odata
)
743 for (i
= 0; i
< g_max_cpus
; i
++) {
744 tstat_pgszdata_t
*pgsz
= data
->tdata_pgsz
;
745 tstat_pgszdata_t
*opgsz
= odata
->tdata_pgsz
;
747 if ((cpu
= data
->tdata_cpuid
) == -1)
753 (void) sprintf(pre
, "%lld %3d u",
754 data
->tdata_snapts
- g_start
, cpu
);
755 parsable_modepgsz(pre
, &pgsz
->tpgsz_user
, &opgsz
->tpgsz_user
);
757 pre
[strlen(pre
) - 1] = 'k';
758 parsable_modepgsz(pre
, &pgsz
->tpgsz_kernel
,
759 &opgsz
->tpgsz_kernel
);
761 data
= (tstat_data_t
*)((uintptr_t)data
+ g_datasize
);
762 odata
= (tstat_data_t
*)((uintptr_t)odata
+ g_datasize
);
767 print_modedata(tstat_modedata_t
*data
, tstat_modedata_t
*odata
, int parsable
)
770 size_t incr
= sizeof (tstat_pgszdata_t
);
771 tstat_sum_t sum
[4], *sump
= sum
;
774 bzero(sum
, sizeof (sum
));
775 g_process
= sum_missdata
;
778 for (ps
= 0; ps
< g_pgsizes
; ps
++) {
779 tlbdata(&data
->tmode_itlb
, &odata
->tmode_itlb
);
780 tlbdata(&data
->tmode_dtlb
, &odata
->tmode_dtlb
);
782 data
= (tstat_modedata_t
*)((uintptr_t)data
+ incr
);
783 odata
= (tstat_modedata_t
*)((uintptr_t)odata
+ incr
);
787 for (i
= 0; i
< 4; i
++) {
788 if (i
== 2 && !parsable
)
791 TSTAT_PRINT_MISSDATA(sum
[i
].tsum_diff
, sum
[i
].tsum_time
);
792 ttl
+= sum
[i
].tsum_time
;
800 (void) printf(" |%4.1f\n", ttl
);
804 print_tlb(tstat_data_t
*data
, tstat_data_t
*odata
)
806 int i
, cpu
, ncpus
= 0;
809 (void) printf("cpu m| %9s %4s %9s %4s | %9s %4s %9s %4s |%4s\n"
810 "-----+-------------------------------+-----------------------"
811 "--------+----\n", "itlb-miss", "%tim", "itsb-miss", "%tim",
812 "dtlb-miss", "%tim", "dtsb-miss", "%tim", "%tim");
814 bzero(sum
, sizeof (sum
));
816 for (i
= 0; i
< g_max_cpus
; i
++) {
817 tstat_pgszdata_t
*pgsz
= data
->tdata_pgsz
;
818 tstat_pgszdata_t
*opgsz
= odata
->tdata_pgsz
;
820 if ((cpu
= data
->tdata_cpuid
) == -1)
824 (void) printf("-----+-------------------------------+-"
825 "------------------------------+----\n");
830 (void) printf("%3d u|", cpu
);
831 print_modedata(&pgsz
->tpgsz_user
, &opgsz
->tpgsz_user
, 0);
832 sum_modedata(&pgsz
->tpgsz_user
, &opgsz
->tpgsz_user
, sum
);
834 (void) printf("%3d k|", cpu
);
835 print_modedata(&pgsz
->tpgsz_kernel
, &opgsz
->tpgsz_kernel
, 0);
836 sum_modedata(&pgsz
->tpgsz_kernel
, &opgsz
->tpgsz_kernel
, sum
);
838 data
= (tstat_data_t
*)((uintptr_t)data
+ g_datasize
);
839 odata
= (tstat_data_t
*)((uintptr_t)odata
+ g_datasize
);
843 (void) printf("=====+===============================+========="
844 "======================+====\n");
846 (void) printf(" ttl |");
847 print_sum(sum
, ncpus
);
852 parsable_tlb(tstat_data_t
*data
, tstat_data_t
*odata
)
856 for (i
= 0; i
< g_max_cpus
; i
++) {
857 tstat_pgszdata_t
*pgsz
= data
->tdata_pgsz
;
858 tstat_pgszdata_t
*opgsz
= odata
->tdata_pgsz
;
860 if ((cpu
= data
->tdata_cpuid
) == -1)
866 (void) printf("%lld %3d u ", data
->tdata_snapts
- g_start
, cpu
);
867 print_modedata(&pgsz
->tpgsz_user
, &opgsz
->tpgsz_user
, 1);
868 (void) printf("%lld %3d k ", data
->tdata_snapts
- g_start
, cpu
);
869 print_modedata(&pgsz
->tpgsz_kernel
, &opgsz
->tpgsz_kernel
, 1);
871 data
= (tstat_data_t
*)((uintptr_t)data
+ g_datasize
);
872 odata
= (tstat_data_t
*)((uintptr_t)odata
+ g_datasize
);
877 print_stats(tstat_data_t
*data
, tstat_data_t
*odata
)
881 tstat_data_t
*base
= data
;
884 * First, blast through all of the data updating our array
885 * of active traps. We keep an array of active traps to prevent
886 * printing lines for traps that are never seen -- while still printing
887 * lines for traps that have been seen only once on some CPU.
889 for (i
= 0; i
< g_max_cpus
; i
++) {
890 if (data
[i
].tdata_cpuid
== -1)
893 for (j
= 0; j
< TSTAT_NENT
; j
++) {
894 if (!data
[i
].tdata_traps
[j
] || g_active
[j
])
903 for (done
= 0; !done
; data
+= g_cpus_per_line
) {
904 for (i
= 0; i
< g_cpus_per_line
; i
++) {
905 if (&data
[i
] - base
>= g_max_cpus
)
908 if ((id
= data
[i
].tdata_cpuid
) == -1)
912 (void) printf("vct name |");
914 (void) printf(" %scpu%d", id
>= 100 ? "" :
915 id
>= 10 ? " " : " ", id
);
921 if (i
!= g_cpus_per_line
)
924 (void) printf("\n------------------------+");
926 for (j
= 0; j
< i
; j
++)
927 (void) printf("---------");
930 for (j
= 0; j
< TSTAT_NENT
; j
++) {
933 if ((!g_active
[j
]) || ((gtp
= get_trap_ent(j
)) == NULL
))
936 (void) printf("%3x %-20s|", j
, gtp
->tent_name
);
937 for (k
= 0; k
< i
; k
++) {
938 (void) printf(" %8lld", TSTAT_DELTA(&data
[k
],
939 &odata
[data
- base
+ k
], tdata_traps
[j
]));
948 parsable_stats(tstat_data_t
*data
, tstat_data_t
*odata
)
953 for (base
= data
; data
- base
< g_max_cpus
; data
++, odata
++) {
954 if (data
->tdata_cpuid
== -1)
957 for (i
= 0; i
< TSTAT_NENT
; i
++) {
960 if ((!data
->tdata_traps
[i
] && !g_active
[i
]) ||
961 ((gtp
= get_trap_ent(i
)) == NULL
))
964 (void) printf("%lld %d %x %s ",
965 data
->tdata_snapts
- g_start
, data
->tdata_cpuid
, i
,
968 (void) printf("%lld\n", TSTAT_DELTA(data
, odata
,
975 check_data(tstat_data_t
*data
, tstat_data_t
*odata
)
980 if (data
->tdata_cpuid
== -1) {
982 * The last CPU we were watching must have been DR'd out
983 * of the system. Print a vaguely useful message and exit.
985 fatal("all initially selected CPUs have been unconfigured\n");
989 * If a CPU is DR'd out of the system, we'll stop receiving data
990 * for it. CPUs are never added, however (that is, if a CPU is
991 * DR'd into the system, we won't automatically start receiving
992 * data for it). We check for this by making sure that all of
993 * the CPUs present in the old data are present in the new data.
994 * If we find one missing in the new data, we correct the old data
995 * by removing the old CPU. This assures that delta are printed
998 for (i
= 0; i
< g_max_cpus
; i
++) {
999 if (odata
->tdata_cpuid
== -1)
1002 if (data
->tdata_cpuid
!= odata
->tdata_cpuid
)
1005 data
= (tstat_data_t
*)((uintptr_t)data
+ g_datasize
);
1006 odata
= (tstat_data_t
*)((uintptr_t)odata
+ g_datasize
);
1009 if (i
== g_max_cpus
)
1013 * If we're here, we know that the odata is a CPU which has been
1014 * DR'd out. We'll now smoosh it out of the old data.
1016 for (odata
->tdata_cpuid
= -1; i
< g_max_cpus
- 1; i
++) {
1017 ndata
= (tstat_data_t
*)((uintptr_t)odata
+ g_datasize
);
1018 bcopy(ndata
, odata
, g_datasize
);
1019 ndata
->tdata_cpuid
= -1;
1023 * There may be other CPUs DR'd out; tail-call recurse.
1025 check_data(data
, odata
);
1029 main(int argc
, char **argv
)
1034 long count
= 0, rate
= 0;
1035 int list
= 0, parsable
= 0;
1036 void (*print
)(tstat_data_t
*, tstat_data_t
*);
1041 void (*print
)(tstat_data_t
*, tstat_data_t
*);
1042 void (*parsable
)(tstat_data_t
*, tstat_data_t
*);
1045 { '\0', print_stats
, parsable_stats
, 0 },
1046 { 'e', print_stats
, parsable_stats
, 1 },
1047 { 't', print_tlb
, parsable_tlb
, 0 },
1048 { 'T', print_tlbpgsz
, parsable_tlbpgsz
, 0 },
1049 { -1, NULL
, NULL
, 0 }
1050 }, *tabent
= NULL
, *iter
;
1052 uintptr_t offs
= (uintptr_t)&tab
->print
- (uintptr_t)tab
;
1055 * If argv[0] is non-NULL, set argv[0] to keep any getopt(3C) output
1056 * consistent with other error output.
1058 if (argv
[0] != NULL
)
1059 argv
[0] = TSTAT_COMMAND
;
1061 if ((g_fd
= open(TSTAT_DEVICE
, O_RDWR
)) == -1)
1062 fatal("couldn't open " TSTAT_DEVICE
);
1066 while ((c
= getopt(argc
, argv
, "alnNtTc:C:r:e:P")) != EOF
) {
1068 * First, check to see if this option changes our printing
1071 for (iter
= tab
; iter
->opt
>= 0; iter
++) {
1075 if (tabent
!= NULL
) {
1076 if (tabent
== iter
) {
1077 if (tabent
->repeat
) {
1079 * This option is allowed to
1080 * have repeats; break out.
1085 fatal("expected -%c at most once\n", c
);
1088 fatal("only one of -%c, -%c expected\n",
1102 char *s
= strtok(optarg
, ",");
1106 s
= strtok(NULL
, ",");
1118 * This undocumented option prevents trapstat from
1119 * actually switching the %tba to point to the
1120 * interposing trap table. It's very useful when
1121 * debugging trapstat bugs: one can specify "-n"
1122 * and then examine the would-be interposing trap
1123 * table without running the risk of RED stating.
1125 if (ioctl(g_fd
, TSTATIOC_NOGO
) == -1)
1126 fatal("TSTATIOC_NOGO");
1131 * This undocumented option forces trapstat to ignore
1132 * its determined probe effect. This may be useful
1133 * if it is believed that the probe effect has been
1134 * grossly overestimated.
1142 * When running with TLB statistics, we want to
1143 * minimize probe effect by running with all other
1144 * entries explicitly disabled.
1146 if (ioctl(g_fd
, TSTATIOC_NOENTRY
) == -1)
1147 fatal("TSTATIOC_NOENTRY");
1149 if (ioctl(g_fd
, TSTATIOC_TLBDATA
) == -1)
1150 fatal("TSTATIOC_TLBDATA");
1155 * We allow CPUs to be specified as an optionally
1156 * comma separated list of either CPU IDs or ranges
1159 char *s
= strtok(optarg
, ",");
1162 id
= strtoul(s
, &end
, 0);
1164 if (id
== ULONG_MAX
&& errno
== ERANGE
) {
1166 fatal("invalid cpu '%s'\n", s
);
1169 if (*(s
= end
) != '\0') {
1173 fatal("invalid cpu '%s'\n", s
);
1174 p
= strtoul(++s
, &end
, 0);
1177 (p
== ULONG_MAX
&& errno
== ERANGE
))
1178 fatal("invalid cpu '%s'\n", s
);
1185 s
= strtok(NULL
, ",");
1192 psetid_t pset
= strtoul(optarg
, &end
, 0);
1195 (pset
== ULONG_MAX
&& errno
== ERANGE
))
1196 fatal("invalid processor set '%s'\n", optarg
);
1203 rate
= strtol(optarg
, &end
, 0);
1206 (rate
== LONG_MAX
&& errno
== ERANGE
))
1207 fatal("invalid rate '%s'\n", optarg
);
1210 fatal("rate must be greater than zero\n");
1212 if (rate
> TSTAT_MAX_RATE
)
1213 fatal("rate may not exceed %d\n",
1216 set_interval(NANOSEC
/ rate
);
1221 offs
= (uintptr_t)&tab
->parsable
- (uintptr_t)tab
;
1231 print_entries(stdout
, parsable
);
1235 if (optind
!= argc
) {
1237 int interval
= strtol(argv
[optind
], &end
, 0);
1241 * That wasn't a valid number. It must be that we're
1242 * to execute this command.
1247 (void) sigprocmask(SIG_SETMASK
, &g_oset
, NULL
);
1248 (void) execvp(argv
[optind
], &argv
[optind
]);
1251 * No luck. Set errno.
1253 g_exec_errno
= errno
;
1254 _exit(EXIT_FAILURE
);
1257 fatal("cannot fork");
1264 fatal("interval must be greater than zero.\n");
1266 if (interval
== LONG_MAX
&& errno
== ERANGE
)
1267 fatal("invalid interval '%s'\n", argv
[optind
]);
1269 set_interval(NANOSEC
* (hrtime_t
)interval
);
1271 if (++optind
!= argc
) {
1272 char *s
= argv
[optind
];
1274 count
= strtol(s
, &end
, 0);
1276 if (*end
!= '\0' || count
<= 0 ||
1277 (count
== LONG_MAX
&& errno
== ERANGE
))
1278 fatal("invalid count '%s'\n", s
);
1283 set_interval(NANOSEC
);
1289 print
= *(void(**)(tstat_data_t
*, tstat_data_t
*))
1290 ((uintptr_t)tabent
+ offs
);
1292 for (id
= 0; id
< g_max_cpus
; id
++) {
1293 if (!g_selected
[id
])
1296 if (ioctl(g_fd
, TSTATIOC_CPU
, id
) == -1)
1297 fatal("TSTATIOC_CPU failed for cpu %d", id
);
1300 g_start
= gethrtime();
1302 if (ioctl(g_fd
, TSTATIOC_GO
) == -1)
1303 fatal("TSTATIOC_GO failed");
1305 if (ioctl(g_fd
, TSTATIOC_READ
, g_data
[g_gen
^ 1]) == -1)
1306 fatal("initial TSTATIOC_READ failed");
1308 (void) sigemptyset(&set
);
1310 for (indefinite
= (count
== 0); indefinite
|| count
; count
--) {
1312 (void) sigsuspend(&set
);
1319 if (g_child_exited
&& g_exec_errno
!= 0) {
1320 errno
= g_exec_errno
;
1321 fatal("could not execute %s", argv
[optind
]);
1324 if (ioctl(g_fd
, TSTATIOC_READ
, g_data
[g_gen
]) == -1)
1325 fatal("TSTATIOC_READ failed");
1328 * Before we blithely print the data, we need to
1329 * make sure that we haven't lost a CPU.
1331 check_data(g_data
[g_gen
], g_data
[g_gen
^ 1]);
1332 (*print
)(g_data
[g_gen
], g_data
[g_gen
^ 1]);
1333 (void) fflush(stdout
);
1335 if (g_child_exited
) {
1336 if (WIFEXITED(g_child_status
)) {
1337 if (WEXITSTATUS(g_child_status
) == 0)
1340 (void) fprintf(stderr
, TSTAT_COMMAND
": "
1341 "warning: %s exited with code %d\n",
1342 argv
[optind
], WEXITSTATUS(g_child_status
));
1344 (void) fprintf(stderr
, TSTAT_COMMAND
": "
1345 "warning: %s died on signal %d\n",
1346 argv
[optind
], WTERMSIG(g_child_status
));