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