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