xref: /titanic_50/usr/src/cmd/avs/sdbc/sd_stats.c (revision f38cb554a534c6df738be3f4d23327e69888e634)
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