1 /* 2 * This file is part of ltrace. 3 * Copyright (C) 2012, 2013, 2014 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 46 struct 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 62 static char *progname; /* Program name (`ltrace') */ 63 int opt_i = 0; /* instruction pointer */ 64 int opt_r = 0; /* print relative timestamp */ 65 int opt_t = 0; /* print absolute timestamp */ 66 int opt_T = 0; /* show the time spent inside each call */ 67 68 /* List of pids given to option -p: */ 69 struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */ 70 71 /* Vector of struct opt_F_t. */ 72 struct vect opt_F; 73 74 static void 75 err_usage(void) { 76 fprintf(stderr, "Try `%s --help' for more information.\n", progname); 77 exit(1); 78 } 79 80 static void 81 usage(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_UNWINDER) 111 " -w, --where=NR print backtrace showing NR stack frames at most.\n" 112 #endif /* defined(HAVE_UNWINDER) */ 113 " -x FILTER modify which static functions to trace.\n" 114 "\nReport bugs to ltrace-devel (at) lists.alioth.debian.org\n", 115 progname); 116 } 117 118 static void 119 usage_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 "Also note that these values are used inconsistently in ltrace, and the\n" 132 "only debuglevel that you can rely on is -D77 that will show everything.\n" 133 ); 134 } 135 136 static char * 137 search_for_command(char *filename) { 138 static char pathname[PATH_MAX]; 139 char *path; 140 int m, n; 141 142 if (strchr(filename, '/')) { 143 return filename; 144 } 145 for (path = getenv("PATH"); path && *path; path += m) { 146 if (strchr(path, ':')) { 147 n = strchr(path, ':') - path; 148 m = n + 1; 149 } else { 150 m = n = strlen(path); 151 } 152 if (n + strlen(filename) + 1 >= PATH_MAX) { 153 fprintf(stderr, "Error: filename too long.\n"); 154 exit(1); 155 } 156 strncpy(pathname, path, n); 157 if (n && pathname[n - 1] != '/') { 158 pathname[n++] = '/'; 159 } 160 strcpy(pathname + n, filename); 161 if (!access(pathname, X_OK)) { 162 return pathname; 163 } 164 } 165 return filename; 166 } 167 168 static void 169 guess_cols(void) { 170 struct winsize ws; 171 char *c; 172 173 options.align = DEFAULT_ALIGN; 174 c = getenv("COLUMNS"); 175 if (c && *c) { 176 char *endptr; 177 int cols; 178 cols = strtol(c, &endptr, 0); 179 if (cols > 0 && !*endptr) { 180 options.align = cols * 5 / 8; 181 } 182 } else if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) { 183 options.align = ws.ws_col * 5 / 8; 184 } else if (ioctl(2, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) { 185 options.align = ws.ws_col * 5 / 8; 186 } 187 } 188 189 static int 190 compile_libname(const char *expr, const char *a_lib, int lib_re_p, 191 struct filter_lib_matcher *matcher) 192 { 193 if (strcmp(a_lib, "MAIN") == 0) { 194 filter_lib_matcher_main_init(matcher); 195 } else { 196 /* Add ^ and $ to the library expression as well. */ 197 char lib[strlen(a_lib) + 3]; 198 sprintf(lib, "^%s$", a_lib); 199 200 enum filter_lib_matcher_type type 201 = lib[0] == '/' ? FLM_PATHNAME : FLM_SONAME; 202 203 regex_t lib_re; 204 int status = (lib_re_p ? regcomp : globcomp)(&lib_re, lib, 0); 205 if (status != 0) { 206 char buf[100]; 207 regerror(status, &lib_re, buf, sizeof buf); 208 fprintf(stderr, "Couldn't compile '%s': %s.\n", 209 expr, buf); 210 return -1; 211 } 212 filter_lib_matcher_name_init(matcher, type, lib_re); 213 } 214 return 0; 215 } 216 217 static int 218 add_filter_rule(struct filter *filt, const char *expr, 219 enum filter_rule_type type, 220 const char *a_sym, int sym_re_p, 221 const char *a_lib, int lib_re_p) 222 { 223 struct filter_rule *rule = malloc(sizeof(*rule)); 224 struct filter_lib_matcher *matcher = malloc(sizeof(*matcher)); 225 226 if (rule == NULL || matcher == NULL) { 227 fail: 228 free(rule); 229 free(matcher); 230 return -1; 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, "Couldn't compile '%s': %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 return 0; 259 } 260 261 static int 262 grok_libname_pattern(char **libnamep, char **libendp) 263 { 264 char *libname = *libnamep; 265 char *libend = *libendp; 266 267 if (libend[0] != '/') 268 return 0; 269 270 *libend-- = 0; 271 if (libname != libend && libname[0] == '/') 272 ++libname; 273 else 274 fprintf(stderr, "Unmatched '/' in library name.\n"); 275 276 *libendp = libend; 277 *libnamep = libname; 278 return 1; 279 } 280 281 static int 282 parse_filter(struct filter *filt, char *expr, int operators) 283 { 284 /* Filter is a chain of sym@lib rules separated by '-' or '+'. 285 * If the filter expression starts with '-', the missing 286 * initial rule is implicitly *@*. */ 287 288 enum filter_rule_type type = FR_ADD; 289 290 while (*expr != 0) { 291 size_t s = strcspn(expr, &"-+@"[operators ? 0 : 2]); 292 char *symname = expr; 293 char *libname; 294 char *next = expr + s + 1; 295 enum filter_rule_type this_type = type; 296 297 if (expr[s] == 0) { 298 libname = "*"; 299 expr = next - 1; 300 301 } else if (expr[s] == '-' || expr[s] == '+') { 302 type = expr[s] == '-' ? FR_SUBTRACT : FR_ADD; 303 expr[s] = 0; 304 libname = "*"; 305 expr = next; 306 307 } else { 308 assert(expr[s] == '@'); 309 expr[s] = 0; 310 s = strcspn(next, &"-+"[operators ? 0 : 2]); 311 if (s == 0) { 312 libname = "*"; 313 expr = next; 314 } else if (next[s] == 0) { 315 expr = next + s; 316 libname = next; 317 } else { 318 assert(next[s] == '-' || next[s] == '+'); 319 type = next[s] == '-' ? FR_SUBTRACT : FR_ADD; 320 next[s] = 0; 321 expr = next + s + 1; 322 libname = next; 323 } 324 } 325 326 assert(*libname != 0); 327 char *symend = symname + strlen(symname) - 1; 328 char *libend = libname + strlen(libname) - 1; 329 int sym_is_re = 0; 330 int lib_is_re = 0; 331 332 /* 333 * /xxx/@... and ...@/xxx/ means that xxx are regular 334 * expressions. They are globs otherwise. 335 * 336 * /xxx@yyy/ is the same as /xxx/@/yyy/ 337 * 338 * @/xxx matches library path name 339 * @.xxx matches library relative path name 340 */ 341 if (symname[0] == '/') { 342 if (symname != symend && symend[0] == '/') { 343 ++symname; 344 *symend-- = 0; 345 sym_is_re = 1; 346 347 } else { 348 sym_is_re = 1; 349 lib_is_re = 1; 350 ++symname; 351 352 /* /XXX@YYY/ is the same as 353 * /XXX/@/YYY/. */ 354 if (libend[0] != '/') 355 fprintf(stderr, "Unmatched '/'" 356 " in symbol name.\n"); 357 else 358 *libend-- = 0; 359 } 360 } 361 362 /* If libname ends in '/', then we expect '/' in the 363 * beginning too. Otherwise the initial '/' is part 364 * of absolute file name. */ 365 if (!lib_is_re) 366 lib_is_re = grok_libname_pattern(&libname, &libend); 367 368 if (*symname == 0) /* /@AA/ */ 369 symname = "*"; 370 if (*libname == 0) /* /aa@/ */ 371 libname = "*"; 372 373 add_filter_rule(filt, expr, this_type, 374 symname, sym_is_re, 375 libname, lib_is_re); 376 } 377 378 return 0; 379 } 380 381 static struct filter * 382 recursive_parse_chain(const char *orig, char *expr, int operators) 383 { 384 struct filter *filt = malloc(sizeof(*filt)); 385 if (filt == NULL) { 386 fprintf(stderr, "(Part of) filter will be ignored: '%s': %s.\n", 387 expr, strerror(errno)); 388 return NULL; 389 } 390 391 filter_init(filt); 392 if (parse_filter(filt, expr, operators) < 0) { 393 fprintf(stderr, "Filter '%s' will be ignored.\n", orig); 394 free(filt); 395 filt = NULL; 396 } 397 398 return filt; 399 } 400 401 static struct filter ** 402 slist_chase_end(struct filter **begin) 403 { 404 for (; *begin != NULL; begin = &(*begin)->next) 405 ; 406 return begin; 407 } 408 409 static void 410 parse_filter_chain(const char *expr, struct filter **retp) 411 { 412 char *str = strdup(expr); 413 if (str == NULL) { 414 fprintf(stderr, "Filter '%s' will be ignored: %s.\n", 415 expr, strerror(errno)); 416 return; 417 } 418 /* Support initial '!' for backward compatibility. */ 419 if (str[0] == '!') 420 str[0] = '-'; 421 422 *slist_chase_end(retp) = recursive_parse_chain(expr, str, 1); 423 free(str); 424 } 425 426 static int 427 parse_int(const char *optarg, char opt, int min, int max) 428 { 429 char *endptr; 430 long int l = strtol(optarg, &endptr, 0); 431 if (l < min || (max != 0 && l > max) 432 || *optarg == 0 || *endptr != 0) { 433 const char *fmt = max != 0 434 ? "Invalid argument to -%c: '%s'. Use integer %d..%d.\n" 435 : "Invalid argument to -%c: '%s'. Use integer >=%d.\n"; 436 fprintf(stderr, fmt, opt, optarg, min, max); 437 exit(1); 438 } 439 return (int)l; 440 } 441 442 int 443 parse_colon_separated_list(const char *paths, struct vect *vec) 444 { 445 /* PATHS contains a colon-separated list of directories and 446 * files to load. It's modeled after shell PATH variable, 447 * which doesn't allow escapes. PYTHONPATH in CPython behaves 448 * the same way. So let's follow suit, it makes things easier 449 * to us. */ 450 451 char *clone = strdup(paths); 452 if (clone == NULL) { 453 fprintf(stderr, "Couldn't parse argument %s: %s.\n", 454 paths, strerror(errno)); 455 return -1; 456 } 457 458 /* It's undesirable to use strtok, because we want the string 459 * "a::b" to have three elements. */ 460 char *tok = clone - 1; 461 char *end = clone + strlen(clone); 462 while (tok < end) { 463 ++tok; 464 size_t len = strcspn(tok, ":"); 465 tok[len] = 0; 466 467 struct opt_F_t arg = { 468 .pathname = tok, 469 .own_pathname = tok == clone, 470 }; 471 if (VECT_PUSHBACK(vec, &arg) < 0) 472 /* Presumably this is not a deal-breaker. */ 473 fprintf(stderr, "Couldn't store component of %s: %s.\n", 474 paths, strerror(errno)); 475 476 tok += len; 477 } 478 479 return 0; 480 } 481 482 void 483 opt_F_destroy(struct opt_F_t *entry) 484 { 485 if (entry == NULL) 486 return; 487 if (entry->own_pathname) 488 free(entry->pathname); 489 } 490 491 enum opt_F_kind 492 opt_F_get_kind(struct opt_F_t *entry) 493 { 494 if (entry->kind == OPT_F_UNKNOWN) { 495 struct stat st; 496 if (lstat(entry->pathname, &st) < 0) { 497 fprintf(stderr, "Couldn't stat %s: %s\n", 498 entry->pathname, strerror(errno)); 499 entry->kind = OPT_F_BROKEN; 500 } else if (S_ISDIR(st.st_mode)) { 501 entry->kind = OPT_F_DIR; 502 } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { 503 entry->kind = OPT_F_FILE; 504 } else { 505 fprintf(stderr, "%s is neither a regular file, " 506 "nor a directory.\n", entry->pathname); 507 entry->kind = OPT_F_BROKEN; 508 } 509 } 510 assert(entry->kind != OPT_F_UNKNOWN); 511 return entry->kind; 512 } 513 514 char ** 515 process_options(int argc, char **argv) 516 { 517 VECT_INIT(&opt_F, struct opt_F_t); 518 519 progname = argv[0]; 520 options.output = stderr; 521 options.no_signals = 0; 522 #if defined(HAVE_UNWINDER) 523 options.bt_depth = -1; 524 #endif /* defined(HAVE_UNWINDER) */ 525 526 guess_cols(); 527 528 int libcalls = 1; 529 530 while (1) { 531 int c; 532 char *p; 533 #ifdef HAVE_GETOPT_LONG 534 int option_index = 0; 535 static struct option long_options[] = { 536 {"align", 1, 0, 'a'}, 537 {"config", 1, 0, 'F'}, 538 {"debug", 1, 0, 'D'}, 539 # ifdef USE_DEMANGLE 540 {"demangle", 0, 0, 'C'}, 541 # endif 542 {"indent", 1, 0, 'n'}, 543 {"help", 0, 0, 'h'}, 544 {"library", 1, 0, 'l'}, 545 {"output", 1, 0, 'o'}, 546 {"version", 0, 0, 'V'}, 547 {"no-signals", 0, 0, 'b'}, 548 # if defined(HAVE_UNWINDER) 549 {"where", 1, 0, 'w'}, 550 # endif /* defined(HAVE_UNWINDER) */ 551 {0, 0, 0, 0} 552 }; 553 #endif 554 555 const char *opts = "+" 556 #ifdef USE_DEMANGLE 557 "C" 558 #endif 559 #if defined(HAVE_UNWINDER) 560 "w:" 561 #endif 562 "cfhiLrStTVba:A:D:e:F:l:n:o:p:s:u:x:"; 563 564 #ifdef HAVE_GETOPT_LONG 565 c = getopt_long(argc, argv, opts, long_options, &option_index); 566 #else 567 c = getopt(argc, argv, opts); 568 #endif 569 if (c == -1) { 570 break; 571 } 572 switch (c) { 573 case 'a': 574 options.align = parse_int(optarg, 'a', 0, 0); 575 break; 576 case 'A': 577 options.arraylen = parse_int(optarg, 'A', 0, 0); 578 break; 579 case 'b': 580 options.no_signals = 1; 581 break; 582 case 'c': 583 options.summary++; 584 break; 585 #ifdef USE_DEMANGLE 586 case 'C': 587 options.demangle++; 588 break; 589 #endif 590 case 'D': 591 if (optarg[0]=='h') { 592 usage_debug(); 593 exit(0); 594 } 595 options.debug = strtoul(optarg,&p,8); 596 if (*p) { 597 fprintf(stderr, "%s: --debug requires an octal argument\n", progname); 598 err_usage(); 599 } 600 break; 601 602 case 'e': 603 parse_filter_chain(optarg, &options.plt_filter); 604 break; 605 606 case 'f': 607 options.follow = 1; 608 break; 609 case 'F': 610 parse_colon_separated_list(optarg, &opt_F); 611 break; 612 case 'h': 613 usage(); 614 exit(0); 615 case 'i': 616 opt_i++; 617 break; 618 619 case 'l': { 620 size_t patlen = strlen(optarg); 621 char buf[patlen + 2]; 622 sprintf(buf, "@%s", optarg); 623 *slist_chase_end(&options.export_filter) 624 = recursive_parse_chain(buf, buf, 0); 625 break; 626 } 627 628 case 'L': 629 libcalls = 0; 630 break; 631 case 'n': 632 options.indent = parse_int(optarg, 'n', 0, 20); 633 break; 634 case 'o': 635 options.output = fopen(optarg, "w"); 636 if (!options.output) { 637 fprintf(stderr, 638 "can't open %s for writing: %s\n", 639 optarg, strerror(errno)); 640 exit(1); 641 } 642 setvbuf(options.output, (char *)NULL, _IOLBF, 0); 643 fcntl(fileno(options.output), F_SETFD, FD_CLOEXEC); 644 break; 645 case 'p': 646 { 647 struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t)); 648 if (!tmp) { 649 perror("ltrace: malloc"); 650 exit(1); 651 } 652 tmp->pid = parse_int(optarg, 'p', 1, 0); 653 tmp->next = opt_p; 654 opt_p = tmp; 655 break; 656 } 657 case 'r': 658 opt_r++; 659 break; 660 case 's': 661 options.strlen = parse_int(optarg, 's', 0, 0); 662 break; 663 case 'S': 664 options.syscalls = 1; 665 break; 666 case 't': 667 opt_t++; 668 break; 669 case 'T': 670 opt_T++; 671 break; 672 case 'u': 673 options.user = optarg; 674 break; 675 case 'V': 676 printf("ltrace " PACKAGE_VERSION "\n" 677 "Copyright (C) 2010-2013 Petr Machata, Red Hat Inc.\n" 678 "Copyright (C) 1997-2009 Juan Cespedes <cespedes (at) debian.org>.\n" 679 "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n" 680 "This is free software: you are free to change and redistribute it.\n" 681 "There is NO WARRANTY, to the extent permitted by law.\n"); 682 exit(0); 683 break; 684 #if defined(HAVE_UNWINDER) 685 case 'w': 686 options.bt_depth = parse_int(optarg, 'w', 1, 0); 687 break; 688 #endif /* defined(HAVE_UNWINDER) */ 689 690 case 'x': 691 parse_filter_chain(optarg, &options.static_filter); 692 break; 693 694 default: 695 err_usage(); 696 } 697 } 698 argc -= optind; 699 argv += optind; 700 701 /* If neither -e, nor -l, nor -L are used, set default -e. 702 * Use @MAIN for now, as that's what ltrace used to have in 703 * the past. XXX Maybe we should make this "*" instead. */ 704 if (libcalls 705 && options.plt_filter == NULL 706 && options.export_filter == NULL) { 707 parse_filter_chain("@MAIN", &options.plt_filter); 708 options.hide_caller = 1; 709 } 710 if (!libcalls && options.plt_filter != NULL) { 711 fprintf(stderr, 712 "%s: Option -L can't be used with -e or -l.\n", 713 progname); 714 err_usage(); 715 } 716 717 if (!opt_p && argc < 1) { 718 fprintf(stderr, "%s: too few arguments\n", progname); 719 err_usage(); 720 } 721 if (opt_r && opt_t) { 722 fprintf(stderr, 723 "%s: Options -r and -t can't be used together\n", 724 progname); 725 err_usage(); 726 } 727 if (argc > 0) { 728 command = search_for_command(argv[0]); 729 } 730 return &argv[0]; 731 } 732