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