1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/pset.h>
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/sysinfo.h>
30
31 #include <assert.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <ctype.h>
36 #include <unistd.h>
37 #include <memory.h>
38 #include <string.h>
39 #include <strings.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <kstat.h>
43 #include <poll.h>
44 #include <signal.h>
45 #include <locale.h>
46
47 #include "statcommon.h"
48
49 #define SNAP(s, i, l, n) ((s) ? agg_proc_snap(s, i, l, n) : 0)
50
51 #define REPRINT 20
52
53 char *cmdname = "mpstat";
54 int caught_cont = 0;
55
56 static uint_t timestamp_fmt = NODATE;
57
58 static int hz;
59 static int display_pset = -1;
60 static int show_set = 0;
61 static int suppress_state;
62
63 static void print_header(int, int);
64 static void show_cpu_usage(struct snapshot *, struct snapshot *, int);
65 static void usage(void);
66
67 int
main(int argc,char ** argv)68 main(int argc, char **argv)
69 {
70 int c;
71 int display_agg = 0;
72 int iter = 1;
73 int interval = 0;
74 char *endptr;
75 int infinite_cycles = 0;
76 kstat_ctl_t *kc;
77 struct snapshot *old = NULL;
78 struct snapshot *new = NULL;
79 enum snapshot_types types = SNAP_CPUS;
80 hrtime_t start_n;
81 hrtime_t period_n;
82
83 (void) setlocale(LC_ALL, "");
84 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
85 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
86 #endif
87 (void) textdomain(TEXT_DOMAIN);
88
89 while ((c = getopt(argc, argv, "apP:qT:")) != (int)EOF)
90 switch (c) {
91 case 'a':
92 /*
93 * Display aggregate data for processor sets.
94 */
95 display_agg = 1;
96 break;
97 case 'p':
98 /*
99 * Display all processor sets.
100 */
101 if (display_pset != -1)
102 usage();
103 show_set = 1;
104 break;
105 case 'P':
106 /*
107 * Display specific processor set.
108 */
109 if (show_set == 1)
110 usage();
111 display_pset = (int)strtol
112 (optarg, &endptr, 10);
113 if (*endptr != NULL)
114 usage();
115 /*
116 * Not valid to specify a negative processor
117 * set value.
118 */
119 if (display_pset < 0)
120 usage();
121 break;
122 case 'q':
123 suppress_state = 1;
124 break;
125 case 'T':
126 if (optarg) {
127 if (*optarg == 'u')
128 timestamp_fmt = UDATE;
129 else if (*optarg == 'd')
130 timestamp_fmt = DDATE;
131 else
132 usage();
133 } else {
134 usage();
135 }
136 break;
137 case '?':
138 usage();
139 break;
140 }
141
142 hz = sysconf(_SC_CLK_TCK);
143
144 if (argc > optind) {
145 interval = (int)strtol(argv[optind], &endptr, 10);
146 if (*endptr != NULL)
147 usage();
148 period_n = (hrtime_t)interval * NANOSEC;
149 if (argc > optind + 1) {
150 iter = (unsigned int)strtoul
151 (argv[optind + 1], &endptr, 10);
152 if (*endptr != NULL || iter < 0)
153 usage();
154 if (iter == 0)
155 return (0);
156 } else {
157 infinite_cycles = 1;
158 }
159 }
160
161 if (display_agg || show_set || display_pset != -1)
162 types |= SNAP_PSETS;
163
164 kc = open_kstat();
165
166 /* Set up handler for SIGCONT */
167 if (signal(SIGCONT, cont_handler) == SIG_ERR)
168 fail(1, "signal failed");
169
170 start_n = gethrtime();
171
172 while (infinite_cycles || iter > 0) {
173 free_snapshot(old);
174 old = new;
175 new = acquire_snapshot(kc, types, NULL);
176
177 if (!suppress_state)
178 snapshot_report_changes(old, new);
179
180 /* if config changed, show stats from boot */
181 if (snapshot_has_changed(old, new)) {
182 free_snapshot(old);
183 old = NULL;
184 }
185
186 show_cpu_usage(old, new, display_agg);
187
188 if (!infinite_cycles && --iter < 1)
189 break;
190
191 /* Have a kip */
192 sleep_until(&start_n, period_n, infinite_cycles, &caught_cont);
193 }
194 (void) kstat_close(kc);
195
196 return (0);
197 }
198
199 /*
200 * Print an mpstat output header.
201 */
202 static void
print_header(int display_agg,int show_set)203 print_header(int display_agg, int show_set)
204 {
205 if (display_agg == 1)
206 (void) printf("SET minf mjf xcal intr ithr csw icsw migr "
207 "smtx srw syscl usr sys wt idl sze");
208 else {
209 (void) printf("CPU minf mjf xcal intr ithr csw icsw migr "
210 "smtx srw syscl usr sys wt idl");
211 if (show_set == 1)
212 (void) printf(" set");
213 }
214 (void) printf("\n");
215 }
216
217 static void
print_cpu(struct cpu_snapshot * c1,struct cpu_snapshot * c2)218 print_cpu(struct cpu_snapshot *c1, struct cpu_snapshot *c2)
219 {
220 uint64_t ticks = 0;
221 double etime, percent;
222 kstat_t *old_vm = NULL;
223 kstat_t *old_sys = NULL;
224
225 if (display_pset != -1 && display_pset != c2->cs_pset_id)
226 return;
227
228 /*
229 * the first mpstat output will have c1 = NULL, to give
230 * results since boot
231 */
232 if (c1) {
233 old_vm = &c1->cs_vm;
234 old_sys = &c1->cs_sys;
235
236 /* check there are stats to report */
237 if (!CPU_ACTIVE(c1))
238 return;
239 }
240
241 /* check there are stats to report */
242 if (!CPU_ACTIVE(c2))
243 return;
244
245 ticks = cpu_ticks_delta(old_sys, &c2->cs_sys);
246
247 etime = (double)ticks / hz;
248 if (etime == 0.0) /* Prevent divide by zero errors */
249 etime = 1.0;
250 percent = 100.0 / etime / hz;
251
252 (void) printf("%3d %4.0f %3.0f %4.0f %5.0f %4.0f "
253 "%4.0f %4.0f %4.0f %4.0f %4.0f %5.0f %3.0f %3.0f "
254 "%3.0f %3.0f",
255 c2->cs_id,
256 (kstat_delta(old_vm, &c2->cs_vm, "hat_fault") +
257 kstat_delta(old_vm, &c2->cs_vm, "as_fault")) / etime,
258 kstat_delta(old_vm, &c2->cs_vm, "maj_fault") / etime,
259 kstat_delta(old_sys, &c2->cs_sys, "xcalls") / etime,
260 kstat_delta(old_sys, &c2->cs_sys, "intr") / etime,
261 kstat_delta(old_sys, &c2->cs_sys, "intrthread") / etime,
262 kstat_delta(old_sys, &c2->cs_sys, "pswitch") / etime,
263 kstat_delta(old_sys, &c2->cs_sys, "inv_swtch") / etime,
264 kstat_delta(old_sys, &c2->cs_sys, "cpumigrate") / etime,
265 kstat_delta(old_sys, &c2->cs_sys, "mutex_adenters") / etime,
266 (kstat_delta(old_sys, &c2->cs_sys, "rw_rdfails") +
267 kstat_delta(old_sys, &c2->cs_sys, "rw_wrfails")) / etime,
268 kstat_delta(old_sys, &c2->cs_sys, "syscall") / etime,
269 kstat_delta(old_sys, &c2->cs_sys, "cpu_ticks_user") * percent,
270 kstat_delta(old_sys, &c2->cs_sys, "cpu_ticks_kernel") * percent,
271 kstat_delta(old_sys, &c2->cs_sys, "cpu_ticks_wait") * percent,
272 kstat_delta(old_sys, &c2->cs_sys, "cpu_ticks_idle") * percent);
273
274 if (show_set)
275 (void) printf(" %3d", c2->cs_pset_id);
276 (void) printf("\n");
277 }
278
279 /*ARGSUSED*/
280 static void
compare_cpu(void * v1,void * v2,void * data)281 compare_cpu(void *v1, void *v2, void *data)
282 {
283 struct cpu_snapshot *c1 = (struct cpu_snapshot *)v1;
284 struct cpu_snapshot *c2 = (struct cpu_snapshot *)v2;
285
286 if (c2 == NULL)
287 return;
288
289 print_cpu(c1, c2);
290 }
291
292 static int
pset_has_stats(struct pset_snapshot * p)293 pset_has_stats(struct pset_snapshot *p)
294 {
295 int count = 0;
296 size_t i;
297 for (i = 0; i < p->ps_nr_cpus; i++) {
298 if (CPU_ACTIVE(p->ps_cpus[i]))
299 count++;
300 }
301 return (count);
302 }
303
304 static void
agg_stat(kstat_t * k1,kstat_t * k2,char * name)305 agg_stat(kstat_t *k1, kstat_t *k2, char *name)
306 {
307 kstat_named_t *ksn = kstat_data_lookup(k1, name);
308 kstat_named_t *ksn2 = kstat_data_lookup(k2, name);
309 ksn->value.ui64 += ksn2->value.ui64;
310 }
311
312 static kstat_t *
agg_vm(struct pset_snapshot * p,kstat_t * ks)313 agg_vm(struct pset_snapshot *p, kstat_t *ks)
314 {
315 size_t i;
316
317 if (p->ps_nr_cpus == NULL)
318 return (NULL);
319
320 if (kstat_copy(&p->ps_cpus[0]->cs_vm, ks))
321 return (NULL);
322
323 for (i = 1; i < p->ps_nr_cpus; i++) {
324 agg_stat(ks, &p->ps_cpus[i]->cs_vm, "hat_fault");
325 agg_stat(ks, &p->ps_cpus[i]->cs_vm, "as_fault");
326 agg_stat(ks, &p->ps_cpus[i]->cs_vm, "maj_fault");
327 }
328
329 return (ks);
330 }
331
332 static kstat_t *
agg_sys(struct pset_snapshot * p,kstat_t * ks)333 agg_sys(struct pset_snapshot *p, kstat_t *ks)
334 {
335 size_t i;
336
337 if (p->ps_nr_cpus == NULL)
338 return (NULL);
339
340 if (kstat_copy(&p->ps_cpus[0]->cs_sys, ks))
341 return (NULL);
342
343 for (i = 1; i < p->ps_nr_cpus; i++) {
344 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "xcalls");
345 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "intr");
346 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "intrthread");
347 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "pswitch");
348 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "inv_swtch");
349 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "cpumigrate");
350 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "mutex_adenters");
351 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "rw_rdfails");
352 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "rw_wrfails");
353 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "syscall");
354 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "cpu_ticks_user");
355 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "cpu_ticks_kernel");
356 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "cpu_ticks_wait");
357 agg_stat(ks, &p->ps_cpus[i]->cs_sys, "cpu_ticks_idle");
358 }
359
360 return (ks);
361 }
362
363 static uint64_t
get_nr_ticks(struct pset_snapshot * p1,struct pset_snapshot * p2)364 get_nr_ticks(struct pset_snapshot *p1, struct pset_snapshot *p2)
365 {
366 kstat_t *old = NULL;
367 kstat_t *new = NULL;
368 size_t i = 0;
369
370 for (i = 0; p1 && i < p1->ps_nr_cpus; i++) {
371 if (p1->ps_cpus[i]->cs_sys.ks_data) {
372 old = &p1->ps_cpus[i]->cs_sys;
373 break;
374 }
375 }
376
377 for (i = 0; p2 && i < p2->ps_nr_cpus; i++) {
378 if (p2->ps_cpus[i]->cs_sys.ks_data) {
379 new = &p2->ps_cpus[i]->cs_sys;
380 break;
381 }
382 }
383
384 if (old == NULL && new == NULL)
385 return (0);
386
387 if (new == NULL) {
388 new = old;
389 old = NULL;
390 }
391
392 return (cpu_ticks_delta(old, new));
393 }
394
395 static void
print_pset(struct pset_snapshot * p1,struct pset_snapshot * p2)396 print_pset(struct pset_snapshot *p1, struct pset_snapshot *p2)
397 {
398 uint64_t ticks = 0;
399 double etime, percent;
400 kstat_t old_vm;
401 kstat_t old_sys;
402 kstat_t new_vm;
403 kstat_t new_sys;
404
405 if (display_pset != -1 && display_pset != p2->ps_id)
406 return;
407
408 if ((p1 && !pset_has_stats(p1)) || !pset_has_stats(p2))
409 return;
410
411 old_vm.ks_data = old_sys.ks_data = NULL;
412 new_vm.ks_data = new_sys.ks_data = NULL;
413
414 /*
415 * FIXME: these aggs will count "new" or disappeared cpus
416 * in a set, leaving an apparent huge change.
417 */
418
419 /*
420 * the first mpstat output will have p1 = NULL, to give
421 * results since boot
422 */
423 if (p1) {
424 if (!agg_vm(p1, &old_vm) || !agg_sys(p1, &old_sys))
425 goto out;
426 }
427
428 if (!agg_vm(p2, &new_vm) || !agg_sys(p2, &new_sys))
429 goto out;
430
431 ticks = get_nr_ticks(p1, p2);
432
433 etime = (double)ticks / hz;
434 if (etime == 0.0) /* Prevent divide by zero errors */
435 etime = 1.0;
436 percent = 100.0 / p2->ps_nr_cpus / etime / hz;
437
438 (void) printf("%3d %4.0f %3.0f %4.0f %5.0f %4.0f "
439 "%4.0f %4.0f %4.0f %4.0f %4.0f %5.0f %3.0f %3.0f "
440 "%3.0f %3.0f %3d\n",
441 p2->ps_id,
442 (kstat_delta(&old_vm, &new_vm, "hat_fault") +
443 kstat_delta(&old_vm, &new_vm, "as_fault")) / etime,
444 kstat_delta(&old_vm, &new_vm, "maj_fault") / etime,
445 kstat_delta(&old_sys, &new_sys, "xcalls") / etime,
446 kstat_delta(&old_sys, &new_sys, "intr") / etime,
447 kstat_delta(&old_sys, &new_sys, "intrthread") / etime,
448 kstat_delta(&old_sys, &new_sys, "pswitch") / etime,
449 kstat_delta(&old_sys, &new_sys, "inv_swtch") / etime,
450 kstat_delta(&old_sys, &new_sys, "cpumigrate") / etime,
451 kstat_delta(&old_sys, &new_sys, "mutex_adenters") / etime,
452 (kstat_delta(&old_sys, &new_sys, "rw_rdfails") +
453 kstat_delta(&old_sys, &new_sys, "rw_wrfails")) / etime,
454 kstat_delta(&old_sys, &new_sys, "syscall") / etime,
455 kstat_delta(&old_sys, &new_sys, "cpu_ticks_user") * percent,
456 kstat_delta(&old_sys, &new_sys, "cpu_ticks_kernel") * percent,
457 kstat_delta(&old_sys, &new_sys, "cpu_ticks_wait") * percent,
458 kstat_delta(&old_sys, &new_sys, "cpu_ticks_idle") * percent,
459 p2->ps_nr_cpus);
460
461 out:
462 free(old_vm.ks_data);
463 free(old_sys.ks_data);
464 free(new_vm.ks_data);
465 free(new_sys.ks_data);
466 }
467
468 /*ARGSUSED*/
469 static void
compare_pset(void * v1,void * v2,void * data)470 compare_pset(void *v1, void *v2, void *data)
471 {
472 struct pset_snapshot *p1 = (struct pset_snapshot *)v1;
473 struct pset_snapshot *p2 = (struct pset_snapshot *)v2;
474
475 if (p2 == NULL)
476 return;
477
478 print_pset(p1, p2);
479 }
480
481
482 /*
483 * Report statistics for a sample interval.
484 */
485 static void
show_cpu_usage(struct snapshot * old,struct snapshot * new,int display_agg)486 show_cpu_usage(struct snapshot *old, struct snapshot *new, int display_agg)
487 {
488 static int lines_until_reprint = 0;
489 enum snapshot_types type = SNAP_CPUS;
490 snapshot_cb cb = compare_cpu;
491
492 if (timestamp_fmt != NODATE)
493 print_timestamp(timestamp_fmt);
494
495 if (lines_until_reprint == 0 || nr_active_cpus(new) > 1) {
496 print_header(display_agg, show_set);
497 lines_until_reprint = REPRINT;
498 }
499
500 lines_until_reprint--;
501
502 if (display_agg) {
503 type = SNAP_PSETS;
504 cb = compare_pset;
505 }
506
507 /* print stats since boot the first time round */
508 (void) snapshot_walk(type, old, new, cb, NULL);
509 (void) fflush(stdout);
510 }
511
512 /*
513 * Usage message on error.
514 */
515 static void
usage(void)516 usage(void)
517 {
518 (void) fprintf(stderr,
519 "Usage: mpstat [-aq] [-p | -P processor_set] [-T d|u] "
520 "[interval [count]]\n");
521 exit(1);
522 }
523