options.c revision 418584d25da07c5a9f7a61b74cfe713c1f8d7085
1/* 2 * This file is part of ltrace. 3 * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc. 4 * Copyright (C) 2009,2010 Joe Damato 5 * Copyright (C) 1998,1999,2002,2003,2004,2007,2008,2009 Juan Cespedes 6 * Copyright (C) 2006 Ian Wienand 7 * Copyright (C) 2006 Steve Fink 8 * Copyright (C) 2006 Paul Gilliam, IBM Corporation 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public License as 12 * published by the Free Software Foundation; either version 2 of the 13 * License, or (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, but 16 * WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 23 * 02110-1301 USA 24 */ 25 26#include "config.h" 27 28#include <sys/ioctl.h> 29#include <sys/stat.h> 30#include <sys/types.h> 31#include <assert.h> 32#include <errno.h> 33#include <fcntl.h> 34#include <getopt.h> 35#include <limits.h> 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39#include <unistd.h> 40 41#include "common.h" 42#include "filter.h" 43#include "glob.h" 44#include "demangle.h" 45 46struct options_t options = { 47 .align = DEFAULT_ALIGN, /* alignment column for results */ 48 .user = NULL, /* username to run command as */ 49 .syscalls = 0, /* display syscalls */ 50#ifdef USE_DEMANGLE 51 .demangle = 0, /* Demangle low-level symbol names */ 52#endif 53 .indent = 0, /* indent output according to program flow */ 54 .output = NULL, /* output to a specific file */ 55 .summary = 0, /* Report a summary on program exit */ 56 .debug = 0, /* debug */ 57 .arraylen = DEFAULT_ARRAYLEN, /* maximum # array elements to print */ 58 .strlen = DEFAULT_STRLEN, /* maximum # of bytes printed in strings */ 59 .follow = 0, /* trace child processes */ 60}; 61 62static char *progname; /* Program name (`ltrace') */ 63int opt_i = 0; /* instruction pointer */ 64int opt_r = 0; /* print relative timestamp */ 65int opt_t = 0; /* print absolute timestamp */ 66int opt_T = 0; /* show the time spent inside each call */ 67 68/* List of pids given to option -p: */ 69struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */ 70 71/* Vector of struct opt_F_t. */ 72struct vect opt_F; 73 74static void 75err_usage(void) { 76 fprintf(stderr, "Try `%s --help' for more information.\n", progname); 77 exit(1); 78} 79 80static void 81usage(void) { 82 fprintf(stdout, "Usage: %s [option ...] [command [arg ...]]\n" 83 "Trace library calls of a given program.\n\n" 84 " -a, --align=COLUMN align return values in a secific column.\n" 85 " -A MAXELTS maximum number of array elements to print.\n" 86 " -b, --no-signals don't print signals.\n" 87 " -c count time and calls, and report a summary on exit.\n" 88# ifdef USE_DEMANGLE 89 " -C, --demangle decode low-level symbol names into user-level names.\n" 90# endif 91 " -D, --debug=MASK enable debugging (see -Dh or --debug=help).\n" 92 " -Dh, --debug=help show help on debugging.\n" 93 " -e FILTER modify which library calls to trace.\n" 94 " -f trace children (fork() and clone()).\n" 95 " -F, --config=FILE load alternate configuration file (may be repeated).\n" 96 " -h, --help display this help and exit.\n" 97 " -i print instruction pointer at time of library call.\n" 98 " -l, --library=LIBRARY_PATTERN only trace symbols implemented by this library.\n" 99 " -L do NOT display library calls.\n" 100 " -n, --indent=NR indent output by NR spaces for each call level nesting.\n" 101 " -o, --output=FILENAME write the trace output to file with given name.\n" 102 " -p PID attach to the process with the process ID pid.\n" 103 " -r print relative timestamps.\n" 104 " -s STRSIZE specify the maximum string size to print.\n" 105 " -S trace system calls as well as library calls.\n" 106 " -t, -tt, -ttt print absolute timestamps.\n" 107 " -T show the time spent inside each call.\n" 108 " -u USERNAME run command with the userid, groupid of username.\n" 109 " -V, --version output version information and exit.\n" 110#if defined(HAVE_LIBUNWIND) 111 " -w, --where=NR print backtrace showing NR stack frames at most.\n" 112#endif /* defined(HAVE_LIBUNWIND) */ 113 " -x FILTER modify which static functions to trace.\n" 114 "\nReport bugs to ltrace-devel@lists.alioth.debian.org\n", 115 progname); 116} 117 118static void 119usage_debug(void) { 120 fprintf(stdout, "%s debugging option, --debug=<octal> or -D<octal>:\n", progname); 121 fprintf(stdout, 122 "\n" 123 " number ref. in source description\n" 124 " 1 general Generally helpful progress information\n" 125 " 10 event Shows every event received by a traced process\n" 126 " 20 process Shows actions carried upon a traced processes\n" 127 " 40 function Shows every entry to internal functions\n" 128 "\n" 129 "Debugging options are mixed using bitwise-or.\n" 130 "Note that the meanings and values are subject to change.\n" 131 ); 132} 133 134static char * 135search_for_command(char *filename) { 136 static char pathname[PATH_MAX]; 137 char *path; 138 int m, n; 139 140 if (strchr(filename, '/')) { 141 return filename; 142 } 143 for (path = getenv("PATH"); path && *path; path += m) { 144 if (strchr(path, ':')) { 145 n = strchr(path, ':') - path; 146 m = n + 1; 147 } else { 148 m = n = strlen(path); 149 } 150 if (n + strlen(filename) + 1 >= PATH_MAX) { 151 fprintf(stderr, "Error: filename too long.\n"); 152 exit(1); 153 } 154 strncpy(pathname, path, n); 155 if (n && pathname[n - 1] != '/') { 156 pathname[n++] = '/'; 157 } 158 strcpy(pathname + n, filename); 159 if (!access(pathname, X_OK)) { 160 return pathname; 161 } 162 } 163 return filename; 164} 165 166static void 167guess_cols(void) { 168 struct winsize ws; 169 char *c; 170 171 options.align = DEFAULT_ALIGN; 172 c = getenv("COLUMNS"); 173 if (c && *c) { 174 char *endptr; 175 int cols; 176 cols = strtol(c, &endptr, 0); 177 if (cols > 0 && !*endptr) { 178 options.align = cols * 5 / 8; 179 } 180 } else if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) { 181 options.align = ws.ws_col * 5 / 8; 182 } else if (ioctl(2, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) { 183 options.align = ws.ws_col * 5 / 8; 184 } 185} 186 187static int 188compile_libname(const char *expr, const char *a_lib, int lib_re_p, 189 struct filter_lib_matcher *matcher) 190{ 191 if (strcmp(a_lib, "MAIN") == 0) { 192 filter_lib_matcher_main_init(matcher); 193 } else { 194 /* Add ^ and $ to the library expression as well. */ 195 char lib[strlen(a_lib) + 3]; 196 sprintf(lib, "^%s$", a_lib); 197 198 enum filter_lib_matcher_type type 199 = lib[0] == '/' ? FLM_PATHNAME : FLM_SONAME; 200 201 regex_t lib_re; 202 int status = (lib_re_p ? regcomp : globcomp)(&lib_re, lib, 0); 203 if (status != 0) { 204 char buf[100]; 205 regerror(status, &lib_re, buf, sizeof buf); 206 fprintf(stderr, "Rule near '%s' will be ignored: %s.\n", 207 expr, buf); 208 return -1; 209 } 210 filter_lib_matcher_name_init(matcher, type, lib_re); 211 } 212 return 0; 213} 214 215static void 216add_filter_rule(struct filter *filt, const char *expr, 217 enum filter_rule_type type, 218 const char *a_sym, int sym_re_p, 219 const char *a_lib, int lib_re_p) 220{ 221 struct filter_rule *rule = malloc(sizeof(*rule)); 222 struct filter_lib_matcher *matcher = malloc(sizeof(*matcher)); 223 224 if (rule == NULL || matcher == NULL) { 225 fprintf(stderr, "Rule near '%s' will be ignored: %s.\n", 226 expr, strerror(errno)); 227 fail: 228 free(rule); 229 free(matcher); 230 return; 231 } 232 233 regex_t symbol_re; 234 { 235 /* Add ^ to the start of expression and $ to the end, so that 236 * we match the whole symbol name. Let the user write the "*" 237 * explicitly if they wish. */ 238 char sym[strlen(a_sym) + 3]; 239 sprintf(sym, "^%s$", a_sym); 240 int status = (sym_re_p ? regcomp : globcomp) 241 (&symbol_re, sym, 0); 242 if (status != 0) { 243 char buf[100]; 244 regerror(status, &symbol_re, buf, sizeof buf); 245 fprintf(stderr, "Rule near '%s' will be ignored: %s.\n", 246 expr, buf); 247 goto fail; 248 } 249 } 250 251 if (compile_libname(expr, a_lib, lib_re_p, matcher) < 0) { 252 regfree(&symbol_re); 253 goto fail; 254 } 255 256 filter_rule_init(rule, type, matcher, symbol_re); 257 filter_add_rule(filt, rule); 258} 259 260static int 261grok_libname_pattern(char **libnamep, char **libendp) 262{ 263 char *libname = *libnamep; 264 char *libend = *libendp; 265 266 if (libend[0] != '/') 267 return 0; 268 269 *libend-- = 0; 270 if (libname != libend && libname[0] == '/') 271 ++libname; 272 else 273 fprintf(stderr, "Unmatched '/' in library name.\n"); 274 275 *libendp = libend; 276 *libnamep = libname; 277 return 1; 278} 279 280static int 281parse_filter(struct filter *filt, char *expr, int operators) 282{ 283 /* Filter is a chain of sym@lib rules separated by '-' or '+'. 284 * If the filter expression starts with '-', the missing 285 * initial rule is implicitly *@*. */ 286 287 enum filter_rule_type type = FR_ADD; 288 289 while (*expr != 0) { 290 size_t s = strcspn(expr, &"-+@"[operators ? 0 : 2]); 291 char *symname = expr; 292 char *libname; 293 char *next = expr + s + 1; 294 enum filter_rule_type this_type = type; 295 296 if (expr[s] == 0) { 297 libname = "*"; 298 expr = next - 1; 299 300 } else if (expr[s] == '-' || expr[s] == '+') { 301 type = expr[s] == '-' ? FR_SUBTRACT : FR_ADD; 302 expr[s] = 0; 303 libname = "*"; 304 expr = next; 305 306 } else { 307 assert(expr[s] == '@'); 308 expr[s] = 0; 309 s = strcspn(next, &"-+"[operators ? 0 : 2]); 310 if (s == 0) { 311 libname = "*"; 312 expr = next; 313 } else if (next[s] == 0) { 314 expr = next + s; 315 libname = next; 316 } else { 317 assert(next[s] == '-' || next[s] == '+'); 318 type = next[s] == '-' ? FR_SUBTRACT : FR_ADD; 319 next[s] = 0; 320 expr = next + s + 1; 321 libname = next; 322 } 323 } 324 325 assert(*libname != 0); 326 char *symend = symname + strlen(symname) - 1; 327 char *libend = libname + strlen(libname) - 1; 328 int sym_is_re = 0; 329 int lib_is_re = 0; 330 331 /* 332 * /xxx/@... and ...@/xxx/ means that xxx are regular 333 * expressions. They are globs otherwise. 334 * 335 * /xxx@yyy/ is the same as /xxx/@/yyy/ 336 * 337 * @/xxx matches library path name 338 * @.xxx matches library relative path name 339 */ 340 if (symname[0] == '/') { 341 if (symname != symend && symend[0] == '/') { 342 ++symname; 343 *symend-- = 0; 344 sym_is_re = 1; 345 346 } else { 347 sym_is_re = 1; 348 lib_is_re = 1; 349 ++symname; 350 351 /* /XXX@YYY/ is the same as 352 * /XXX/@/YYY/. */ 353 if (libend[0] != '/') 354 fprintf(stderr, "Unmatched '/'" 355 " in symbol name.\n"); 356 else 357 *libend-- = 0; 358 } 359 } 360 361 /* If libname ends in '/', then we expect '/' in the 362 * beginning too. Otherwise the initial '/' is part 363 * of absolute file name. */ 364 if (!lib_is_re) 365 lib_is_re = grok_libname_pattern(&libname, &libend); 366 367 if (*symname == 0) /* /@AA/ */ 368 symname = "*"; 369 if (*libname == 0) /* /aa@/ */ 370 libname = "*"; 371 372 add_filter_rule(filt, expr, this_type, 373 symname, sym_is_re, 374 libname, lib_is_re); 375 } 376 377 return 0; 378} 379 380static struct filter * 381recursive_parse_chain(char *expr, int operators) 382{ 383 struct filter *filt = malloc(sizeof(*filt)); 384 if (filt == NULL) { 385 fprintf(stderr, "(Part of) filter will be ignored: '%s': %s.\n", 386 expr, strerror(errno)); 387 return NULL; 388 } 389 390 filter_init(filt); 391 if (parse_filter(filt, expr, operators) < 0) { 392 fprintf(stderr, "Filter '%s' will be ignored.\n", expr); 393 free(filt); 394 filt = NULL; 395 } 396 397 return filt; 398} 399 400static struct filter ** 401slist_chase_end(struct filter **begin) 402{ 403 for (; *begin != NULL; begin = &(*begin)->next) 404 ; 405 return begin; 406} 407 408static void 409parse_filter_chain(const char *expr, struct filter **retp) 410{ 411 char *str = strdup(expr); 412 if (str == NULL) { 413 fprintf(stderr, "Filter '%s' will be ignored: %s.\n", 414 expr, strerror(errno)); 415 return; 416 } 417 /* Support initial '!' for backward compatibility. */ 418 if (str[0] == '!') 419 str[0] = '-'; 420 421 *slist_chase_end(retp) = recursive_parse_chain(str, 1); 422 free(str); 423} 424 425static int 426parse_int(const char *optarg, char opt, int min, int max) 427{ 428 char *endptr; 429 long int l = strtol(optarg, &endptr, 0); 430 if (l < min || (max != 0 && l > max) 431 || *optarg == 0 || *endptr != 0) { 432 const char *fmt = max != 0 433 ? "Invalid argument to -%c: '%s'. Use integer %d..%d.\n" 434 : "Invalid argument to -%c: '%s'. Use integer >=%d.\n"; 435 fprintf(stderr, fmt, opt, optarg, min, max); 436 exit(1); 437 } 438 return (int)l; 439} 440 441int 442parse_colon_separated_list(const char *paths, struct vect *vec) 443{ 444 /* PATHS contains a colon-separated list of directories and 445 * files to load. It's modeled after shell PATH variable, 446 * which doesn't allow escapes. PYTHONPATH in CPython behaves 447 * the same way. So let's follow suit, it makes things easier 448 * to us. */ 449 450 char *clone = strdup(paths); 451 if (clone == NULL) { 452 fprintf(stderr, "Couldn't parse argument %s: %s.\n", 453 paths, strerror(errno)); 454 return -1; 455 } 456 457 /* It's undesirable to use strtok, because we want the string 458 * "a::b" to have three elements. */ 459 char *tok = clone - 1; 460 char *end = clone + strlen(clone); 461 while (tok < end) { 462 ++tok; 463 size_t len = strcspn(tok, ":"); 464 tok[len] = 0; 465 466 struct opt_F_t arg = { 467 .pathname = tok, 468 .own_pathname = tok == clone, 469 }; 470 if (VECT_PUSHBACK(vec, &arg) < 0) 471 /* Presumably this is not a deal-breaker. */ 472 fprintf(stderr, "Couldn't store component of %s: %s.\n", 473 paths, strerror(errno)); 474 475 tok += len; 476 } 477 478 return 0; 479} 480 481void 482opt_F_destroy(struct opt_F_t *entry) 483{ 484 if (entry == NULL) 485 return; 486 if (entry->own_pathname) 487 free(entry->pathname); 488} 489 490enum opt_F_kind 491opt_F_get_kind(struct opt_F_t *entry) 492{ 493 if (entry->kind == OPT_F_UNKNOWN) { 494 struct stat st; 495 if (lstat(entry->pathname, &st) < 0) { 496 fprintf(stderr, "Couldn't stat %s: %s\n", 497 entry->pathname, strerror(errno)); 498 entry->kind = OPT_F_BROKEN; 499 } else if (S_ISDIR(st.st_mode)) { 500 entry->kind = OPT_F_DIR; 501 } else if (S_ISREG(st.st_mode)) { 502 entry->kind = OPT_F_FILE; 503 } else { 504 fprintf(stderr, "%s is neither a regular file, " 505 "nor a directory.\n", entry->pathname); 506 entry->kind = OPT_F_BROKEN; 507 } 508 } 509 assert(entry->kind != OPT_F_UNKNOWN); 510 return entry->kind; 511} 512 513char ** 514process_options(int argc, char **argv) 515{ 516 VECT_INIT(&opt_F, struct opt_F_t); 517 518 progname = argv[0]; 519 options.output = stderr; 520 options.no_signals = 0; 521#if defined(HAVE_LIBUNWIND) 522 options.bt_depth = -1; 523#endif /* defined(HAVE_LIBUNWIND) */ 524 525 guess_cols(); 526 527 int libcalls = 1; 528 529 while (1) { 530 int c; 531 char *p; 532#ifdef HAVE_GETOPT_LONG 533 int option_index = 0; 534 static struct option long_options[] = { 535 {"align", 1, 0, 'a'}, 536 {"config", 1, 0, 'F'}, 537 {"debug", 1, 0, 'D'}, 538# ifdef USE_DEMANGLE 539 {"demangle", 0, 0, 'C'}, 540# endif 541 {"indent", 1, 0, 'n'}, 542 {"help", 0, 0, 'h'}, 543 {"library", 1, 0, 'l'}, 544 {"output", 1, 0, 'o'}, 545 {"version", 0, 0, 'V'}, 546 {"no-signals", 0, 0, 'b'}, 547# if defined(HAVE_LIBUNWIND) 548 {"where", 1, 0, 'w'}, 549# endif /* defined(HAVE_LIBUNWIND) */ 550 {0, 0, 0, 0} 551 }; 552#endif 553 554 const char *opts = "+" 555#ifdef USE_DEMANGLE 556 "C" 557#endif 558#if defined(HAVE_LIBUNWIND) 559 "w:" 560#endif 561 "cfhiLrStTVba:A:D:e:F:l:n:o:p:s:u:x:X:"; 562 563#ifdef HAVE_GETOPT_LONG 564 c = getopt_long(argc, argv, opts, long_options, &option_index); 565#else 566 c = getopt(argc, argv, opts); 567#endif 568 if (c == -1) { 569 break; 570 } 571 switch (c) { 572 case 'a': 573 options.align = parse_int(optarg, 'a', 0, 0); 574 break; 575 case 'A': 576 options.arraylen = parse_int(optarg, 'A', 0, 0); 577 break; 578 case 'b': 579 options.no_signals = 1; 580 break; 581 case 'c': 582 options.summary++; 583 break; 584#ifdef USE_DEMANGLE 585 case 'C': 586 options.demangle++; 587 break; 588#endif 589 case 'D': 590 if (optarg[0]=='h') { 591 usage_debug(); 592 exit(0); 593 } 594 options.debug = strtoul(optarg,&p,8); 595 if (*p) { 596 fprintf(stderr, "%s: --debug requires an octal argument\n", progname); 597 err_usage(); 598 } 599 break; 600 601 case 'e': 602 parse_filter_chain(optarg, &options.plt_filter); 603 break; 604 605 case 'f': 606 options.follow = 1; 607 break; 608 case 'F': 609 parse_colon_separated_list(optarg, &opt_F); 610 break; 611 case 'h': 612 usage(); 613 exit(0); 614 case 'i': 615 opt_i++; 616 break; 617 618 case 'l': { 619 size_t patlen = strlen(optarg); 620 char buf[patlen + 2]; 621 sprintf(buf, "@%s", optarg); 622 *slist_chase_end(&options.export_filter) 623 = recursive_parse_chain(buf, 0); 624 break; 625 } 626 627 case 'L': 628 libcalls = 0; 629 break; 630 case 'n': 631 options.indent = parse_int(optarg, 'n', 0, 20); 632 break; 633 case 'o': 634 options.output = fopen(optarg, "w"); 635 if (!options.output) { 636 fprintf(stderr, 637 "can't open %s for writing: %s\n", 638 optarg, strerror(errno)); 639 exit(1); 640 } 641 setvbuf(options.output, (char *)NULL, _IOLBF, 0); 642 fcntl(fileno(options.output), F_SETFD, FD_CLOEXEC); 643 break; 644 case 'p': 645 { 646 struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t)); 647 if (!tmp) { 648 perror("ltrace: malloc"); 649 exit(1); 650 } 651 tmp->pid = parse_int(optarg, 'p', 1, 0); 652 tmp->next = opt_p; 653 opt_p = tmp; 654 break; 655 } 656 case 'r': 657 opt_r++; 658 break; 659 case 's': 660 options.strlen = parse_int(optarg, 's', 0, 0); 661 break; 662 case 'S': 663 options.syscalls = 1; 664 break; 665 case 't': 666 opt_t++; 667 break; 668 case 'T': 669 opt_T++; 670 break; 671 case 'u': 672 options.user = optarg; 673 break; 674 case 'V': 675 printf("ltrace " PACKAGE_VERSION "\n" 676 "Copyright (C) 2010-2012 Petr Machata, Red Hat Inc.\n" 677 "Copyright (C) 1997-2009 Juan Cespedes <cespedes@debian.org>.\n" 678 "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n" 679 "This is free software: you are free to change and redistribute it.\n" 680 "There is NO WARRANTY, to the extent permitted by law.\n"); 681 exit(0); 682 break; 683#if defined(HAVE_LIBUNWIND) 684 case 'w': 685 options.bt_depth = parse_int(optarg, 'w', 1, 0); 686 break; 687#endif /* defined(HAVE_LIBUNWIND) */ 688 689 case 'x': 690 parse_filter_chain(optarg, &options.static_filter); 691 break; 692 693 default: 694 err_usage(); 695 } 696 } 697 argc -= optind; 698 argv += optind; 699 700 /* If neither -e, nor -l, nor -L are used, set default -e. 701 * Use @MAIN for now, as that's what ltrace used to have in 702 * the past. XXX Maybe we should make this "*" instead. */ 703 if (libcalls 704 && options.plt_filter == NULL 705 && options.export_filter == NULL) { 706 parse_filter_chain("@MAIN", &options.plt_filter); 707 options.hide_caller = 1; 708 } 709 if (!libcalls && options.plt_filter != NULL) { 710 fprintf(stderr, 711 "%s: Option -L can't be used with -e or -l.\n", 712 progname); 713 err_usage(); 714 } 715 716 if (!opt_p && argc < 1) { 717 fprintf(stderr, "%s: too few arguments\n", progname); 718 err_usage(); 719 } 720 if (opt_r && opt_t) { 721 fprintf(stderr, 722 "%s: Options -r and -t can't be used together\n", 723 progname); 724 err_usage(); 725 } 726 if (argc > 0) { 727 command = search_for_command(argv[0]); 728 } 729 return &argv[0]; 730} 731