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