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