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