xref: /illumos-gate/usr/src/cmd/stat/mpstat/mpstat.c (revision ea429a71)
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 = 0;
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 != '\0')
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 != '\0')
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 != '\0' || 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 == 0)
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 == 0)
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