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