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