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 /* 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <unistd.h> 30 #include <strings.h> 31 #include <curses.h> 32 #include <signal.h> 33 #include <fcntl.h> 34 #include <locale.h> 35 36 #include <sys/types.h> 37 #include <sys/time.h> 38 #include <sys/nsctl/sdbc_ioctl.h> 39 #include <sys/unistat/spcs_s_u.h> 40 #include <sys/nsctl/sd_bcache.h> 41 #include <sys/nsctl/sd_conf.h> 42 43 extern void total_display(void); 44 extern void display_cache(void); 45 extern void wrefresh_file(WINDOW *, int); 46 extern int is_dirty(void); 47 extern int dual_stats(void); 48 void checkbuf(int); 49 void setup_ranges(char *); 50 void prheading(int); 51 extern int zero_nic(void); 52 53 #ifdef m88k 54 #define USEC_INIT() usec_ptr = (unsigned int *)timer_init() 55 #define USEC_READ() (*usec_ptr) 56 #else /* !m88k */ 57 #define USEC_INIT() USEC_START() 58 #include <sys/time.h> 59 static struct timeval Usec_time; 60 static int Usec_started = 0; 61 62 extern int higher(int); 63 extern int is_dirty(); 64 extern int dual_stats(); 65 extern void total_display(); 66 extern void display_cache(); 67 extern void wrefresh_file(WINDOW *, int); 68 void setup_ranges(char *); 69 70 void prheading(int); 71 void checkbuf(int); 72 void quit(int); 73 void leave(int); 74 #pragma does_not_return(quit, leave) 75 76 int sdbc_max_devices = 0; 77 78 static void 79 USEC_START() 80 { 81 if (!Usec_started) { 82 (void) gettimeofday(&Usec_time, NULL); 83 Usec_started = 1; 84 } 85 } 86 87 static unsigned int 88 USEC_READ() 89 { 90 struct timeval tv; 91 if (!Usec_started) 92 USEC_START(); 93 94 (void) gettimeofday(&tv, NULL); 95 return (unsigned)((tv.tv_sec - Usec_time.tv_sec) * 1000000 96 + (tv.tv_usec - Usec_time.tv_usec)); 97 } 98 #endif /* m88k */ 99 100 int rev_flag = 0; /* Reverse video flag */ 101 int bold_flg = 0; /* Bold flag */ 102 int under_flg = 0; /* Underline flag */ 103 int errflg = 0; /* Error flag */ 104 int node_sw = 0; /* Per node switch */ 105 int toggle_total_sw = 0; 106 int mirror_sw = 0; /* Dual copy switch */ 107 108 int kmemfd; 109 int delay = 1; /* Display delay (seconds) */ 110 111 time_t *usec_ptr; 112 time_t currtime = 0; 113 int lasttime = 0; 114 int Elapsed_Time = 0; 115 116 static char *range; 117 static int had_r_option = 0; 118 int logfd = -1; /* screen output logging */ 119 extern int range_num; 120 extern int screen; 121 extern int dual_screen; 122 int *on_off; 123 int *dual_on_off; 124 int *updates_prev; 125 double *rate_prev; 126 int *samples; 127 _sd_stats_t *cs_cur; 128 _sd_stats_t *cs_prev; 129 _sd_stats_t *cs_persec; 130 131 typedef struct { 132 int lb, ub; 133 } range_t; 134 135 extern range_t ranges[]; 136 137 #ifdef lint 138 int 139 sd_stats_lintmain(int argc, char *argv[]) 140 #else 141 int 142 main(int argc, char *argv[]) 143 #endif 144 { 145 spcs_s_info_t ustats; 146 struct timeval tout; 147 fd_set readfds; 148 char *errmessage, *ch; 149 int c, period, prev; 150 int count = 0, dflag = 0; 151 int fd = fileno(stdin); 152 153 errmessage = NULL; 154 155 if (strcmp(argv[0], "sd_stats") != 0) 156 errmessage = getenv("SD_STATS_USAGE"); 157 158 if (errmessage == NULL) 159 errmessage = gettext("Usage: sd_stats [-Mz] " 160 "[-d delay_time] [-l logfile] [-r range]"); 161 162 if (SDBC_IOCTL(SDBC_MAXFILES, &sdbc_max_devices, 163 0, 0, 0, 0, &ustats) == SPCS_S_ERROR) { 164 if (ustats) { /* if SPCS_S_ERROR */ 165 spcs_s_report(ustats, stderr); 166 spcs_s_ufree(&ustats); 167 } 168 (void) fprintf(stderr, gettext("cannot get maxfiles\n")); 169 exit(1); 170 } 171 on_off = calloc(sdbc_max_devices, sizeof (int)); 172 dual_on_off = calloc(sdbc_max_devices, sizeof (int)); 173 updates_prev = calloc(sdbc_max_devices, sizeof (int)); 174 samples = calloc(sdbc_max_devices, sizeof (int)); 175 rate_prev = calloc(sdbc_max_devices, sizeof (double)); 176 cs_cur = malloc(sizeof (_sd_stats_t) + 177 (sdbc_max_devices - 1) * sizeof (_sd_shared_t)); 178 cs_prev = malloc(sizeof (_sd_stats_t) + 179 (sdbc_max_devices - 1) * sizeof (_sd_shared_t)); 180 cs_persec = malloc(sizeof (_sd_stats_t) + 181 (sdbc_max_devices - 1) * sizeof (_sd_shared_t)); 182 range = malloc(100); 183 184 if (!on_off || !dual_on_off || !updates_prev || !samples || 185 !rate_prev || !cs_cur || !cs_prev || !cs_persec || !range) { 186 (void) fprintf(stderr, gettext("no free memory\n")); 187 exit(1); 188 } 189 190 *range = '\0'; 191 192 while ((c = getopt(argc, argv, "DMzd:l:r:h")) != EOF) { 193 194 prev = c; 195 switch (c) { 196 197 case 'd': 198 delay = atoi(optarg); 199 ch = optarg; 200 while (*ch != '\0') { 201 if (!isdigit(*ch)) 202 errflg++; 203 ch++; 204 } 205 break; 206 207 case 'l': 208 logfd = open(optarg, O_CREAT|O_WRONLY|O_TRUNC, 0644); 209 break; 210 211 case 'r': 212 ch = optarg; 213 while (*ch != '\0') { 214 if ((!isdigit(*ch)) && (*ch != ',') && 215 (*ch != ':')) 216 errflg++; 217 ch++; 218 } 219 if (errflg) 220 break; 221 222 range = realloc((char *)range, 223 (strlen(range) + strlen(optarg) + 1) 224 * sizeof (char)); 225 226 if (had_r_option) 227 (void) strcat(range, ","); 228 (void) strcat(range, optarg); 229 had_r_option = 1; 230 break; 231 232 case 'z': 233 if (SDBC_IOCTL(SDBC_ZAP_STATS, 0, 0, 0, 0, 0, 234 &ustats) == SPCS_S_ERROR) { 235 if (ustats) { 236 spcs_s_report(ustats, stderr); 237 spcs_s_ufree(&ustats); 238 } 239 } 240 241 break; 242 243 case 'D': 244 dflag = 1; 245 break; 246 247 case 'M': 248 mirror_sw = 1; 249 break; 250 251 case 'h': 252 case '?': 253 default : 254 errflg++; 255 break; 256 } 257 } 258 259 if (errflg) { 260 (void) fprintf(stderr, "%s\n", errmessage); 261 exit(1); 262 } else if (!prev) { 263 if (argc > 1) { 264 (void) fprintf(stderr, "%s\n", errmessage); 265 exit(1); 266 } 267 } 268 269 if (dflag) { 270 exit(is_dirty()); 271 } 272 273 274 /* 275 * A few curses routines to setup screen and tty interface 276 */ 277 (void) initscr(); 278 (void) cbreak(); 279 (void) noecho(); 280 (void) nonl(); 281 (void) erase(); 282 (void) clear(); 283 (void) refresh(); 284 285 setup_ranges(range); 286 287 /* 288 * Set signal handle 289 */ 290 (void) sigset(SIGPIPE, leave); 291 (void) sigset(SIGINT, leave); 292 (void) sigset(SIGQUIT, leave); 293 (void) signal(SIGFPE, leave); 294 (void) signal(SIGSEGV, leave); 295 296 USEC_INIT(); 297 currtime = USEC_READ(); 298 299 /* 300 * Wait one second before reading the new values 301 */ 302 (void) sleep(1); 303 304 /*CONSTCOND*/ 305 while (1) { 306 307 lasttime = currtime; 308 currtime = USEC_READ(); 309 310 /* 311 * If less that 1 second, force it to one second 312 */ 313 if ((period = (currtime - lasttime) / 1000000) <= 0) 314 period = 1; 315 316 /* 317 * Calculate new per/period values for statistics 318 */ 319 Elapsed_Time += period; 320 321 /* 322 * Display new statistics 323 */ 324 prheading(++count); 325 326 if (mirror_sw) { 327 if (dual_stats() < 0) 328 mirror_sw = 0; 329 } else if (toggle_total_sw) 330 total_display(); 331 else 332 display_cache(); 333 334 (void) move(0, 0); 335 (void) refresh(); 336 if (logfd > -1) wrefresh_file(stdscr, logfd); 337 338 FD_ZERO(&readfds); 339 FD_SET(fd, &readfds); 340 tout.tv_sec = delay; 341 for (;;) { 342 tout.tv_usec = 0; 343 if (select(fd + 1, &readfds, (fd_set *)0, (fd_set *)0, 344 &tout) <= 0) 345 break; 346 if ((c = getch()) == EOF) { 347 (void) sleep(delay); 348 break; 349 } 350 checkbuf(c); 351 tout.tv_sec = 0; 352 } 353 (void) erase(); 354 } 355 #pragma error_messages(off, E_STATEMENT_NOT_REACHED) 356 return (0); 357 #pragma error_messages(default, E_STATEMENT_NOT_REACHED) 358 } 359 360 void 361 checkbuf(int c) 362 { 363 spcs_s_info_t ustats; 364 365 switch (c) { 366 case 'b' : /* ctrl b or b -- scroll backward */ 367 case 2 : 368 { 369 if (mirror_sw == 1) { 370 if (dual_screen > 0) 371 dual_screen--; 372 break; 373 } 374 if (screen > 0) 375 screen--; 376 break; 377 } 378 379 case 'f' : /* ctrl f or f -- scroll forward */ 380 case 6 : 381 { 382 if (mirror_sw == 1) { 383 dual_screen++; 384 break; 385 } 386 screen++; 387 break; 388 } 389 390 case 't': 391 case 'T': 392 if (mirror_sw == 1) 393 mirror_sw = 0; 394 395 toggle_total_sw ^= 1; 396 break; 397 398 case '-': 399 case KEY_DOWN: 400 if (delay > 1) { 401 --delay; 402 } else { 403 (void) beep(); 404 } 405 break; 406 407 case '+': 408 case KEY_UP: 409 delay++; 410 break; 411 412 case 'C': 413 case 0xc: 414 (void) clearok(stdscr, TRUE); 415 break; 416 417 case 'B': 418 if (bold_flg) { 419 bold_flg = 0; 420 (void) attroff(A_BOLD); 421 } else { 422 bold_flg = 1; 423 (void) attron(A_BOLD); 424 } 425 break; 426 427 case 'R': 428 if (rev_flag) { 429 rev_flag = 0; 430 (void) attroff(A_REVERSE); 431 } else { 432 rev_flag = 1; 433 (void) attron(A_REVERSE); 434 } 435 break; 436 437 case 'z': 438 if (SDBC_IOCTL(SDBC_ZAP_STATS, 0, 0, 0, 0, 0, 439 &ustats) == SPCS_S_ERROR) { 440 if (ustats) { 441 spcs_s_report(ustats, stderr); 442 spcs_s_ufree(&ustats); 443 } 444 } 445 break; 446 447 case 'm': 448 case 'M': 449 mirror_sw = mirror_sw ? 0 : 1; 450 (void) clear(); 451 break; 452 } 453 } 454 455 void 456 prheading(int count) 457 { 458 time_t tim; 459 460 /* 461 * Print sample count in upper left corner 462 */ 463 (void) mvprintw(0, 0, "SAMPLE %-8d", count); 464 465 /* 466 * Get time and print it in upper right corner 467 */ 468 tim = time((time_t *)0); 469 (void) mvprintw(0, 79 - 10, "%-8.8s\n", &(ctime(&tim)[11])); 470 } 471 472 /*ARGSUSED*/ 473 void 474 leave(int status) 475 { 476 (void) sigignore(SIGPIPE); 477 (void) sigignore(SIGALRM); 478 /* clear(); */ 479 (void) move(LINES, 0); 480 (void) refresh(); 481 if (logfd > -1) wrefresh_file(stdscr, logfd); 482 quit(0); 483 } 484 485 void 486 quit(int status) 487 { 488 (void) resetterm(); 489 (void) endwin(); 490 exit(status); 491 } 492 493 void 494 setup_ranges(char *range) 495 { 496 int ndx; 497 char chr1; 498 char prev_chr = '\0'; 499 int got_colon = 0; 500 int after_got_colon = 0; 501 int got_comma = 0; 502 int after_got_comma = 0; 503 int number = 0; 504 int prev_num = 0; 505 506 if (range == NULL || (strlen(range) == 0)) { 507 ranges[range_num].lb = 0; 508 ranges[range_num].ub = sdbc_max_devices - 1; 509 return; 510 } else { 511 ndx = 0; 512 got_comma = 0; 513 got_colon = 0; 514 while ((chr1 = (range[ndx++])) != '\0') { 515 switch (chr1) { 516 case '0': 517 case '1': 518 case '2': 519 case '3': 520 case '4': 521 case '5': 522 case '6': 523 case '7': 524 case '8': 525 case '9': 526 number = number*10 + (chr1 - '0'); 527 break; 528 case ':': 529 got_colon = 1; 530 break; 531 case ',': 532 got_comma = 1; 533 break; 534 default: /* ignore any unknown characters */ 535 break; 536 } /* switch */ 537 if (got_comma && after_got_colon) { 538 after_got_colon = 0; 539 got_comma = 0; 540 if (number >= sdbc_max_devices) 541 number = sdbc_max_devices - 1; 542 ranges[range_num].lb = prev_num; 543 ranges[range_num].ub = number; 544 if (range_num == 99) break; 545 range_num++; 546 number = 0; 547 } else if (got_colon && after_got_comma) { 548 got_colon = 0; 549 after_got_colon = 1; 550 after_got_comma = 0; 551 if (number >= sdbc_max_devices) 552 number = sdbc_max_devices - 1; 553 prev_num = number; 554 number = 0; 555 } else if (got_colon) { 556 got_colon = 0; 557 after_got_colon = 1; 558 if ((prev_chr != '\0') && (prev_chr != ':')) { 559 if (number >= sdbc_max_devices) 560 number = sdbc_max_devices - 1; 561 prev_num = number; 562 number = 0; 563 } 564 } else if (got_comma) { 565 got_comma = 0; 566 after_got_comma = 1; 567 after_got_colon = 0; 568 if (number >= sdbc_max_devices) 569 number = sdbc_max_devices -1; 570 if ((prev_chr != '\0') && (prev_chr != ',')) { 571 ranges[range_num].lb = number; 572 ranges[range_num].ub = number; 573 if (range_num == 99) break; 574 range_num++; 575 } 576 number = 0; 577 } /* if */ 578 prev_chr = chr1; 579 } /* while */ 580 if (number >= sdbc_max_devices) 581 number = sdbc_max_devices - 1; 582 if (after_got_colon) { 583 ranges[range_num].lb = prev_num; 584 ranges[range_num].ub = number; 585 } else { 586 if ((after_got_comma) && (prev_chr == ',')) 587 range_num--; 588 else { 589 ranges[range_num].lb = number; 590 ranges[range_num].ub = number; 591 } 592 } 593 } 594 } 595