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
USEC_START()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
USEC_READ()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
sd_stats_lintmain(int argc,char * argv[])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
checkbuf(int c)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
prheading(int count)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
leave(int status)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
quit(int status)486 quit(int status)
487 {
488 (void) resetterm();
489 (void) endwin();
490 exit(status);
491 }
492
493 void
setup_ranges(char * range)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