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