options.c revision fa2aefc32eb4196966a14d1e7def931652f40d46
1#include "config.h" 2 3#include <string.h> 4#include <stdlib.h> 5#include <unistd.h> 6#include <fcntl.h> 7#include <errno.h> 8#include <limits.h> 9#include <sys/ioctl.h> 10 11#include <getopt.h> 12 13#include "common.h" 14 15#ifndef SYSCONFDIR 16#define SYSCONFDIR "/etc" 17#endif 18 19#define SYSTEM_CONFIG_FILE SYSCONFDIR "/ltrace.conf" 20#define USER_CONFIG_FILE "~/.ltrace.conf" 21 22struct options_t options = { 23 .align = DEFAULT_ALIGN, /* alignment column for results */ 24 .user = NULL, /* username to run command as */ 25 .syscalls = 0, /* display syscalls */ 26 .libcalls = 1, /* display library calls */ 27#ifdef USE_DEMANGLE 28 .demangle = 0, /* Demangle low-level symbol names */ 29#endif 30 .indent = 0, /* indent output according to program flow */ 31 .output = NULL, /* output to a specific file */ 32 .summary = 0, /* Report a summary on program exit */ 33 .debug = 0, /* debug */ 34 .arraylen = DEFAULT_ARRAYLEN, /* maximum # array elements to print */ 35 .strlen = DEFAULT_STRLEN, /* maximum # of bytes printed in strings */ 36 .follow = 0, /* trace child processes */ 37}; 38 39char *library[MAX_LIBRARIES]; 40int library_num = 0; 41static char *progname; /* Program name (`ltrace') */ 42int opt_i = 0; /* instruction pointer */ 43int opt_r = 0; /* print relative timestamp */ 44int opt_t = 0; /* print absolute timestamp */ 45int opt_T = 0; /* show the time spent inside each call */ 46 47/* List of pids given to option -p: */ 48struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */ 49 50/* List of function names given to option -e: */ 51struct opt_e_t *opt_e = NULL; 52int opt_e_enable = 1; 53 54/* List of global function names given to -x: */ 55struct opt_x_t *opt_x = NULL; 56 57/* List of filenames give to option -F: */ 58struct opt_F_t *opt_F = NULL; /* alternate configuration file(s) */ 59 60#ifdef PLT_REINITALISATION_BP 61/* Set a break on the routine named here in order to re-initialize breakpoints 62 after all the PLTs have been initialzed */ 63char *PLTs_initialized_by_here = PLT_REINITALISATION_BP; 64#endif 65 66static void 67err_usage(void) { 68 fprintf(stderr, "Try `%s --help' for more information\n", progname); 69 exit(1); 70} 71 72static void 73usage(void) { 74 fprintf(stdout, "Usage: %s [option ...] [command [arg ...]]\n" 75 "Trace library calls of a given program.\n\n" 76 " -a, --align=COLUMN align return values in a secific column.\n" 77 " -A ARRAYLEN maximum number of array elements to print.\n" 78 " -c count time and calls, and report a summary on exit.\n" 79# ifdef USE_DEMANGLE 80 " -C, --demangle decode low-level symbol names into user-level names.\n" 81# endif 82 " -D, --debug=LEVEL enable debugging (see -Dh or --debug=help).\n" 83 " -Dh, --debug=help show help on debugging.\n" 84 " -e expr modify which events to trace.\n" 85 " -f trace children (fork() and clone()).\n" 86 " -F, --config=FILE load alternate configuration file (may be repeated).\n" 87 " -g, --no-plt disable breakpoints on PLT entries.\n" 88 " -h, --help display this help and exit.\n" 89 " -i print instruction pointer at time of library call.\n" 90 " -l, --library=FILE print library calls from this library only.\n" 91 " -L do NOT display library calls.\n" 92 " -n, --indent=NR indent output by NR spaces for each call level nesting.\n" 93 " -o, --output=FILE write the trace output to that file.\n" 94 " -p PID attach to the process with the process ID pid.\n" 95 " -r print relative timestamps.\n" 96 " -s STRLEN specify the maximum string size to print.\n" 97 " -S display system calls.\n" 98 " -t, -tt, -ttt print absolute timestamps.\n" 99 " -T show the time spent inside each call.\n" 100 " -u USERNAME run command with the userid, groupid of username.\n" 101 " -V, --version output version information and exit.\n" 102 " -x NAME treat the global NAME like a library subroutine.\n" 103#ifdef PLT_REINITALISATION_BP 104 " -X NAME same as -x; and PLT's will be initialized by here.\n" 105#endif 106 "\nReport bugs to ltrace-devel@lists.alioth.debian.org\n", 107 progname); 108} 109 110static void 111usage_debug(void) { 112 fprintf(stdout, "%s debugging option, --debug=<octal> or -D<octal>:\n", progname); 113 fprintf(stdout, 114 "\n" 115 " number ref. in source description\n" 116 " 1 general Generally helpful progress information\n" 117 " 10 event Shows every event received by a traced process\n" 118 " 20 process Shows actions carried upon a traced processes\n" 119 " 40 function Shows every entry to internal functions\n" 120 "\n" 121 "Debugging options are mixed using bitwise-or.\n" 122 "Note that the meanings and values are subject to change.\n" 123 ); 124} 125 126static char * 127search_for_command(char *filename) { 128 static char pathname[PATH_MAX]; 129 char *path; 130 int m, n; 131 132 if (strchr(filename, '/')) { 133 return filename; 134 } 135 for (path = getenv("PATH"); path && *path; path += m) { 136 if (strchr(path, ':')) { 137 n = strchr(path, ':') - path; 138 m = n + 1; 139 } else { 140 m = n = strlen(path); 141 } 142 if (n + strlen(filename) + 1 >= PATH_MAX) { 143 fprintf(stderr, "Error: filename too long\n"); 144 exit(1); 145 } 146 strncpy(pathname, path, n); 147 if (n && pathname[n - 1] != '/') { 148 pathname[n++] = '/'; 149 } 150 strcpy(pathname + n, filename); 151 if (!access(pathname, X_OK)) { 152 return pathname; 153 } 154 } 155 return filename; 156} 157 158static void 159guess_cols(void) { 160 struct winsize ws; 161 char *c; 162 163 options.align = DEFAULT_ALIGN; 164 c = getenv("COLUMNS"); 165 if (c && *c) { 166 char *endptr; 167 int cols; 168 cols = strtol(c, &endptr, 0); 169 if (cols > 0 && !*endptr) { 170 options.align = cols * 5 / 8; 171 } 172 } else if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) { 173 options.align = ws.ws_col * 5 / 8; 174 } else if (ioctl(2, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) { 175 options.align = ws.ws_col * 5 / 8; 176 } 177} 178 179char ** 180process_options(int argc, char **argv) { 181 progname = argv[0]; 182 options.output = stderr; 183 options.no_plt = 0; 184 185 guess_cols(); 186 187 while (1) { 188 int c; 189 char *p; 190 int option_index = 0; 191 static struct option long_options[] = { 192 {"align", 1, 0, 'a'}, 193 {"config", 1, 0, 'F'}, 194 {"debug", 1, 0, 'D'}, 195# ifdef USE_DEMANGLE 196 {"demangle", 0, 0, 'C'}, 197#endif 198 {"indent", 1, 0, 'n'}, 199 {"help", 0, 0, 'h'}, 200 {"library", 1, 0, 'l'}, 201 {"output", 1, 0, 'o'}, 202 {"version", 0, 0, 'V'}, 203 {"no-plt", 0, 0, 'g'}, 204 {0, 0, 0, 0} 205 }; 206 c = getopt_long(argc, argv, "+cfhiLrStTVg" 207# ifdef USE_DEMANGLE 208 "C" 209# endif 210 "a:A:D:e:F:l:n:o:p:s:u:x:X:", long_options, 211 &option_index); 212 if (c == -1) { 213 break; 214 } 215 switch (c) { 216 case 'a': 217 options.align = atoi(optarg); 218 break; 219 case 'A': 220 options.arraylen = atoi(optarg); 221 break; 222 case 'c': 223 options.summary++; 224 break; 225#ifdef USE_DEMANGLE 226 case 'C': 227 options.demangle++; 228 break; 229#endif 230 case 'D': 231 if (optarg[0]=='h') { 232 usage_debug(); 233 exit(0); 234 } 235 options.debug = strtoul(optarg,&p,8); 236 if (*p) { 237 fprintf(stderr, "%s: --debug requires an octal argument\n", progname); 238 err_usage(); 239 } 240 break; 241 case 'e': 242 { 243 char *str_e = strdup(optarg); 244 if (!str_e) { 245 perror("ltrace: strdup"); 246 exit(1); 247 } 248 if (str_e[0] == '!') { 249 opt_e_enable = 0; 250 str_e++; 251 } 252 while (*str_e) { 253 struct opt_e_t *tmp; 254 char *str2 = strchr(str_e, ','); 255 if (str2) { 256 *str2 = '\0'; 257 } 258 tmp = malloc(sizeof(struct opt_e_t)); 259 if (!tmp) { 260 perror("ltrace: malloc"); 261 exit(1); 262 } 263 tmp->name = str_e; 264 tmp->next = opt_e; 265 opt_e = tmp; 266 if (str2) { 267 str_e = str2 + 1; 268 } else { 269 break; 270 } 271 } 272 break; 273 } 274 case 'f': 275 options.follow = 1; 276 break; 277 case 'F': 278 { 279 struct opt_F_t *tmp = malloc(sizeof(struct opt_F_t)); 280 if (!tmp) { 281 perror("ltrace: malloc"); 282 exit(1); 283 } 284 tmp->filename = strdup(optarg); 285 tmp->next = opt_F; 286 opt_F = tmp; 287 break; 288 } 289 case 'g': 290 options.no_plt = 1; 291 break; 292 case 'h': 293 usage(); 294 exit(0); 295 case 'i': 296 opt_i++; 297 break; 298 case 'l': 299 if (library_num == MAX_LIBRARIES) { 300 fprintf(stderr, 301 "Too many libraries. Maximum is %i.\n", 302 MAX_LIBRARIES); 303 exit(1); 304 } 305 library[library_num++] = optarg; 306 break; 307 case 'L': 308 options.libcalls = 0; 309 break; 310 case 'n': 311 options.indent = atoi(optarg); 312 break; 313 case 'o': 314 options.output = fopen(optarg, "w"); 315 if (!options.output) { 316 fprintf(stderr, 317 "Can't open %s for output: %s\n", 318 optarg, strerror(errno)); 319 exit(1); 320 } 321 setvbuf(options.output, (char *)NULL, _IOLBF, 0); 322 fcntl(fileno(options.output), F_SETFD, FD_CLOEXEC); 323 break; 324 case 'p': 325 { 326 struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t)); 327 if (!tmp) { 328 perror("ltrace: malloc"); 329 exit(1); 330 } 331 tmp->pid = atoi(optarg); 332 tmp->next = opt_p; 333 opt_p = tmp; 334 break; 335 } 336 case 'r': 337 opt_r++; 338 break; 339 case 's': 340 options.strlen = atoi(optarg); 341 break; 342 case 'S': 343 options.syscalls = 1; 344 break; 345 case 't': 346 opt_t++; 347 break; 348 case 'T': 349 opt_T++; 350 break; 351 case 'u': 352 options.user = optarg; 353 break; 354 case 'V': 355 printf("ltrace version " PACKAGE_VERSION ".\n" 356 "Copyright (C) 1997-2009 Juan Cespedes <cespedes@debian.org>.\n" 357 "This is free software; see the GNU General Public Licence\n" 358 "version 2 or later for copying conditions. There is NO warranty.\n"); 359 exit(0); 360 break; 361 case 'X': 362#ifdef PLT_REINITALISATION_BP 363 PLTs_initialized_by_here = optarg; 364#else 365 fprintf(stderr, "WARNING: \"-X\" not used for this " 366 "architecture: assuming you meant \"-x\"\n"); 367#endif 368 /* Fall Thru */ 369 370 case 'x': 371 { 372 struct opt_x_t *p = opt_x; 373 374 /* First, check for duplicate. */ 375 while (p && strcmp(p->name, optarg)) { 376 p = p->next; 377 } 378 if (p) { 379 break; 380 } 381 382 /* If not duplicate, add to list. */ 383 p = malloc(sizeof(struct opt_x_t)); 384 if (!p) { 385 perror("ltrace: malloc"); 386 exit(1); 387 } 388 p->name = optarg; 389 p->found = 0; 390 p->next = opt_x; 391 opt_x = p; 392 break; 393 } 394 395 default: 396 err_usage(); 397 } 398 } 399 argc -= optind; 400 argv += optind; 401 402 if (!opt_F) { 403 opt_F = malloc(sizeof(struct opt_F_t)); 404 opt_F->next = malloc(sizeof(struct opt_F_t)); 405 opt_F->next->next = NULL; 406 opt_F->filename = USER_CONFIG_FILE; 407 opt_F->next->filename = SYSTEM_CONFIG_FILE; 408 } 409 /* Reverse the config file list since it was built by 410 * prepending, and it would make more sense to process the 411 * files in the order they were given. Probably it would make 412 * more sense to keep a tail pointer instead? */ 413 { 414 struct opt_F_t *egg = NULL; 415 struct opt_F_t *chicken; 416 while (opt_F) { 417 chicken = opt_F->next; 418 opt_F->next = egg; 419 egg = opt_F; 420 opt_F = chicken; 421 } 422 opt_F = egg; 423 } 424 425 if (!opt_p && argc < 1) { 426 fprintf(stderr, "%s: too few arguments\n", progname); 427 err_usage(); 428 } 429 if (opt_r && opt_t) { 430 fprintf(stderr, "%s: Incompatible options -r and -t\n", 431 progname); 432 err_usage(); 433 } 434 if (argc > 0) { 435 command = search_for_command(argv[0]); 436 } 437 return &argv[0]; 438} 439