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 /*
23 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <signal.h>
33 #include <strings.h>
34 #include <limits.h>
35 #include <sys/mman.h>
36 #include <sys/pset.h>
37 #include <sys/varargs.h>
38 #include <sys/trapstat.h>
39 #include <sys/wait.h>
40 #include <stddef.h>
41 #include <termio.h>
42 #include "_trapstat.h"
43
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))
49
50 #define TSTAT_PRINT_MISSDATA(diff, time) \
51 (void) printf(" %9lld %4.1f", (diff), (time));
52
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
58
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;
63 static timer_t g_tid;
64 static int g_interval = NANOSEC;
65 static int g_peffect = 1;
66 static int g_absolute = 0;
67 static sigset_t g_oset;
68
69 static psetid_t g_pset = PS_NONE;
70 static processorid_t *g_pset_cpus;
71 static uint_t g_pset_ncpus;
72
73 static int g_cpus_per_line = (80 - TSTAT_COLUMN_OFFS) / TSTAT_COLUMNS_PER_CPU;
74 static int g_winch;
75
76 static int g_pgsizes;
77 static size_t *g_pgsize;
78 static char **g_pgnames;
79 static size_t g_datasize;
80
81 static int g_gen;
82 static int g_fd;
83 static uint8_t g_active[TSTAT_NENT];
84
85 static hrtime_t g_start;
86
87 static int g_exec_errno;
88 static int g_child_exited;
89 static int g_child_status;
90
91 static void (*g_process)(void *, uint64_t, double);
92 static void *g_arg;
93
94 typedef struct tstat_sum {
95 uint64_t tsum_diff;
96 double tsum_time;
97 } tstat_sum_t;
98
99 /*
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.
103 */
104 /* ARGSUSED0 */
105 tstat_ent_t *
get_trap_ent(int ndx)106 get_trap_ent(int ndx)
107 {
108 return (NULL);
109 }
110
111 static void
usage(void)112 usage(void)
113 {
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 "
123 "by entrylist\n\n"
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 "
127 "set\n\n"
128 "Other options:\n\n"
129 " -a Display trap values as accumulating values "
130 "instead of rates\n"
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 "
134 "per second\n\n");
135
136 exit(EXIT_FAILURE);
137 }
138
139 static void
fatal(char * fmt,...)140 fatal(char *fmt, ...)
141 {
142 va_list ap;
143 int error = errno;
144
145 va_start(ap, fmt);
146
147 (void) fprintf(stderr, TSTAT_COMMAND ": ");
148 (void) vfprintf(stderr, fmt, ap);
149
150 if (fmt[strlen(fmt) - 1] != '\n')
151 (void) fprintf(stderr, ": %s\n", strerror(error));
152
153 exit(EXIT_FAILURE);
154 }
155
156 static void
set_width(void)157 set_width(void)
158 {
159 struct winsize win;
160
161 if (!isatty(fileno(stdout)))
162 return;
163
164 if (ioctl(fileno(stdout), TIOCGWINSZ, &win) == -1)
165 return;
166
167 if (win.ws_col == 0) {
168 /*
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.)
172 */
173 return;
174 }
175
176 g_cpus_per_line = (win.ws_col - TSTAT_COLUMN_OFFS) /
177 TSTAT_COLUMNS_PER_CPU;
178
179 if (g_cpus_per_line < 1)
180 g_cpus_per_line = 1;
181 }
182
183 static void
intr(int signo)184 intr(int signo)
185 {
186 int error = errno;
187
188 switch (signo) {
189 case SIGWINCH:
190 g_winch = 1;
191 set_width();
192 break;
193
194 case SIGCHLD:
195 g_child_exited = 1;
196
197 while (wait(&g_child_status) == -1 && errno == EINTR)
198 continue;
199 break;
200
201 default:
202 break;
203 }
204
205 errno = error;
206 }
207
208 static void
setup(void)209 setup(void)
210 {
211 struct sigaction act;
212 struct sigevent ev;
213 sigset_t set;
214 int i;
215
216 for (i = 0; i < TSTAT_NENT; i++) {
217 tstat_ent_t *gtp;
218
219 if ((gtp = get_trap_ent(i)) == NULL)
220 continue;
221
222 if (gtp->tent_type == TSTAT_ENT_RESERVED)
223 gtp->tent_name = "reserved";
224
225 if (gtp->tent_type == TSTAT_ENT_UNUSED)
226 gtp->tent_name = "unused";
227 }
228
229 g_max_cpus = (processorid_t)sysconf(_SC_CPUID_MAX) + 1;
230
231 if ((g_selected = malloc(sizeof (int8_t) * g_max_cpus)) == NULL)
232 fatal("could not allocate g_selected");
233
234 bzero(g_selected, sizeof (int8_t) * g_max_cpus);
235
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");
239
240 bzero(g_pset_cpus, sizeof (processorid_t) * g_max_cpus);
241
242 if ((g_pgsizes = getpagesizes(NULL, 0)) == -1)
243 fatal("getpagesizes()");
244
245 if ((g_pgsize = malloc(sizeof (size_t) * g_pgsizes)) == NULL)
246 fatal("could not allocate g_pgsize array");
247
248 if (getpagesizes(g_pgsize, g_pgsizes) == -1)
249 fatal("getpagesizes(%d)", g_pgsizes);
250
251 if ((g_pgnames = malloc(sizeof (char *) * g_pgsizes)) == NULL)
252 fatal("could not allocate g_pgnames");
253
254 for (i = 0; i < g_pgsizes; i++) {
255 size_t j, mul;
256 size_t sz = g_pgsize[i];
257
258 if ((g_pgnames[i] = malloc(TSTAT_PAGESIZE_STRLEN)) == NULL)
259 fatal("could not allocate g_pgnames[%d]", i);
260
261 for (j = 0, mul = 10; (1 << mul) <= sz; j++, mul += 10)
262 continue;
263
264 (void) snprintf(g_pgnames[i], TSTAT_PAGESIZE_STRLEN,
265 "%d%c", sz >> (mul - 10), " kmgtpe"[j]);
266 }
267
268 g_datasize =
269 sizeof (tstat_data_t) + (g_pgsizes - 1) * sizeof (tstat_pgszdata_t);
270
271 if ((g_data[0] = malloc(g_datasize * g_max_cpus)) == NULL)
272 fatal("could not allocate data buffer 0");
273
274 if ((g_data[1] = malloc(g_datasize * g_max_cpus)) == NULL)
275 fatal("could not allocate data buffer 1");
276
277 (void) sigemptyset(&act.sa_mask);
278 act.sa_flags = 0;
279 act.sa_handler = intr;
280 (void) sigaction(SIGUSR1, &act, NULL);
281 (void) sigaction(SIGCHLD, &act, NULL);
282
283 (void) sigaddset(&act.sa_mask, SIGCHLD);
284 (void) sigaddset(&act.sa_mask, SIGUSR1);
285 (void) sigaction(SIGWINCH, &act, NULL);
286 set_width();
287
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);
293
294 ev.sigev_notify = SIGEV_SIGNAL;
295 ev.sigev_signo = SIGUSR1;
296
297 if (timer_create(CLOCK_HIGHRES, &ev, &g_tid) == -1)
298 fatal("cannot create CLOCK_HIGHRES timer");
299 }
300
301 static void
set_interval(hrtime_t nsec)302 set_interval(hrtime_t nsec)
303 {
304 struct itimerspec ts;
305
306 /*
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
310 * rate-per-second.
311 */
312 g_interval = nsec < NANOSEC ? nsec : NANOSEC;
313
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;
318
319 if (timer_settime(g_tid, TIMER_RELTIME, &ts, NULL) == -1)
320 fatal("cannot set time on CLOCK_HIGHRES timer");
321 }
322
323 static void
print_entries(FILE * stream,int parsable)324 print_entries(FILE *stream, int parsable)
325 {
326 int entno;
327
328 if (!parsable) {
329 (void) fprintf(stream, " %3s %3s | %-20s | %s\n", "hex",
330 "dec", "entry name", "description");
331
332 (void) fprintf(stream, "----------+----------------------"
333 "+-----------------------\n");
334 }
335
336 for (entno = 0; entno < TSTAT_NENT; entno++) {
337 tstat_ent_t *gtp;
338
339 if ((gtp = get_trap_ent(entno)) == NULL)
340 continue;
341
342 if (gtp->tent_type != TSTAT_ENT_USED)
343 continue;
344
345 (void) fprintf(stream, "0x%03x %3d %s%-20s %s%s\n",
346 entno, entno,
347 parsable ? "" : "| ", gtp->tent_name,
348 parsable ? "" : "| ", gtp->tent_descr);
349 }
350 }
351
352 static void
select_entry(char * entry)353 select_entry(char *entry)
354 {
355 ulong_t entno;
356 char *end;
357
358 /*
359 * The entry may be specified as a number (e.g., "0x68", "104") or
360 * as a name ("dtlb-miss").
361 */
362 entno = strtoul(entry, &end, 0);
363
364 if (*end == '\0') {
365 if (entno >= TSTAT_NENT)
366 goto bad_entry;
367 } else {
368 for (entno = 0; entno < TSTAT_NENT; entno++) {
369 tstat_ent_t *gtp;
370
371 if ((gtp = get_trap_ent(entno)) == NULL)
372 continue;
373
374 if (gtp->tent_type != TSTAT_ENT_USED)
375 continue;
376
377 if (strcmp(entry, gtp->tent_name) == 0)
378 break;
379 }
380
381 if (entno == TSTAT_NENT)
382 goto bad_entry;
383 }
384
385 if (ioctl(g_fd, TSTATIOC_ENTRY, entno) == -1)
386 fatal("TSTATIOC_ENTRY failed for entry 0x%x", entno);
387
388 g_active[entno] = 1;
389 return;
390
391 bad_entry:
392 (void) fprintf(stderr, TSTAT_COMMAND ": invalid entry '%s'", entry);
393 (void) fprintf(stderr, "; valid entries:\n\n");
394 print_entries(stderr, 0);
395 exit(EXIT_FAILURE);
396 }
397
398 static void
select_cpu(processorid_t cpu)399 select_cpu(processorid_t cpu)
400 {
401 if (g_pset != PS_NONE)
402 fatal("cannot specify both a processor set and a processor\n");
403
404 if (cpu < 0 || cpu >= g_max_cpus)
405 fatal("cpu %d out of range\n", cpu);
406
407 if (p_online(cpu, P_STATUS) == -1) {
408 if (errno != EINVAL)
409 fatal("could not get status for cpu %d", cpu);
410 fatal("cpu %d not present\n", cpu);
411 }
412
413 g_selected[cpu] = 1;
414 }
415
416 static void
select_cpus(processorid_t low,processorid_t high)417 select_cpus(processorid_t low, processorid_t high)
418 {
419 if (g_pset != PS_NONE)
420 fatal("cannot specify both a processor set and processors\n");
421
422 if (low < 0 || low >= g_max_cpus)
423 fatal("invalid cpu '%d'\n", low);
424
425 if (high < 0 || high >= g_max_cpus)
426 fatal("invalid cpu '%d'\n", high);
427
428 if (low >= high)
429 fatal("invalid range '%d' to '%d'\n", low, high);
430
431 do {
432 if (p_online(low, P_STATUS) != -1)
433 g_selected[low] = 1;
434 } while (++low <= high);
435 }
436
437 static void
select_pset(psetid_t pset)438 select_pset(psetid_t pset)
439 {
440 processorid_t i;
441
442 if (pset < 0)
443 fatal("processor set %d is out of range\n", pset);
444
445 /*
446 * Only one processor set can be specified.
447 */
448 if (g_pset != PS_NONE)
449 fatal("at most one processor set may be specified\n");
450
451 /*
452 * One cannot select processors _and_ a processor set.
453 */
454 for (i = 0; i < g_max_cpus; i++)
455 if (g_selected[i])
456 break;
457
458 if (i != g_max_cpus)
459 fatal("cannot specify both a processor and a processor set\n");
460
461 g_pset = pset;
462 g_pset_ncpus = g_max_cpus;
463
464 if (pset_info(g_pset, NULL, &g_pset_ncpus, g_pset_cpus) == -1)
465 fatal("invalid processor set: %d\n", g_pset);
466
467 if (g_pset_ncpus == 0)
468 fatal("processor set %d empty\n", g_pset);
469
470 if (ioctl(g_fd, TSTATIOC_NOCPU) == -1)
471 fatal("TSTATIOC_NOCPU failed");
472
473 for (i = 0; i < g_pset_ncpus; i++)
474 g_selected[g_pset_cpus[i]] = 1;
475 }
476
477 static void
check_pset(void)478 check_pset(void)
479 {
480 uint_t ncpus = g_max_cpus;
481 processorid_t i;
482
483 if (g_pset == PS_NONE)
484 return;
485
486 if (pset_info(g_pset, NULL, &ncpus, g_pset_cpus) == -1) {
487 if (errno == EINVAL)
488 fatal("processor set %d destroyed\n", g_pset);
489
490 fatal("couldn't get info for processor set %d", g_pset);
491 }
492
493 if (ncpus == 0)
494 fatal("processor set %d empty\n", g_pset);
495
496 if (ncpus == g_pset_ncpus) {
497 for (i = 0; i < g_pset_ncpus; i++) {
498 if (!g_selected[g_pset_cpus[i]])
499 break;
500 }
501
502 /*
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.
506 */
507 if (i == g_pset_ncpus)
508 return;
509 }
510
511 /*
512 * If we're here, we have a new processor set. First, we need
513 * to zero out the selection array.
514 */
515 bzero(g_selected, sizeof (int8_t) * g_max_cpus);
516
517 g_pset_ncpus = ncpus;
518
519 if (ioctl(g_fd, TSTATIOC_STOP) == -1)
520 fatal("TSTATIOC_STOP failed");
521
522 if (ioctl(g_fd, TSTATIOC_NOCPU) == -1)
523 fatal("TSATIOC_NOCPU failed");
524
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);
529 }
530
531 /*
532 * Now that we have selected the CPUs, we're going to reenable
533 * trapstat, and reread the data for the current generation.
534 */
535 if (ioctl(g_fd, TSTATIOC_GO) == -1)
536 fatal("TSTATIOC_GO failed");
537
538 if (ioctl(g_fd, TSTATIOC_READ, g_data[g_gen]) == -1)
539 fatal("TSTATIOC_READ failed");
540 }
541
542 static void
missdata(tstat_missdata_t * miss,tstat_missdata_t * omiss)543 missdata(tstat_missdata_t *miss, tstat_missdata_t *omiss)
544 {
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;
552 double p;
553
554 /*
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.
558 */
559 time = miss->tmiss_time - omiss->tmiss_time + peffect;
560
561 if (time >= tick) {
562 /*
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
566 * values...
567 */
568 time = tick / 1000 * 999;
569 }
570
571 p = (double)time / (double)tick * (double)100.0;
572
573 (*g_process)(g_arg, diff, p);
574 }
575
576 static void
tlbdata(tstat_tlbdata_t * tlb,tstat_tlbdata_t * otlb)577 tlbdata(tstat_tlbdata_t *tlb, tstat_tlbdata_t *otlb)
578 {
579 missdata(&tlb->ttlb_tlb, &otlb->ttlb_tlb);
580 missdata(&tlb->ttlb_tsb, &otlb->ttlb_tsb);
581 }
582
583 static void
print_missdata(double * ttl,uint64_t diff,double p)584 print_missdata(double *ttl, uint64_t diff, double p)
585 {
586 TSTAT_PRINT_MISSDATA(diff, p);
587
588 if (ttl != NULL)
589 *ttl += p;
590 }
591
592 static void
print_modepgsz(char * prefix,tstat_modedata_t * data,tstat_modedata_t * odata)593 print_modepgsz(char *prefix, tstat_modedata_t *data, tstat_modedata_t *odata)
594 {
595 int ps;
596 size_t incr = sizeof (tstat_pgszdata_t);
597
598 for (ps = 0; ps < g_pgsizes; ps++) {
599 double ttl = 0.0;
600
601 g_process = (void(*)(void *, uint64_t, double))print_missdata;
602 g_arg = &ttl;
603
604 (void) printf("%s %4s|", prefix, g_pgnames[ps]);
605 tlbdata(&data->tmode_itlb, &odata->tmode_itlb);
606 (void) printf(" |");
607 tlbdata(&data->tmode_dtlb, &odata->tmode_dtlb);
608
609 (void) printf(" |%4.1f\n", ttl);
610
611 data = (tstat_modedata_t *)((uintptr_t)data + incr);
612 odata = (tstat_modedata_t *)((uintptr_t)odata + incr);
613 }
614 }
615
616 static void
parsable_modepgsz(char * prefix,tstat_modedata_t * data,tstat_modedata_t * odata)617 parsable_modepgsz(char *prefix, tstat_modedata_t *data, tstat_modedata_t *odata)
618 {
619 int ps;
620 size_t incr = sizeof (tstat_pgszdata_t);
621
622 g_process = (void(*)(void *, uint64_t, double))print_missdata;
623 g_arg = NULL;
624
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);
629 (void) printf("\n");
630
631 data = (tstat_modedata_t *)((uintptr_t)data + incr);
632 odata = (tstat_modedata_t *)((uintptr_t)odata + incr);
633 }
634 }
635
636 static void
sum_missdata(void * sump,uint64_t diff,double p)637 sum_missdata(void *sump, uint64_t diff, double p)
638 {
639 tstat_sum_t *sum = *((tstat_sum_t **)sump);
640
641 sum->tsum_diff += diff;
642 sum->tsum_time += p;
643
644 (*(tstat_sum_t **)sump)++;
645 }
646
647 static void
sum_modedata(tstat_modedata_t * data,tstat_modedata_t * odata,tstat_sum_t * sum)648 sum_modedata(tstat_modedata_t *data, tstat_modedata_t *odata, tstat_sum_t *sum)
649 {
650 int ps, incr = sizeof (tstat_pgszdata_t);
651 tstat_sum_t *sump;
652
653 for (ps = 0; ps < g_pgsizes; ps++) {
654 sump = sum;
655
656 g_process = sum_missdata;
657 g_arg = &sump;
658
659 tlbdata(&data->tmode_itlb, &odata->tmode_itlb);
660 tlbdata(&data->tmode_dtlb, &odata->tmode_dtlb);
661
662 data = (tstat_modedata_t *)((uintptr_t)data + incr);
663 odata = (tstat_modedata_t *)((uintptr_t)odata + incr);
664 }
665 }
666
667 static void
print_sum(tstat_sum_t * sum,int divisor)668 print_sum(tstat_sum_t *sum, int divisor)
669 {
670 int i;
671 double ttl = 0.0;
672
673 for (i = 0; i < 4; i++) {
674 if (i == 2)
675 (void) printf(" |");
676
677 sum[i].tsum_time /= divisor;
678
679 TSTAT_PRINT_MISSDATA(sum[i].tsum_diff, sum[i].tsum_time);
680 ttl += sum[i].tsum_time;
681 }
682
683 (void) printf(" |%4.1f\n", ttl);
684 }
685
686 static void
print_tlbpgsz(tstat_data_t * data,tstat_data_t * odata)687 print_tlbpgsz(tstat_data_t *data, tstat_data_t *odata)
688 {
689 int i, cpu, ncpus = 0;
690 char pre[12];
691 tstat_sum_t sum[4];
692
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");
697
698 bzero(sum, sizeof (sum));
699
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;
703
704 if ((cpu = data->tdata_cpuid) == -1)
705 break;
706
707 if (i != 0)
708 (void) printf("----------+-----------------------------"
709 "--+-------------------------------+----\n");
710
711 g_ndata = data;
712 g_odata = odata;
713
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);
717
718 (void) printf("- - - - - + - - - - - - - - - - - - - -"
719 " - + - - - - - - - - - - - - - - - + - -\n");
720
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);
724
725 data = (tstat_data_t *)((uintptr_t)data + g_datasize);
726 odata = (tstat_data_t *)((uintptr_t)odata + g_datasize);
727 ncpus++;
728 }
729
730 (void) printf("==========+===============================+========="
731 "======================+====\n");
732 (void) printf(" ttl |");
733 print_sum(sum, ncpus);
734 (void) printf("\n");
735 }
736
737 static void
parsable_tlbpgsz(tstat_data_t * data,tstat_data_t * odata)738 parsable_tlbpgsz(tstat_data_t *data, tstat_data_t *odata)
739 {
740 int i, cpu;
741 char pre[30];
742
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;
746
747 if ((cpu = data->tdata_cpuid) == -1)
748 break;
749
750 g_ndata = data;
751 g_odata = odata;
752
753 (void) sprintf(pre, "%lld %3d u",
754 data->tdata_snapts - g_start, cpu);
755 parsable_modepgsz(pre, &pgsz->tpgsz_user, &opgsz->tpgsz_user);
756
757 pre[strlen(pre) - 1] = 'k';
758 parsable_modepgsz(pre, &pgsz->tpgsz_kernel,
759 &opgsz->tpgsz_kernel);
760
761 data = (tstat_data_t *)((uintptr_t)data + g_datasize);
762 odata = (tstat_data_t *)((uintptr_t)odata + g_datasize);
763 }
764 }
765
766 static void
print_modedata(tstat_modedata_t * data,tstat_modedata_t * odata,int parsable)767 print_modedata(tstat_modedata_t *data, tstat_modedata_t *odata, int parsable)
768 {
769 int ps, i;
770 size_t incr = sizeof (tstat_pgszdata_t);
771 tstat_sum_t sum[4], *sump = sum;
772 double ttl = 0.0;
773
774 bzero(sum, sizeof (sum));
775 g_process = sum_missdata;
776 g_arg = &sump;
777
778 for (ps = 0; ps < g_pgsizes; ps++) {
779 tlbdata(&data->tmode_itlb, &odata->tmode_itlb);
780 tlbdata(&data->tmode_dtlb, &odata->tmode_dtlb);
781
782 data = (tstat_modedata_t *)((uintptr_t)data + incr);
783 odata = (tstat_modedata_t *)((uintptr_t)odata + incr);
784 sump = sum;
785 }
786
787 for (i = 0; i < 4; i++) {
788 if (i == 2 && !parsable)
789 (void) printf(" |");
790
791 TSTAT_PRINT_MISSDATA(sum[i].tsum_diff, sum[i].tsum_time);
792 ttl += sum[i].tsum_time;
793 }
794
795 if (parsable) {
796 (void) printf("\n");
797 return;
798 }
799
800 (void) printf(" |%4.1f\n", ttl);
801 }
802
803 static void
print_tlb(tstat_data_t * data,tstat_data_t * odata)804 print_tlb(tstat_data_t *data, tstat_data_t *odata)
805 {
806 int i, cpu, ncpus = 0;
807 tstat_sum_t sum[4];
808
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");
813
814 bzero(sum, sizeof (sum));
815
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;
819
820 if ((cpu = data->tdata_cpuid) == -1)
821 break;
822
823 if (i != 0)
824 (void) printf("-----+-------------------------------+-"
825 "------------------------------+----\n");
826
827 g_ndata = data;
828 g_odata = odata;
829
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);
833
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);
837
838 data = (tstat_data_t *)((uintptr_t)data + g_datasize);
839 odata = (tstat_data_t *)((uintptr_t)odata + g_datasize);
840 ncpus++;
841 }
842
843 (void) printf("=====+===============================+========="
844 "======================+====\n");
845
846 (void) printf(" ttl |");
847 print_sum(sum, ncpus);
848 (void) printf("\n");
849 }
850
851 static void
parsable_tlb(tstat_data_t * data,tstat_data_t * odata)852 parsable_tlb(tstat_data_t *data, tstat_data_t *odata)
853 {
854 int i, cpu;
855
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;
859
860 if ((cpu = data->tdata_cpuid) == -1)
861 break;
862
863 g_ndata = data;
864 g_odata = odata;
865
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);
870
871 data = (tstat_data_t *)((uintptr_t)data + g_datasize);
872 odata = (tstat_data_t *)((uintptr_t)odata + g_datasize);
873 }
874 }
875
876 static void
print_stats(tstat_data_t * data,tstat_data_t * odata)877 print_stats(tstat_data_t *data, tstat_data_t *odata)
878 {
879 int i, j, k, done;
880 processorid_t id;
881 tstat_data_t *base = data;
882
883 /*
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.
888 */
889 for (i = 0; i < g_max_cpus; i++) {
890 if (data[i].tdata_cpuid == -1)
891 break;
892
893 for (j = 0; j < TSTAT_NENT; j++) {
894 if (!data[i].tdata_traps[j] || g_active[j])
895 continue;
896
897 g_active[j] = 1;
898 }
899 }
900
901 data = base;
902
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)
906 break;
907
908 if ((id = data[i].tdata_cpuid) == -1)
909 break;
910
911 if (i == 0)
912 (void) printf("vct name |");
913
914 (void) printf(" %scpu%d", id >= 100 ? "" :
915 id >= 10 ? " " : " ", id);
916 }
917
918 if (i == 0)
919 break;
920
921 if (i != g_cpus_per_line)
922 done = 1;
923
924 (void) printf("\n------------------------+");
925
926 for (j = 0; j < i; j++)
927 (void) printf("---------");
928 (void) printf("\n");
929
930 for (j = 0; j < TSTAT_NENT; j++) {
931 tstat_ent_t *gtp;
932
933 if ((!g_active[j]) || ((gtp = get_trap_ent(j)) == NULL))
934 continue;
935
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]));
940 }
941 (void) printf("\n");
942 }
943 (void) printf("\n");
944 }
945 }
946
947 static void
parsable_stats(tstat_data_t * data,tstat_data_t * odata)948 parsable_stats(tstat_data_t *data, tstat_data_t *odata)
949 {
950 tstat_data_t *base;
951 int i;
952
953 for (base = data; data - base < g_max_cpus; data++, odata++) {
954 if (data->tdata_cpuid == -1)
955 break;
956
957 for (i = 0; i < TSTAT_NENT; i++) {
958 tstat_ent_t *gtp;
959
960 if ((!data->tdata_traps[i] && !g_active[i]) ||
961 ((gtp = get_trap_ent(i)) == NULL))
962 continue;
963
964 (void) printf("%lld %d %x %s ",
965 data->tdata_snapts - g_start, data->tdata_cpuid, i,
966 gtp->tent_name);
967
968 (void) printf("%lld\n", TSTAT_DELTA(data, odata,
969 tdata_traps[i]));
970 }
971 }
972 }
973
974 static void
check_data(tstat_data_t * data,tstat_data_t * odata)975 check_data(tstat_data_t *data, tstat_data_t *odata)
976 {
977 tstat_data_t *ndata;
978 int i;
979
980 if (data->tdata_cpuid == -1) {
981 /*
982 * The last CPU we were watching must have been DR'd out
983 * of the system. Print a vaguely useful message and exit.
984 */
985 fatal("all initially selected CPUs have been unconfigured\n");
986 }
987
988 /*
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
996 * correctly.
997 */
998 for (i = 0; i < g_max_cpus; i++) {
999 if (odata->tdata_cpuid == -1)
1000 return;
1001
1002 if (data->tdata_cpuid != odata->tdata_cpuid)
1003 break;
1004
1005 data = (tstat_data_t *)((uintptr_t)data + g_datasize);
1006 odata = (tstat_data_t *)((uintptr_t)odata + g_datasize);
1007 }
1008
1009 if (i == g_max_cpus)
1010 return;
1011
1012 /*
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.
1015 */
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;
1020 }
1021
1022 /*
1023 * There may be other CPUs DR'd out; tail-call recurse.
1024 */
1025 check_data(data, odata);
1026 }
1027
1028 int
main(int argc,char ** argv)1029 main(int argc, char **argv)
1030 {
1031 processorid_t id;
1032 char *end;
1033 int c;
1034 ulong_t indefinite;
1035 long count = 0, rate = 0;
1036 int list = 0, parsable = 0;
1037 void (*print)(tstat_data_t *, tstat_data_t *);
1038 sigset_t set;
1039
1040 struct {
1041 char opt;
1042 void (*print)(tstat_data_t *, tstat_data_t *);
1043 void (*parsable)(tstat_data_t *, tstat_data_t *);
1044 int repeat;
1045 } tab[] = {
1046 { '\0', print_stats, parsable_stats, 0 },
1047 { 'e', print_stats, parsable_stats, 1 },
1048 { 't', print_tlb, parsable_tlb, 0 },
1049 { 'T', print_tlbpgsz, parsable_tlbpgsz, 0 },
1050 { -1, NULL, NULL, 0 }
1051 }, *tabent = NULL, *iter;
1052
1053 uintptr_t offs = (uintptr_t)&tab->print - (uintptr_t)tab;
1054
1055 /*
1056 * If argv[0] is non-NULL, set argv[0] to keep any getopt(3C) output
1057 * consistent with other error output.
1058 */
1059 if (argv[0] != NULL)
1060 argv[0] = TSTAT_COMMAND;
1061
1062 if ((g_fd = open(TSTAT_DEVICE, O_RDWR)) == -1)
1063 fatal("couldn't open " TSTAT_DEVICE);
1064
1065 setup();
1066
1067 while ((c = getopt(argc, argv, "alnNtTc:C:r:e:P")) != EOF) {
1068 /*
1069 * First, check to see if this option changes our printing
1070 * function.
1071 */
1072 for (iter = tab; iter->opt >= 0; iter++) {
1073 if (c != iter->opt)
1074 continue;
1075
1076 if (tabent != NULL) {
1077 if (tabent == iter) {
1078 if (tabent->repeat) {
1079 /*
1080 * This option is allowed to
1081 * have repeats; break out.
1082 */
1083 break;
1084 }
1085
1086 fatal("expected -%c at most once\n", c);
1087 }
1088
1089 fatal("only one of -%c, -%c expected\n",
1090 tabent->opt, c);
1091 }
1092
1093 tabent = iter;
1094 break;
1095 }
1096
1097 switch (c) {
1098 case 'a':
1099 g_absolute = 1;
1100 break;
1101
1102 case 'e': {
1103 char *s = strtok(optarg, ",");
1104
1105 while (s != NULL) {
1106 select_entry(s);
1107 s = strtok(NULL, ",");
1108 }
1109
1110 break;
1111 }
1112
1113 case 'l':
1114 list = 1;
1115 break;
1116
1117 case 'n':
1118 /*
1119 * This undocumented option prevents trapstat from
1120 * actually switching the %tba to point to the
1121 * interposing trap table. It's very useful when
1122 * debugging trapstat bugs: one can specify "-n"
1123 * and then examine the would-be interposing trap
1124 * table without running the risk of RED stating.
1125 */
1126 if (ioctl(g_fd, TSTATIOC_NOGO) == -1)
1127 fatal("TSTATIOC_NOGO");
1128 break;
1129
1130 case 'N':
1131 /*
1132 * This undocumented option forces trapstat to ignore
1133 * its determined probe effect. This may be useful
1134 * if it is believed that the probe effect has been
1135 * grossly overestimated.
1136 */
1137 g_peffect = 0;
1138 break;
1139
1140 case 't':
1141 case 'T':
1142 /*
1143 * When running with TLB statistics, we want to
1144 * minimize probe effect by running with all other
1145 * entries explicitly disabled.
1146 */
1147 if (ioctl(g_fd, TSTATIOC_NOENTRY) == -1)
1148 fatal("TSTATIOC_NOENTRY");
1149
1150 if (ioctl(g_fd, TSTATIOC_TLBDATA) == -1)
1151 fatal("TSTATIOC_TLBDATA");
1152 break;
1153
1154 case 'c': {
1155 /*
1156 * We allow CPUs to be specified as an optionally
1157 * comma separated list of either CPU IDs or ranges
1158 * of CPU IDs.
1159 */
1160 char *s = strtok(optarg, ",");
1161
1162 while (s != NULL) {
1163 id = strtoul(s, &end, 0);
1164
1165 if (id == ULONG_MAX && errno == ERANGE) {
1166 *end = '\0';
1167 fatal("invalid cpu '%s'\n", s);
1168 }
1169
1170 if (*(s = end) != '\0') {
1171 processorid_t p;
1172
1173 if (*s != '-')
1174 fatal("invalid cpu '%s'\n", s);
1175 p = strtoul(++s, &end, 0);
1176
1177 if (*end != '\0' ||
1178 (p == ULONG_MAX && errno == ERANGE))
1179 fatal("invalid cpu '%s'\n", s);
1180
1181 select_cpus(id, p);
1182 } else {
1183 select_cpu(id);
1184 }
1185
1186 s = strtok(NULL, ",");
1187 }
1188
1189 break;
1190 }
1191
1192 case 'C': {
1193 psetid_t pset = strtoul(optarg, &end, 0);
1194
1195 if (*end != '\0' ||
1196 (pset == ULONG_MAX && errno == ERANGE))
1197 fatal("invalid processor set '%s'\n", optarg);
1198
1199 select_pset(pset);
1200 break;
1201 }
1202
1203 case 'r': {
1204 rate = strtol(optarg, &end, 0);
1205
1206 if (*end != '\0' ||
1207 (rate == LONG_MAX && errno == ERANGE))
1208 fatal("invalid rate '%s'\n", optarg);
1209
1210 if (rate <= 0)
1211 fatal("rate must be greater than zero\n");
1212
1213 if (rate > TSTAT_MAX_RATE)
1214 fatal("rate may not exceed %d\n",
1215 TSTAT_MAX_RATE);
1216
1217 set_interval(NANOSEC / rate);
1218 break;
1219 }
1220
1221 case 'P':
1222 offs = (uintptr_t)&tab->parsable - (uintptr_t)tab;
1223 parsable = 1;
1224 break;
1225
1226 default:
1227 usage();
1228 }
1229 }
1230
1231 if (list) {
1232 print_entries(stdout, parsable);
1233 exit(EXIT_SUCCESS);
1234 }
1235
1236 if (optind != argc) {
1237
1238 int interval = strtol(argv[optind], &end, 0);
1239
1240 if (*end != '\0') {
1241 /*
1242 * That wasn't a valid number. It must be that we're
1243 * to execute this command.
1244 */
1245 switch (vfork()) {
1246 case 0:
1247 (void) close(g_fd);
1248 (void) sigprocmask(SIG_SETMASK, &g_oset, NULL);
1249 (void) execvp(argv[optind], &argv[optind]);
1250
1251 /*
1252 * No luck. Set errno.
1253 */
1254 g_exec_errno = errno;
1255 _exit(EXIT_FAILURE);
1256 /*NOTREACHED*/
1257 case -1:
1258 fatal("cannot fork");
1259 /*NOTREACHED*/
1260 default:
1261 break;
1262 }
1263 } else {
1264 if (interval <= 0)
1265 fatal("interval must be greater than zero.\n");
1266
1267 if (interval == LONG_MAX && errno == ERANGE)
1268 fatal("invalid interval '%s'\n", argv[optind]);
1269
1270 set_interval(NANOSEC * (hrtime_t)interval);
1271
1272 if (++optind != argc) {
1273 char *s = argv[optind];
1274
1275 count = strtol(s, &end, 0);
1276
1277 if (*end != '\0' || count <= 0 ||
1278 (count == LONG_MAX && errno == ERANGE))
1279 fatal("invalid count '%s'\n", s);
1280 }
1281 }
1282 } else {
1283 if (!rate)
1284 set_interval(NANOSEC);
1285 }
1286
1287 if (tabent == NULL)
1288 tabent = tab;
1289
1290 print = *(void(**)(tstat_data_t *, tstat_data_t *))
1291 ((uintptr_t)tabent + offs);
1292
1293 for (id = 0; id < g_max_cpus; id++) {
1294 if (!g_selected[id])
1295 continue;
1296
1297 if (ioctl(g_fd, TSTATIOC_CPU, id) == -1)
1298 fatal("TSTATIOC_CPU failed for cpu %d", id);
1299 }
1300
1301 g_start = gethrtime();
1302
1303 if (ioctl(g_fd, TSTATIOC_GO) == -1)
1304 fatal("TSTATIOC_GO failed");
1305
1306 if (ioctl(g_fd, TSTATIOC_READ, g_data[g_gen ^ 1]) == -1)
1307 fatal("initial TSTATIOC_READ failed");
1308
1309 (void) sigemptyset(&set);
1310
1311 for (indefinite = (count == 0); indefinite || count; count--) {
1312
1313 (void) sigsuspend(&set);
1314
1315 if (g_winch) {
1316 g_winch = 0;
1317 continue;
1318 }
1319
1320 if (g_child_exited && g_exec_errno != 0) {
1321 errno = g_exec_errno;
1322 fatal("could not execute %s", argv[optind]);
1323 }
1324
1325 if (ioctl(g_fd, TSTATIOC_READ, g_data[g_gen]) == -1)
1326 fatal("TSTATIOC_READ failed");
1327
1328 /*
1329 * Before we blithely print the data, we need to
1330 * make sure that we haven't lost a CPU.
1331 */
1332 check_data(g_data[g_gen], g_data[g_gen ^ 1]);
1333 (*print)(g_data[g_gen], g_data[g_gen ^ 1]);
1334 (void) fflush(stdout);
1335
1336 if (g_child_exited) {
1337 if (WIFEXITED(g_child_status)) {
1338 if (WEXITSTATUS(g_child_status) == 0)
1339 break;
1340
1341 (void) fprintf(stderr, TSTAT_COMMAND ": "
1342 "warning: %s exited with code %d\n",
1343 argv[optind], WEXITSTATUS(g_child_status));
1344 } else {
1345 (void) fprintf(stderr, TSTAT_COMMAND ": "
1346 "warning: %s died on signal %d\n",
1347 argv[optind], WTERMSIG(g_child_status));
1348 }
1349 break;
1350 }
1351
1352 check_pset();
1353
1354 g_gen ^= 1;
1355 }
1356
1357 return (0);
1358 }
1359