xref: /titanic_51/usr/src/cmd/latencytop/common/latencytop.c (revision 62b885670cb2373723bdad8e9fc26c05f8dabfb6)
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 (c) 2008-2009, Intel Corporation.
23  * All Rights Reserved.
24  */
25 
26 #include <unistd.h>
27 #include <getopt.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <limits.h>
32 #include <libgen.h>
33 #include <signal.h>
34 #include "latencytop.h"
35 
36 #define	CMPOPT(a, b)	strncmp((a), (b), sizeof (b))
37 
38 lt_config_t g_config;
39 
40 /*
41  * Prints help for command line parameters.
42  */
43 static void
44 print_usage(const char *execname)
45 {
46 	char buffer[PATH_MAX];
47 	(void) snprintf(buffer, sizeof (buffer), "%s", execname);
48 	(void) fprintf(stderr, "\nUsage: %s [option(s)]\n", basename(buffer));
49 	(void) fprintf(stderr, "Options:\n"
50 	    "    -h, --help\n"
51 	    "        Print this help.\n"
52 	    "    -t, --interval TIME\n"
53 	    "        Set refresh interval to TIME. "
54 	    "Valid range [1...60] seconds, default = 5\n"
55 	/*
56 	 * Keep this option private, until we have chance to properly document
57 	 * the format of translation rules.
58 	 */
59 #if 0
60 	    "    -c, --config FILE\n"
61 	    "        Use translation rules defined in FILE.\n"
62 #endif
63 	    "    -o, --output-log-file FILE\n"
64 	    "        Output kernel log to FILE. Default = "
65 	    DEFAULT_KLOG_FILE "\n"
66 	    "    -k, --kernel-log-level LEVEL\n"
67 	    "        Set kernel log level to LEVEL.\n"
68 	    "        0(default) = None, 1 = Unmapped, 2 = Mapped, 3 = All.\n"
69 	    "    -f, --feature [no]feature1,[no]feature2,...\n"
70 	    "        Enable/disable features in LatencyTOP.\n"
71 	    "        [no]filter:\n"
72 	    "        Filter large interruptible latencies, e.g. sleep.\n"
73 	    "        [no]sched:\n"
74 	    "        Monitors sched (PID=0).\n"
75 	    "        [no]sobj:\n"
76 	    "        Monitors synchronization objects.\n"
77 	    "        [no]low:\n"
78 	    "        Lower overhead by sampling small latencies.\n"
79 	    "    -l, --log-period TIME\n"
80 	    "        Write and restart log every TIME seconds, TIME > 60s\n");
81 }
82 
83 /*
84  * Properly shut down when latencytop receives SIGINT or SIGTERM.
85  */
86 /* ARGSUSED */
87 static void
88 signal_handler(int sig)
89 {
90 	lt_gpipe_break("q");
91 }
92 
93 /*
94  * Convert string to integer, return error if extra characters are found.
95  */
96 static int
97 to_int(const char *str, int *result)
98 {
99 	char *tail = NULL;
100 	long ret;
101 
102 	if (str == NULL || result == NULL) {
103 		return (-1);
104 	}
105 
106 	ret = strtol(str, &tail, 10);
107 
108 	if (tail != NULL && *tail != '\0') {
109 		return (-1);
110 	}
111 
112 	*result = (int)ret;
113 
114 	return (0);
115 }
116 
117 /*
118  * The main function.
119  */
120 int
121 main(int argc, char *argv[])
122 {
123 	const char *opt_string = "t:o:k:hf:l:c:";
124 	struct option const longopts[] =
125 	{
126 		{"interval", required_argument, NULL, 't'},
127 		{"output-log-file", required_argument, NULL, 'o'},
128 		{"kernel-log-level", required_argument, NULL, 'k'},
129 		{"help", no_argument, NULL, 'h'},
130 		{"feature", required_argument, NULL, 'f'},
131 		{"log-period", required_argument, NULL, 'l'},
132 		{"config", required_argument, NULL, 'c'},
133 		{NULL, 0, NULL, 0}
134 	};
135 
136 	int optc;
137 	int longind = 0;
138 	int running = 1;
139 	int unknown_option = FALSE;
140 	int refresh_interval = 5;
141 	int klog_level = 0;
142 	int log_interval = 0;
143 	long long last_logged = 0;
144 	char *token = NULL;
145 	int retval = 0;
146 	int gpipe;
147 	int err;
148 	uint64_t collect_end;
149 	uint64_t current_time;
150 	uint64_t delta_time;
151 
152 	lt_gpipe_init();
153 	(void) signal(SIGINT, signal_handler);
154 	(void) signal(SIGTERM, signal_handler);
155 
156 	(void) printf("%s\n%s\n", TITLE, COPYRIGHT);
157 
158 	/* Default global settings */
159 	g_config.enable_filter = 0;
160 	g_config.trace_sched = 0;
161 	g_config.trace_syncobj = 1;
162 	g_config.low_overhead_mode = 0;
163 	g_config.snap_interval = 1000;	/* DTrace snapshot every 1 sec */
164 #ifdef EMBED_CONFIGS
165 	g_config.config_name = NULL;
166 #else
167 	g_config.config_name = lt_strdup(DEFAULT_CONFIG_NAME);
168 #endif
169 
170 	/* Parse command line arguments. */
171 	while ((optc = getopt_long(argc, argv, opt_string,
172 	    longopts, &longind)) != -1) {
173 		switch (optc) {
174 		case 'h':
175 			print_usage(argv[0]);
176 			goto end_none;
177 		case 't':
178 			if (to_int(optarg, &refresh_interval) != 0 ||
179 			    refresh_interval < 1 || refresh_interval > 60) {
180 				lt_display_error(
181 				    "Invalid refresh interval: %s\n", optarg);
182 				unknown_option = TRUE;
183 			}
184 			break;
185 		case 'k':
186 			if (to_int(optarg, &klog_level) != 0 ||
187 			    lt_klog_set_log_level(klog_level) != 0) {
188 				lt_display_error(
189 				    "Invalid log level: %s\n", optarg);
190 				unknown_option = TRUE;
191 			}
192 			break;
193 		case 'o':
194 			err = lt_klog_set_log_file(optarg);
195 			if (err == 0) {
196 				(void) printf("Writing to log file %s.\n",
197 				    optarg);
198 			} else if (err == -1) {
199 				lt_display_error(
200 				    "Log file name is too long: %s\n",
201 				    optarg);
202 				unknown_option = TRUE;
203 			} else if (err == -2) {
204 				lt_display_error(
205 				    "Cannot write to log file: %s\n",
206 				    optarg);
207 				unknown_option = TRUE;
208 			}
209 			break;
210 		case 'f':
211 			for (token = strtok(optarg, ","); token != NULL;
212 			    token = strtok(NULL, ",")) {
213 				int v = TRUE;
214 				if (strncmp(token, "no", 2) == 0) {
215 					v = FALSE;
216 					token = &token[2];
217 				}
218 				if (CMPOPT(token, "filter") == 0) {
219 					g_config.enable_filter = v;
220 				} else if (CMPOPT(token, "sched") == 0) {
221 					g_config.trace_sched = v;
222 				} else if (CMPOPT(token, "sobj") == 0) {
223 					g_config.trace_syncobj = v;
224 				} else if (CMPOPT(token, "low") == 0) {
225 					g_config.low_overhead_mode = v;
226 				} else {
227 					lt_display_error(
228 					    "Unknown feature: %s\n", token);
229 					unknown_option = TRUE;
230 				}
231 			}
232 			break;
233 		case 'l':
234 			if (to_int(optarg, &log_interval) != 0 ||
235 			    log_interval < 60) {
236 				lt_display_error(
237 				    "Invalid refresh interval: %s\n", optarg);
238 				unknown_option = TRUE;
239 			}
240 			break;
241 		case 'c':
242 			if (strlen(optarg) > PATH_MAX) {
243 				lt_display_error(
244 				    "Configuration name is too long.\n");
245 				unknown_option = TRUE;
246 			} else {
247 				g_config.config_name = lt_strdup(optarg);
248 			}
249 			break;
250 		default:
251 			unknown_option = TRUE;
252 			break;
253 		}
254 	}
255 
256 	/* Throw error for commands like: "latencytop 12345678" */
257 	if (optind  < argc) {
258 		int tmpind = optind;
259 		(void) printf("Unknown option(s): ");
260 		while (tmpind < argc) {
261 			(void) printf("%s ", argv[tmpind++]);
262 		}
263 		(void) printf("\n");
264 		unknown_option = TRUE;
265 	}
266 
267 	if (unknown_option) {
268 		print_usage(argv[0]);
269 		retval = 1;
270 		goto end_none;
271 	}
272 
273 	/* Initialization */
274 	lt_klog_init();
275 	if (lt_table_init() != 0) {
276 		lt_display_error("Unable to load configuration table.\n");
277 		retval = 1;
278 		goto end_notable;
279 	}
280 	if (lt_dtrace_init() != 0) {
281 		lt_display_error("Unable to initialize dtrace.\n");
282 		retval = 1;
283 		goto end_nodtrace;
284 	}
285 
286 	last_logged = lt_millisecond();
287 
288 	(void) printf("Collecting data for %d seconds...\n",
289 	    refresh_interval);
290 
291 	gpipe = lt_gpipe_readfd();
292 	collect_end = last_logged + refresh_interval * 1000;
293 	for (;;) {
294 		fd_set read_fd;
295 		struct timeval timeout;
296 		int tsleep = collect_end - lt_millisecond();
297 
298 		if (tsleep <= 0) {
299 			break;
300 		}
301 
302 		if (tsleep > g_config.snap_interval * 1000) {
303 			tsleep = g_config.snap_interval * 1000;
304 		}
305 
306 		timeout.tv_sec = tsleep / 1000;
307 		timeout.tv_usec = (tsleep % 1000) * 1000;
308 
309 		FD_ZERO(&read_fd);
310 		FD_SET(gpipe, &read_fd);
311 		if (select(gpipe + 1, &read_fd, NULL, NULL, &timeout) > 0) {
312 			goto end_ubreak;
313 		}
314 
315 		(void) lt_dtrace_work(0);
316 	}
317 
318 	lt_display_init();
319 
320 	do {
321 		current_time = lt_millisecond();
322 
323 		lt_stat_clear_all();
324 		(void) lt_dtrace_collect();
325 
326 		delta_time = current_time;
327 		current_time = lt_millisecond();
328 		delta_time = current_time - delta_time;
329 
330 		if (log_interval > 0 &&
331 		    current_time - last_logged > log_interval * 1000) {
332 			lt_klog_write();
333 			last_logged = current_time;
334 		}
335 
336 		running = lt_display_loop(refresh_interval * 1000 -
337 		    delta_time);
338 	} while (running != 0);
339 
340 	lt_klog_write();
341 
342 	/* Cleanup */
343 	lt_display_deinit();
344 
345 end_ubreak:
346 	lt_dtrace_deinit();
347 	lt_stat_free_all();
348 
349 end_nodtrace:
350 	lt_table_deinit();
351 
352 end_notable:
353 	lt_klog_deinit();
354 
355 end_none:
356 	lt_gpipe_deinit();
357 	if (g_config.config_name != NULL) {
358 		free(g_config.config_name);
359 	}
360 
361 	return (retval);
362 }
363