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.
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.
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]
22 * Copyright (c) 2008-2009, Intel Corporation.
23 * All Rights Reserved.
34 #include "latencytop.h"
36 #define CMPOPT(a, b) strncmp((a), (b), sizeof (b))
39 * This variable is used to check if "dynamic variable drop" in dtrace
42 boolean_t lt_drop_detected
= 0;
50 LT_CMDOPT_LOG_INTERVAL
,
51 LT_CMDOPT_CONFIG_FILE
,
57 LT_CMDOPT__LAST
/* Must be the last one */
61 * Check for duplicate command line options.
62 * Returns TRUE if duplicate options with different values are found,
63 * returns FALSE otherwise.
66 check_opt_dup(lt_cmd_option_id_t id
, uint64_t value
) {
68 static int opt_set
[(int)LT_CMDOPT__LAST
];
69 static uint64_t opt_val
[(int)LT_CMDOPT__LAST
];
71 const char *errmsg
[] = {
72 "-t is set more than once with different values.",
73 "-o is set more than once.",
74 "-k is set more than once with different values.",
75 "-l is set more than once with different values.",
76 "-c is set more than once.",
77 "-f [no]filter is set more than once with different values.",
78 "-f [no]sched is set more than once with different values.",
79 "-f [no]sobj is set more than once with different values.",
80 "-f [no]low is set more than once with different values.",
81 "-s is set more than once with different values."
84 g_assert(sizeof (errmsg
)/sizeof (errmsg
[0]) == (int)LT_CMDOPT__LAST
);
86 if (!opt_set
[(int)id
]) {
87 opt_set
[(int)id
] = TRUE
;
88 opt_val
[(int)id
] = value
;
92 if (opt_val
[(int)id
] != value
) {
93 (void) fprintf(stderr
, "%s\n", errmsg
[(int)id
]);
101 * Print command-line help message.
104 print_usage(const char *execname
, int long_help
)
106 char buffer
[PATH_MAX
];
107 (void) snprintf(buffer
, sizeof (buffer
), "%s", execname
);
110 /* Print short help to stderr. */
111 (void) fprintf(stderr
, "Usage: %s [option(s)], ",
113 (void) fprintf(stderr
, "use '%s -h' for details.\n",
118 (void) printf("Usage: %s [option(s)]\n", basename(buffer
));
119 (void) printf("Options:\n"
121 " Print this help.\n"
122 " -t, --interval TIME\n"
123 " Set refresh interval to TIME. "
124 "Valid range [1...60] seconds, default = 5\n"
126 * Option "-c, --config FILE" is not user-visible for now.
127 * When we have chance to properly document the format of translation
128 * rules, we'll make it user-visible.
130 " -o, --output-log-file FILE\n"
131 " Output kernel log to FILE. Default = "
132 DEFAULT_KLOG_FILE
"\n"
133 " -k, --kernel-log-level LEVEL\n"
134 " Set kernel log level to LEVEL.\n"
135 " 0(default) = None, 1 = Unmapped, 2 = Mapped, 3 = All.\n"
136 " -f, --feature [no]feature1,[no]feature2,...\n"
137 " Enable/disable features in LatencyTOP.\n"
139 " Filter large interruptible latencies, e.g. sleep.\n"
141 " Monitors sched (PID=0).\n"
143 " Monitors synchronization objects.\n"
145 " Lower overhead by sampling small latencies.\n"
146 " -l, --log-period TIME\n"
147 " Write and restart log every TIME seconds, TIME >= 60\n"
148 " -s --select [ pid=<pid> | pgid=<pgid> ]\n"
149 " Monitor only the given process or processes in the "
150 "given process group.\n");
154 * Properly exit latencytop when it receives SIGINT or SIGTERM.
158 signal_handler(int sig
)
164 * Convert string to integer. It returns error if extra characters are found.
167 to_int(const char *str
, int *result
)
172 if (str
== NULL
|| result
== NULL
) {
176 ret
= strtol(str
, &tail
, 10);
178 if (tail
!= NULL
&& *tail
!= '\0') {
191 main(int argc
, char *argv
[])
193 const char *opt_string
= "t:o:k:hf:l:c:s:";
194 struct option
const longopts
[] = {
195 {"interval", required_argument
, NULL
, 't'},
196 {"output-log-file", required_argument
, NULL
, 'o'},
197 {"kernel-log-level", required_argument
, NULL
, 'k'},
198 {"help", no_argument
, NULL
, 'h'},
199 {"feature", required_argument
, NULL
, 'f'},
200 {"log-period", required_argument
, NULL
, 'l'},
201 {"config", required_argument
, NULL
, 'c'},
202 {"select", required_argument
, NULL
, 's'},
209 int unknown_option
= FALSE
;
210 int refresh_interval
= 5;
212 int log_interval
= 0;
213 long long last_logged
= 0;
218 uint64_t collect_end
;
219 uint64_t current_time
;
221 char logfile
[PATH_MAX
] = "";
225 boolean_t no_dtrace_cleanup
= B_TRUE
;
228 (void) signal(SIGINT
, signal_handler
);
229 (void) signal(SIGTERM
, signal_handler
);
231 /* Default global settings */
232 g_config
.lt_cfg_enable_filter
= 0;
233 g_config
.lt_cfg_trace_sched
= 0;
234 g_config
.lt_cfg_trace_syncobj
= 1;
235 g_config
.lt_cfg_low_overhead_mode
= 0;
236 g_config
.lt_cfg_trace_pid
= 0;
237 g_config
.lt_cfg_trace_pgid
= 0;
238 /* dtrace snapshot every 1 second */
239 g_config
.lt_cfg_snap_interval
= 1000;
241 g_config
.lt_cfg_config_name
= NULL
;
243 g_config
.lt_cfg_config_name
= lt_strdup(DEFAULT_CONFIG_NAME
);
246 /* Parse command line arguments. */
247 while ((optc
= getopt_long(argc
, argv
, opt_string
,
248 longopts
, &longind
)) != -1) {
251 print_usage(argv
[0], TRUE
);
254 if (to_int(optarg
, &refresh_interval
) != 0 ||
255 refresh_interval
< 1 || refresh_interval
> 60) {
257 "Invalid refresh interval: %s\n", optarg
);
258 unknown_option
= TRUE
;
259 } else if (check_opt_dup(LT_CMDOPT_INTERVAL
,
261 unknown_option
= TRUE
;
266 if (to_int(optarg
, &klog_level
) != 0 ||
267 lt_klog_set_log_level(klog_level
) != 0) {
269 "Invalid log level: %s\n", optarg
);
270 unknown_option
= TRUE
;
271 } else if (check_opt_dup(LT_CMDOPT_LOG_LEVEL
,
273 unknown_option
= TRUE
;
278 if (check_opt_dup(LT_CMDOPT_LOG_FILE
, optind
)) {
279 unknown_option
= TRUE
;
280 } else if (strlen(optarg
) >= sizeof (logfile
)) {
282 "Log file name is too long: %s\n",
284 unknown_option
= TRUE
;
286 (void) strncpy(logfile
, optarg
,
292 for (token
= strtok(optarg
, ","); token
!= NULL
;
293 token
= strtok(NULL
, ",")) {
296 if (strncmp(token
, "no", 2) == 0) {
301 if (CMPOPT(token
, "filter") == 0) {
302 if (check_opt_dup(LT_CMDOPT_F_FILTER
,
304 unknown_option
= TRUE
;
306 g_config
.lt_cfg_enable_filter
309 } else if (CMPOPT(token
, "sched") == 0) {
310 if (check_opt_dup(LT_CMDOPT_F_SCHED
,
312 unknown_option
= TRUE
;
314 g_config
.lt_cfg_trace_sched
317 } else if (CMPOPT(token
, "sobj") == 0) {
318 if (check_opt_dup(LT_CMDOPT_F_SOBJ
,
320 unknown_option
= TRUE
;
322 g_config
.lt_cfg_trace_syncobj
325 } else if (CMPOPT(token
, "low") == 0) {
326 if (check_opt_dup(LT_CMDOPT_F_LOW
,
328 unknown_option
= TRUE
;
331 lt_cfg_low_overhead_mode
336 "Unknown feature: %s\n", token
);
337 unknown_option
= TRUE
;
343 if (to_int(optarg
, &log_interval
) != 0 ||
346 "Invalid log interval: %s\n", optarg
);
347 unknown_option
= TRUE
;
348 } else if (check_opt_dup(LT_CMDOPT_LOG_INTERVAL
,
350 unknown_option
= TRUE
;
355 if (strlen(optarg
) >= PATH_MAX
) {
357 "Configuration name is too long.\n");
358 unknown_option
= TRUE
;
359 } else if (check_opt_dup(LT_CMDOPT_CONFIG_FILE
,
361 unknown_option
= TRUE
;
363 g_config
.lt_cfg_config_name
=
369 if (strncmp(optarg
, "pid=", 4) == 0) {
371 select_str
= &optarg
[4];
372 } else if (strncmp(optarg
, "pgid=", 5) == 0) {
374 select_str
= &optarg
[5];
377 "Invalid select option: %s\n", optarg
);
378 unknown_option
= TRUE
;
382 if (to_int(select_str
, &select_value
) != 0) {
384 "Invalid select option: %s\n", optarg
);
385 unknown_option
= TRUE
;
389 if (select_value
<= 0) {
391 "Process/process group ID must be "
392 "greater than 0: %s\n", optarg
);
393 unknown_option
= TRUE
;
397 if (check_opt_dup(LT_CMDOPT_SELECT
,
398 (((uint64_t)select_id
) << 32) | select_value
)) {
399 unknown_option
= TRUE
;
403 if (select_id
== 0) {
404 g_config
.lt_cfg_trace_pid
= select_value
;
406 g_config
.lt_cfg_trace_pgid
= select_value
;
410 unknown_option
= TRUE
;
415 if (!unknown_option
&& strlen(logfile
) > 0) {
416 err
= lt_klog_set_log_file(logfile
);
419 lt_display_error("Log file name is too long: %s\n",
421 unknown_option
= TRUE
;
422 } else if (err
== -2) {
423 lt_display_error("Cannot write to log file: %s\n",
425 unknown_option
= TRUE
;
429 /* Throw error for invalid/junk arguments */
432 (void) fprintf(stderr
, "Unknown option(s): ");
434 while (tmpind
< argc
) {
435 (void) fprintf(stderr
, "%s ", argv
[tmpind
++]);
438 (void) fprintf(stderr
, "\n");
439 unknown_option
= TRUE
;
442 if (unknown_option
) {
443 print_usage(argv
[0], FALSE
);
448 (void) printf("%s\n%s\n", TITLE
, COPYRIGHT
);
455 if (lt_table_init() != 0) {
456 lt_display_error("Unable to load configuration table.\n");
461 if (lt_dtrace_init() != 0) {
462 lt_display_error("Unable to initialize dtrace.\n");
467 last_logged
= lt_millisecond();
469 (void) printf("Collecting data for %d seconds...\n",
472 gpipe
= lt_gpipe_readfd();
473 collect_end
= last_logged
+ refresh_interval
* 1000;
476 struct timeval timeout
;
477 int tsleep
= collect_end
- lt_millisecond();
484 * Interval when we call dtrace_status() and collect
487 if (tsleep
> g_config
.lt_cfg_snap_interval
) {
488 tsleep
= g_config
.lt_cfg_snap_interval
;
491 timeout
.tv_sec
= tsleep
/ 1000;
492 timeout
.tv_usec
= (tsleep
% 1000) * 1000;
495 FD_SET(gpipe
, &read_fd
);
497 if (select(gpipe
+ 1, &read_fd
, NULL
, NULL
, &timeout
) > 0) {
501 (void) lt_dtrace_work(0);
507 current_time
= lt_millisecond();
510 (void) lt_dtrace_collect();
512 delta_time
= current_time
;
513 current_time
= lt_millisecond();
514 delta_time
= current_time
- delta_time
;
516 if (log_interval
> 0 &&
517 current_time
- last_logged
> log_interval
* 1000) {
519 last_logged
= current_time
;
522 running
= lt_display_loop(refresh_interval
* 1000 -
526 * This is to avoid dynamic variable drop
529 if (lt_drop_detected
== B_TRUE
) {
530 if (lt_dtrace_deinit() != 0) {
531 no_dtrace_cleanup
= B_FALSE
;
536 lt_drop_detected
= B_FALSE
;
537 if (lt_dtrace_init() != 0) {
542 } while (running
!= 0);
550 if (no_dtrace_cleanup
== B_FALSE
|| lt_dtrace_deinit() != 0)
564 if (g_config
.lt_cfg_config_name
!= NULL
) {
565 free(g_config
.lt_cfg_config_name
);