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
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.
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.
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
28 #include <sys/ioctl.h>
30 #include <sys/types.h>
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 */
51 .demangle = 0, /* Demangle low-level symbol names */
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 */
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 */
68 /* List of pids given to option -p: */
69 struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */
71 /* Vector of struct opt_F_t. */
76 fprintf(stderr, "Try `%s --help' for more information.\n", progname);
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"
89 " -C, --demangle decode low-level symbol names into user-level names.\n"
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@lists.alioth.debian.org\n",
120 fprintf(stdout, "%s debugging option, --debug=<octal> or -D<octal>:\n", progname);
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"
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"
137 search_for_command(char *filename) {
138 static char pathname[PATH_MAX];
142 if (strchr(filename, '/')) {
145 for (path = getenv("PATH"); path && *path; path += m) {
146 if (strchr(path, ':')) {
147 n = strchr(path, ':') - path;
150 m = n = strlen(path);
152 if (n + strlen(filename) + 1 >= PATH_MAX) {
153 fprintf(stderr, "Error: filename too long.\n");
156 strncpy(pathname, path, n);
157 if (n && pathname[n - 1] != '/') {
160 strcpy(pathname + n, filename);
161 if (!access(pathname, X_OK)) {
173 options.align = DEFAULT_ALIGN;
174 c = getenv("COLUMNS");
178 cols = strtol(c, &endptr, 0);
179 if (cols > 0 && !*endptr) {
180 options.align = cols * 5 / 8;
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;
190 compile_libname(const char *expr, const char *a_lib, int lib_re_p,
191 struct filter_lib_matcher *matcher)
193 if (strcmp(a_lib, "MAIN") == 0) {
194 filter_lib_matcher_main_init(matcher);
196 /* Add ^ and $ to the library expression as well. */
197 char lib[strlen(a_lib) + 3];
198 sprintf(lib, "^%s$", a_lib);
200 enum filter_lib_matcher_type type
201 = lib[0] == '/' ? FLM_PATHNAME : FLM_SONAME;
204 int status = (lib_re_p ? regcomp : globcomp)(&lib_re, lib, 0);
207 regerror(status, &lib_re, buf, sizeof buf);
208 fprintf(stderr, "Couldn't compile '%s': %s.\n",
212 filter_lib_matcher_name_init(matcher, type, lib_re);
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)
223 struct filter_rule *rule = malloc(sizeof(*rule));
224 struct filter_lib_matcher *matcher = malloc(sizeof(*matcher));
226 if (rule == NULL || matcher == NULL) {
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);
244 regerror(status, &symbol_re, buf, sizeof buf);
245 fprintf(stderr, "Couldn't compile '%s': %s.\n",
251 if (compile_libname(expr, a_lib, lib_re_p, matcher) < 0) {
256 filter_rule_init(rule, type, matcher, symbol_re);
257 filter_add_rule(filt, rule);
262 grok_libname_pattern(char **libnamep, char **libendp)
264 char *libname = *libnamep;
265 char *libend = *libendp;
267 if (libend[0] != '/')
271 if (libname != libend && libname[0] == '/')
274 fprintf(stderr, "Unmatched '/' in library name.\n");
282 parse_filter(struct filter *filt, char *expr, int operators)
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 *@*. */
288 enum filter_rule_type type = FR_ADD;
291 size_t s = strcspn(expr, &"-+@"[operators ? 0 : 2]);
292 char *symname = expr;
294 char *next = expr + s + 1;
295 enum filter_rule_type this_type = type;
301 } else if (expr[s] == '-' || expr[s] == '+') {
302 type = expr[s] == '-' ? FR_SUBTRACT : FR_ADD;
308 assert(expr[s] == '@');
310 s = strcspn(next, &"-+"[operators ? 0 : 2]);
314 } else if (next[s] == 0) {
318 assert(next[s] == '-' || next[s] == '+');
319 type = next[s] == '-' ? FR_SUBTRACT : FR_ADD;
326 assert(*libname != 0);
327 char *symend = symname + strlen(symname) - 1;
328 char *libend = libname + strlen(libname) - 1;
333 * /xxx/@... and ...@/xxx/ means that xxx are regular
334 * expressions. They are globs otherwise.
336 * /xxx@yyy/ is the same as /xxx/@/yyy/
338 * @/xxx matches library path name
339 * @.xxx matches library relative path name
341 if (symname[0] == '/') {
342 if (symname != symend && symend[0] == '/') {
352 /* /XXX@YYY/ is the same as
354 if (libend[0] != '/')
355 fprintf(stderr, "Unmatched '/'"
356 " in symbol name.\n");
362 /* If libname ends in '/', then we expect '/' in the
363 * beginning too. Otherwise the initial '/' is part
364 * of absolute file name. */
366 lib_is_re = grok_libname_pattern(&libname, &libend);
368 if (*symname == 0) /* /@AA/ */
370 if (*libname == 0) /* /aa@/ */
373 add_filter_rule(filt, expr, this_type,
381 static struct filter *
382 recursive_parse_chain(const char *orig, char *expr, int operators)
384 struct filter *filt = malloc(sizeof(*filt));
386 fprintf(stderr, "(Part of) filter will be ignored: '%s': %s.\n",
387 expr, strerror(errno));
392 if (parse_filter(filt, expr, operators) < 0) {
393 fprintf(stderr, "Filter '%s' will be ignored.\n", orig);
401 static struct filter **
402 slist_chase_end(struct filter **begin)
404 for (; *begin != NULL; begin = &(*begin)->next)
410 parse_filter_chain(const char *expr, struct filter **retp)
412 char *str = strdup(expr);
414 fprintf(stderr, "Filter '%s' will be ignored: %s.\n",
415 expr, strerror(errno));
418 /* Support initial '!' for backward compatibility. */
422 *slist_chase_end(retp) = recursive_parse_chain(expr, str, 1);
427 parse_int(const char *optarg, char opt, int min, int max)
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);
443 parse_colon_separated_list(const char *paths, struct vect *vec)
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
451 char *clone = strdup(paths);
453 fprintf(stderr, "Couldn't parse argument %s: %s.\n",
454 paths, strerror(errno));
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);
464 size_t len = strcspn(tok, ":");
467 struct opt_F_t arg = {
469 .own_pathname = tok == clone,
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));
483 opt_F_destroy(struct opt_F_t *entry)
487 if (entry->own_pathname)
488 free(entry->pathname);
492 opt_F_get_kind(struct opt_F_t *entry)
494 if (entry->kind == OPT_F_UNKNOWN) {
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;
505 fprintf(stderr, "%s is neither a regular file, "
506 "nor a directory.\n", entry->pathname);
507 entry->kind = OPT_F_BROKEN;
510 assert(entry->kind != OPT_F_UNKNOWN);
515 process_options(int argc, char **argv)
517 VECT_INIT(&opt_F, struct opt_F_t);
520 options.output = stderr;
521 options.no_signals = 0;
522 #if defined(HAVE_UNWINDER)
523 options.bt_depth = -1;
524 #endif /* defined(HAVE_UNWINDER) */
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'},
540 {"demangle", 0, 0, 'C'},
542 {"indent", 1, 0, 'n'},
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) */
555 const char *opts = "+"
559 #if defined(HAVE_UNWINDER)
562 "cfhiLrStTVba:A:D:e:F:l:n:o:p:s:u:x:";
564 #ifdef HAVE_GETOPT_LONG
565 c = getopt_long(argc, argv, opts, long_options, &option_index);
567 c = getopt(argc, argv, opts);
574 options.align = parse_int(optarg, 'a', 0, 0);
577 options.arraylen = parse_int(optarg, 'A', 0, 0);
580 options.no_signals = 1;
591 if (optarg[0]=='h') {
595 options.debug = strtoul(optarg,&p,8);
597 fprintf(stderr, "%s: --debug requires an octal argument\n", progname);
603 parse_filter_chain(optarg, &options.plt_filter);
610 parse_colon_separated_list(optarg, &opt_F);
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);
632 options.indent = parse_int(optarg, 'n', 0, 20);
635 options.output = fopen(optarg, "w");
636 if (!options.output) {
638 "can't open %s for writing: %s\n",
639 optarg, strerror(errno));
642 setvbuf(options.output, (char *)NULL, _IOLBF, 0);
643 fcntl(fileno(options.output), F_SETFD, FD_CLOEXEC);
647 struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t));
649 perror("ltrace: malloc");
652 tmp->pid = parse_int(optarg, 'p', 1, 0);
661 options.strlen = parse_int(optarg, 's', 0, 0);
664 options.syscalls = 1;
673 options.user = optarg;
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@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");
684 #if defined(HAVE_UNWINDER)
686 options.bt_depth = parse_int(optarg, 'w', 1, 0);
688 #endif /* defined(HAVE_UNWINDER) */
691 parse_filter_chain(optarg, &options.static_filter);
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. */
705 && options.plt_filter == NULL
706 && options.export_filter == NULL) {
707 parse_filter_chain("@MAIN", &options.plt_filter);
708 options.hide_caller = 1;
710 if (!libcalls && options.plt_filter != NULL) {
712 "%s: Option -L can't be used with -e or -l.\n",
717 if (!opt_p && argc < 1) {
718 fprintf(stderr, "%s: too few arguments\n", progname);
721 if (opt_r && opt_t) {
723 "%s: Options -r and -t can't be used together\n",
728 command = search_for_command(argv[0]);