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