blob: 8a2fe5da91a30f93995dc00be59b23a2504a3aca [file] [log] [blame]
Petr Machatae99af272012-10-26 00:29:52 +02001/*
2 * This file is part of ltrace.
Petr Machata4b3bda42013-01-11 23:32:17 +01003 * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc.
Petr Machatae99af272012-10-26 00:29:52 +02004 * 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
Juan Cespedesac3db291998-04-25 14:31:58 +020026#include "config.h"
Juan Cespedesac3db291998-04-25 14:31:58 +020027
Juan Cespedesce377d52008-12-16 19:38:10 +010028#include <sys/ioctl.h>
Petr Machata418584d2012-12-04 17:53:56 +010029#include <sys/stat.h>
30#include <sys/types.h>
Petr Machata2b46cfc2012-02-18 11:17:29 +010031#include <assert.h>
Petr Machata1e4fed22012-04-01 00:45:22 +020032#include <errno.h>
Petr Machata1e4fed22012-04-01 00:45:22 +020033#include <fcntl.h>
34#include <getopt.h>
35#include <limits.h>
Petr Machatacc0e1e42012-04-25 13:42:07 +020036#include <stdio.h>
Petr Machata1e4fed22012-04-01 00:45:22 +020037#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
Juan Cespedesac3db291998-04-25 14:31:58 +020040
Juan Cespedesf7281232009-06-25 16:11:21 +020041#include "common.h"
Petr Machata1e4fed22012-04-01 00:45:22 +020042#include "filter.h"
43#include "glob.h"
Petr Machata3ac8db62012-11-23 18:43:10 +010044#include "demangle.h"
Juan Cespedes5e01f651998-03-08 22:31:44 +010045
Juan Cespedesce377d52008-12-16 19:38:10 +010046struct options_t options = {
Juan Cespedesda9b9532009-04-07 15:33:50 +020047 .align = DEFAULT_ALIGN, /* alignment column for results */
48 .user = NULL, /* username to run command as */
49 .syscalls = 0, /* display syscalls */
Juan Cespedesce377d52008-12-16 19:38:10 +010050#ifdef USE_DEMANGLE
Juan Cespedesda9b9532009-04-07 15:33:50 +020051 .demangle = 0, /* Demangle low-level symbol names */
Juan Cespedesce377d52008-12-16 19:38:10 +010052#endif
Juan Cespedesda9b9532009-04-07 15:33:50 +020053 .indent = 0, /* indent output according to program flow */
54 .output = NULL, /* output to a specific file */
Juan Cespedescc813cd2009-04-07 15:45:53 +020055 .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 */
Juan Cespedesce377d52008-12-16 19:38:10 +010060};
61
Juan Cespedesac3db291998-04-25 14:31:58 +020062static char *progname; /* Program name (`ltrace') */
Juan Cespedes5e01f651998-03-08 22:31:44 +010063int opt_i = 0; /* instruction pointer */
Juan Cespedesf666d191998-09-20 23:04:34 +020064int opt_r = 0; /* print relative timestamp */
Juan Cespedes5e0acdb1998-04-04 08:34:07 +020065int opt_t = 0; /* print absolute timestamp */
Juan Cespedesd65efa32003-02-03 00:22:30 +010066int opt_T = 0; /* show the time spent inside each call */
Juan Cespedes5e01f651998-03-08 22:31:44 +010067
68/* List of pids given to option -p: */
Ian Wienand2d45b1a2006-02-20 22:48:07 +010069struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */
Juan Cespedes5e01f651998-03-08 22:31:44 +010070
Petr Machata418584d2012-12-04 17:53:56 +010071/* Vector of struct opt_F_t. */
72struct vect opt_F;
Steve Fink58c73a72006-07-17 23:18:35 +020073
Juan Cespedesf1350522008-12-16 18:19:58 +010074static void
Juan Cespedesc5c744a2009-07-23 18:22:58 +020075err_usage(void) {
Petr Machata20a411d2012-09-25 22:45:26 +020076 fprintf(stderr, "Try `%s --help' for more information.\n", progname);
Juan Cespedesc5c744a2009-07-23 18:22:58 +020077 exit(1);
78}
79
80static void
Juan Cespedesf1350522008-12-16 18:19:58 +010081usage(void) {
Juan Cespedesac3db291998-04-25 14:31:58 +020082 fprintf(stdout, "Usage: %s [option ...] [command [arg ...]]\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010083 "Trace library calls of a given program.\n\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010084 " -a, --align=COLUMN align return values in a secific column.\n"
Petr Machata4b3bda42013-01-11 23:32:17 +010085 " -A MAXELTS maximum number of array elements to print.\n"
Joe Damato59e3fb12009-11-06 19:45:10 -080086 " -b, --no-signals don't print signals.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010087 " -c count time and calls, and report a summary on exit.\n"
Juan Cespedesd914a202004-11-10 00:15:33 +010088# ifdef USE_DEMANGLE
Ian Wienand2d45b1a2006-02-20 22:48:07 +010089 " -C, --demangle decode low-level symbol names into user-level names.\n"
Juan Cespedesac3db291998-04-25 14:31:58 +020090# endif
Petr Machata4b3bda42013-01-11 23:32:17 +010091 " -D, --debug=MASK enable debugging (see -Dh or --debug=help).\n"
Juan Cespedesc5c744a2009-07-23 18:22:58 +020092 " -Dh, --debug=help show help on debugging.\n"
Petr Machata4b3bda42013-01-11 23:32:17 +010093 " -e FILTER modify which library calls to trace.\n"
Juan Cespedesc4e53a92009-05-06 20:36:42 +020094 " -f trace children (fork() and clone()).\n"
Juan Cespedescc813cd2009-04-07 15:45:53 +020095 " -F, --config=FILE load alternate configuration file (may be repeated).\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010096 " -h, --help display this help and exit.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010097 " -i print instruction pointer at time of library call.\n"
Petr Machata4b3bda42013-01-11 23:32:17 +010098 " -l, --library=LIBRARY_PATTERN only trace symbols implemented by this library.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010099 " -L do NOT display library calls.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100100 " -n, --indent=NR indent output by NR spaces for each call level nesting.\n"
Petr Machata4b3bda42013-01-11 23:32:17 +0100101 " -o, --output=FILENAME write the trace output to file with given name.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100102 " -p PID attach to the process with the process ID pid.\n"
103 " -r print relative timestamps.\n"
Petr Machata4b3bda42013-01-11 23:32:17 +0100104 " -s STRSIZE specify the maximum string size to print.\n"
105 " -S trace system calls as well as library calls.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100106 " -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"
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100109 " -V, --version output version information and exit.\n"
Joe Damatoab3b72c2010-10-31 00:21:53 -0700110#if defined(HAVE_LIBUNWIND)
Petr Machata68818a82012-11-30 21:42:59 +0100111 " -w, --where=NR print backtrace showing NR stack frames at most.\n"
Joe Damatoab3b72c2010-10-31 00:21:53 -0700112#endif /* defined(HAVE_LIBUNWIND) */
Petr Machata4b3bda42013-01-11 23:32:17 +0100113 " -x FILTER modify which static functions to trace.\n"
Paul Gilliambe320772006-04-24 22:06:23 +0200114 "\nReport bugs to ltrace-devel@lists.alioth.debian.org\n",
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100115 progname);
Juan Cespedes5e01f651998-03-08 22:31:44 +0100116}
117
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200118static 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
Juan Cespedesf1350522008-12-16 18:19:58 +0100134static char *
135search_for_command(char *filename) {
Juan Cespedesf1bfe202002-03-27 00:22:23 +0100136 static char pathname[PATH_MAX];
Juan Cespedes5e01f651998-03-08 22:31:44 +0100137 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 }
Juan Cespedesf1bfe202002-03-27 00:22:23 +0100150 if (n + strlen(filename) + 1 >= PATH_MAX) {
Petr Machata20a411d2012-09-25 22:45:26 +0200151 fprintf(stderr, "Error: filename too long.\n");
Juan Cespedesf1bfe202002-03-27 00:22:23 +0100152 exit(1);
153 }
154 strncpy(pathname, path, n);
Juan Cespedes5e01f651998-03-08 22:31:44 +0100155 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
Juan Cespedesce377d52008-12-16 19:38:10 +0100166static 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;
Juan Cespedes43739a62009-04-07 13:10:08 +0200182 } else if (ioctl(2, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) {
183 options.align = ws.ws_col * 5 / 8;
Juan Cespedesce377d52008-12-16 19:38:10 +0100184 }
185}
186
Petr Machata02b96072012-09-25 23:10:14 +0200187static 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);
Petr Machatab6c5c8c2012-12-08 03:23:39 +0100203 if (status != 0) {
Petr Machata02b96072012-09-25 23:10:14 +0200204 char buf[100];
205 regerror(status, &lib_re, buf, sizeof buf);
Petr Machataec446692013-01-06 17:14:09 +0100206 fprintf(stderr, "Couldn't compile '%s': %s.\n",
Petr Machata02b96072012-09-25 23:10:14 +0200207 expr, buf);
208 return -1;
209 }
210 filter_lib_matcher_name_init(matcher, type, lib_re);
211 }
212 return 0;
213}
214
Petr Machataec446692013-01-06 17:14:09 +0100215static int
Petr Machata1e4fed22012-04-01 00:45:22 +0200216add_filter_rule(struct filter *filt, const char *expr,
217 enum filter_rule_type type,
Petr Machata5bc4e7f2012-04-06 22:28:24 +0200218 const char *a_sym, int sym_re_p,
219 const char *a_lib, int lib_re_p)
Petr Machata1e4fed22012-04-01 00:45:22 +0200220{
Petr Machata1e4fed22012-04-01 00:45:22 +0200221 struct filter_rule *rule = malloc(sizeof(*rule));
222 struct filter_lib_matcher *matcher = malloc(sizeof(*matcher));
223
224 if (rule == NULL || matcher == NULL) {
Petr Machata1e4fed22012-04-01 00:45:22 +0200225 fail:
226 free(rule);
227 free(matcher);
Petr Machataec446692013-01-06 17:14:09 +0100228 return -1;
Petr Machata1e4fed22012-04-01 00:45:22 +0200229 }
230
231 regex_t symbol_re;
Petr Machata5bc4e7f2012-04-06 22:28:24 +0200232 {
233 /* Add ^ to the start of expression and $ to the end, so that
234 * we match the whole symbol name. Let the user write the "*"
235 * explicitly if they wish. */
236 char sym[strlen(a_sym) + 3];
237 sprintf(sym, "^%s$", a_sym);
Petr Machata02b96072012-09-25 23:10:14 +0200238 int status = (sym_re_p ? regcomp : globcomp)
239 (&symbol_re, sym, 0);
Petr Machata5bc4e7f2012-04-06 22:28:24 +0200240 if (status != 0) {
241 char buf[100];
242 regerror(status, &symbol_re, buf, sizeof buf);
Petr Machataec446692013-01-06 17:14:09 +0100243 fprintf(stderr, "Couldn't compile '%s': %s.\n",
Petr Machatacc0e1e42012-04-25 13:42:07 +0200244 expr, buf);
Petr Machata5bc4e7f2012-04-06 22:28:24 +0200245 goto fail;
246 }
Petr Machata1e4fed22012-04-01 00:45:22 +0200247 }
248
Petr Machata02b96072012-09-25 23:10:14 +0200249 if (compile_libname(expr, a_lib, lib_re_p, matcher) < 0) {
250 regfree(&symbol_re);
251 goto fail;
Petr Machata1e4fed22012-04-01 00:45:22 +0200252 }
253
Petr Machata1e4fed22012-04-01 00:45:22 +0200254 filter_rule_init(rule, type, matcher, symbol_re);
Petr Machatae0973cb2012-04-01 19:36:41 +0200255 filter_add_rule(filt, rule);
Petr Machataec446692013-01-06 17:14:09 +0100256 return 0;
Petr Machata1e4fed22012-04-01 00:45:22 +0200257}
258
259static int
Petr Machata02b96072012-09-25 23:10:14 +0200260grok_libname_pattern(char **libnamep, char **libendp)
261{
262 char *libname = *libnamep;
263 char *libend = *libendp;
264
265 if (libend[0] != '/')
266 return 0;
267
268 *libend-- = 0;
269 if (libname != libend && libname[0] == '/')
270 ++libname;
271 else
272 fprintf(stderr, "Unmatched '/' in library name.\n");
273
274 *libendp = libend;
275 *libnamep = libname;
276 return 1;
277}
278
279static int
Petr Machata51e74ac2012-09-27 23:43:25 +0200280parse_filter(struct filter *filt, char *expr, int operators)
Petr Machata1e4fed22012-04-01 00:45:22 +0200281{
Petr Machata02b96072012-09-25 23:10:14 +0200282 /* Filter is a chain of sym@lib rules separated by '-' or '+'.
283 * If the filter expression starts with '-', the missing
284 * initial rule is implicitly *@*. */
Petr Machata1e4fed22012-04-01 00:45:22 +0200285
286 enum filter_rule_type type = FR_ADD;
287
288 while (*expr != 0) {
Andrey Zonov6bb42012013-02-14 12:32:06 +0100289 size_t s = strcspn(expr, &"-+@"[operators ? 0 : 2]);
Petr Machata1e4fed22012-04-01 00:45:22 +0200290 char *symname = expr;
291 char *libname;
292 char *next = expr + s + 1;
293 enum filter_rule_type this_type = type;
294
295 if (expr[s] == 0) {
296 libname = "*";
297 expr = next - 1;
298
Petr Machata050fa7f2012-04-23 23:44:36 +0200299 } else if (expr[s] == '-' || expr[s] == '+') {
300 type = expr[s] == '-' ? FR_SUBTRACT : FR_ADD;
301 expr[s] = 0;
Petr Machata1e4fed22012-04-01 00:45:22 +0200302 libname = "*";
303 expr = next;
Petr Machata1e4fed22012-04-01 00:45:22 +0200304
305 } else {
306 assert(expr[s] == '@');
307 expr[s] = 0;
Andrey Zonov6bb42012013-02-14 12:32:06 +0100308 s = strcspn(next, &"-+"[operators ? 0 : 2]);
Petr Machata1e4fed22012-04-01 00:45:22 +0200309 if (s == 0) {
310 libname = "*";
311 expr = next;
312 } else if (next[s] == 0) {
313 expr = next + s;
314 libname = next;
Petr Machata1e4fed22012-04-01 00:45:22 +0200315 } else {
Petr Machata050fa7f2012-04-23 23:44:36 +0200316 assert(next[s] == '-' || next[s] == '+');
317 type = next[s] == '-' ? FR_SUBTRACT : FR_ADD;
Petr Machata1e4fed22012-04-01 00:45:22 +0200318 next[s] = 0;
319 expr = next + s + 1;
320 libname = next;
321 }
322 }
323
324 assert(*libname != 0);
325 char *symend = symname + strlen(symname) - 1;
326 char *libend = libname + strlen(libname) - 1;
327 int sym_is_re = 0;
328 int lib_is_re = 0;
329
330 /*
331 * /xxx/@... and ...@/xxx/ means that xxx are regular
332 * expressions. They are globs otherwise.
333 *
334 * /xxx@yyy/ is the same as /xxx/@/yyy/
335 *
336 * @/xxx matches library path name
337 * @.xxx matches library relative path name
338 */
339 if (symname[0] == '/') {
340 if (symname != symend && symend[0] == '/') {
341 ++symname;
342 *symend-- = 0;
343 sym_is_re = 1;
344
345 } else {
346 sym_is_re = 1;
347 lib_is_re = 1;
348 ++symname;
349
350 /* /XXX@YYY/ is the same as
351 * /XXX/@/YYY/. */
352 if (libend[0] != '/')
Petr Machata20a411d2012-09-25 22:45:26 +0200353 fprintf(stderr, "Unmatched '/'"
354 " in symbol name.\n");
Petr Machata1e4fed22012-04-01 00:45:22 +0200355 else
356 *libend-- = 0;
357 }
358 }
359
360 /* If libname ends in '/', then we expect '/' in the
361 * beginning too. Otherwise the initial '/' is part
362 * of absolute file name. */
Petr Machata02b96072012-09-25 23:10:14 +0200363 if (!lib_is_re)
364 lib_is_re = grok_libname_pattern(&libname, &libend);
Petr Machata1e4fed22012-04-01 00:45:22 +0200365
366 if (*symname == 0) /* /@AA/ */
367 symname = "*";
368 if (*libname == 0) /* /aa@/ */
369 libname = "*";
370
Petr Machataa43b2a02013-03-11 21:21:22 -0400371 add_filter_rule(filt, expr, this_type,
372 symname, sym_is_re,
373 libname, lib_is_re);
Petr Machata1e4fed22012-04-01 00:45:22 +0200374 }
375
376 return 0;
377}
378
379static struct filter *
Petr Machataec446692013-01-06 17:14:09 +0100380recursive_parse_chain(const char *orig, char *expr, int operators)
Petr Machata1e4fed22012-04-01 00:45:22 +0200381{
Petr Machata1e4fed22012-04-01 00:45:22 +0200382 struct filter *filt = malloc(sizeof(*filt));
383 if (filt == NULL) {
Petr Machata20a411d2012-09-25 22:45:26 +0200384 fprintf(stderr, "(Part of) filter will be ignored: '%s': %s.\n",
Petr Machatacc0e1e42012-04-25 13:42:07 +0200385 expr, strerror(errno));
Petr Machata1e4fed22012-04-01 00:45:22 +0200386 return NULL;
387 }
388
Petr Machata35c88142012-04-04 00:54:43 +0200389 filter_init(filt);
Petr Machata51e74ac2012-09-27 23:43:25 +0200390 if (parse_filter(filt, expr, operators) < 0) {
Petr Machataec446692013-01-06 17:14:09 +0100391 fprintf(stderr, "Filter '%s' will be ignored.\n", orig);
Petr Machata050fa7f2012-04-23 23:44:36 +0200392 free(filt);
393 filt = NULL;
Petr Machata1e4fed22012-04-01 00:45:22 +0200394 }
Petr Machata050fa7f2012-04-23 23:44:36 +0200395
396 return filt;
Petr Machata1e4fed22012-04-01 00:45:22 +0200397}
398
Petr Machata51e74ac2012-09-27 23:43:25 +0200399static struct filter **
400slist_chase_end(struct filter **begin)
401{
402 for (; *begin != NULL; begin = &(*begin)->next)
403 ;
404 return begin;
405}
406
Petr Machata1e4fed22012-04-01 00:45:22 +0200407static void
Petr Machatab5f80ac2012-04-04 01:46:18 +0200408parse_filter_chain(const char *expr, struct filter **retp)
Petr Machata1e4fed22012-04-01 00:45:22 +0200409{
410 char *str = strdup(expr);
411 if (str == NULL) {
Petr Machata20a411d2012-09-25 22:45:26 +0200412 fprintf(stderr, "Filter '%s' will be ignored: %s.\n",
Petr Machatacc0e1e42012-04-25 13:42:07 +0200413 expr, strerror(errno));
Petr Machata1e4fed22012-04-01 00:45:22 +0200414 return;
415 }
Petr Machata25f319e2012-04-23 23:45:21 +0200416 /* Support initial '!' for backward compatibility. */
417 if (str[0] == '!')
418 str[0] = '-';
419
Petr Machataec446692013-01-06 17:14:09 +0100420 *slist_chase_end(retp) = recursive_parse_chain(expr, str, 1);
Petr Machatadd1ec842012-10-27 00:34:10 +0200421 free(str);
Petr Machata1e4fed22012-04-01 00:45:22 +0200422}
423
Petr Machata68818a82012-11-30 21:42:59 +0100424static int
425parse_int(const char *optarg, char opt, int min, int max)
426{
427 char *endptr;
428 long int l = strtol(optarg, &endptr, 0);
429 if (l < min || (max != 0 && l > max)
430 || *optarg == 0 || *endptr != 0) {
431 const char *fmt = max != 0
432 ? "Invalid argument to -%c: '%s'. Use integer %d..%d.\n"
433 : "Invalid argument to -%c: '%s'. Use integer >=%d.\n";
434 fprintf(stderr, fmt, opt, optarg, min, max);
435 exit(1);
436 }
437 return (int)l;
438}
439
Petr Machata418584d2012-12-04 17:53:56 +0100440int
441parse_colon_separated_list(const char *paths, struct vect *vec)
442{
443 /* PATHS contains a colon-separated list of directories and
444 * files to load. It's modeled after shell PATH variable,
445 * which doesn't allow escapes. PYTHONPATH in CPython behaves
446 * the same way. So let's follow suit, it makes things easier
447 * to us. */
448
449 char *clone = strdup(paths);
450 if (clone == NULL) {
451 fprintf(stderr, "Couldn't parse argument %s: %s.\n",
452 paths, strerror(errno));
453 return -1;
454 }
455
456 /* It's undesirable to use strtok, because we want the string
457 * "a::b" to have three elements. */
458 char *tok = clone - 1;
459 char *end = clone + strlen(clone);
460 while (tok < end) {
461 ++tok;
462 size_t len = strcspn(tok, ":");
463 tok[len] = 0;
464
465 struct opt_F_t arg = {
466 .pathname = tok,
467 .own_pathname = tok == clone,
468 };
469 if (VECT_PUSHBACK(vec, &arg) < 0)
470 /* Presumably this is not a deal-breaker. */
471 fprintf(stderr, "Couldn't store component of %s: %s.\n",
472 paths, strerror(errno));
473
474 tok += len;
475 }
476
477 return 0;
478}
479
480void
481opt_F_destroy(struct opt_F_t *entry)
482{
483 if (entry == NULL)
484 return;
485 if (entry->own_pathname)
486 free(entry->pathname);
487}
488
489enum opt_F_kind
490opt_F_get_kind(struct opt_F_t *entry)
491{
492 if (entry->kind == OPT_F_UNKNOWN) {
493 struct stat st;
494 if (lstat(entry->pathname, &st) < 0) {
495 fprintf(stderr, "Couldn't stat %s: %s\n",
496 entry->pathname, strerror(errno));
497 entry->kind = OPT_F_BROKEN;
498 } else if (S_ISDIR(st.st_mode)) {
499 entry->kind = OPT_F_DIR;
Petr Machatae7be3902012-12-05 01:21:03 +0100500 } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
Petr Machata418584d2012-12-04 17:53:56 +0100501 entry->kind = OPT_F_FILE;
502 } else {
503 fprintf(stderr, "%s is neither a regular file, "
504 "nor a directory.\n", entry->pathname);
505 entry->kind = OPT_F_BROKEN;
506 }
507 }
508 assert(entry->kind != OPT_F_UNKNOWN);
509 return entry->kind;
510}
511
Juan Cespedesf1350522008-12-16 18:19:58 +0100512char **
Petr Machata67fa52f2012-04-05 02:11:39 +0200513process_options(int argc, char **argv)
514{
Petr Machata418584d2012-12-04 17:53:56 +0100515 VECT_INIT(&opt_F, struct opt_F_t);
516
Juan Cespedesac3db291998-04-25 14:31:58 +0200517 progname = argv[0];
Juan Cespedesb65bdc52008-12-16 19:50:16 +0100518 options.output = stderr;
Joe Damato59e3fb12009-11-06 19:45:10 -0800519 options.no_signals = 0;
Joe Damatoab3b72c2010-10-31 00:21:53 -0700520#if defined(HAVE_LIBUNWIND)
521 options.bt_depth = -1;
522#endif /* defined(HAVE_LIBUNWIND) */
Juan Cespedes5e01f651998-03-08 22:31:44 +0100523
Juan Cespedesce377d52008-12-16 19:38:10 +0100524 guess_cols();
525
Petr Machata67fa52f2012-04-05 02:11:39 +0200526 int libcalls = 1;
527
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100528 while (1) {
Juan Cespedesac3db291998-04-25 14:31:58 +0200529 int c;
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200530 char *p;
Petr Machata35d07302012-12-09 11:38:56 +0100531#ifdef HAVE_GETOPT_LONG
Juan Cespedesac3db291998-04-25 14:31:58 +0200532 int option_index = 0;
533 static struct option long_options[] = {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100534 {"align", 1, 0, 'a'},
Juan Cespedesaee09312007-08-31 18:49:48 +0200535 {"config", 1, 0, 'F'},
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200536 {"debug", 1, 0, 'D'},
Juan Cespedesd914a202004-11-10 00:15:33 +0100537# ifdef USE_DEMANGLE
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100538 {"demangle", 0, 0, 'C'},
Petr Machata35d07302012-12-09 11:38:56 +0100539# endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100540 {"indent", 1, 0, 'n'},
541 {"help", 0, 0, 'h'},
542 {"library", 1, 0, 'l'},
543 {"output", 1, 0, 'o'},
544 {"version", 0, 0, 'V'},
Joe Damato59e3fb12009-11-06 19:45:10 -0800545 {"no-signals", 0, 0, 'b'},
Petr Machata35d07302012-12-09 11:38:56 +0100546# if defined(HAVE_LIBUNWIND)
Joe Damatoab3b72c2010-10-31 00:21:53 -0700547 {"where", 1, 0, 'w'},
Petr Machata35d07302012-12-09 11:38:56 +0100548# endif /* defined(HAVE_LIBUNWIND) */
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100549 {0, 0, 0, 0}
Juan Cespedesac3db291998-04-25 14:31:58 +0200550 };
Joe Damatoab3b72c2010-10-31 00:21:53 -0700551#endif
Petr Machata35d07302012-12-09 11:38:56 +0100552
553 const char *opts = "+"
554#ifdef USE_DEMANGLE
555 "C"
556#endif
557#if defined(HAVE_LIBUNWIND)
558 "w:"
559#endif
560 "cfhiLrStTVba:A:D:e:F:l:n:o:p:s:u:x:X:";
561
562#ifdef HAVE_GETOPT_LONG
563 c = getopt_long(argc, argv, opts, long_options, &option_index);
564#else
565 c = getopt(argc, argv, opts);
566#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100567 if (c == -1) {
Juan Cespedesac3db291998-04-25 14:31:58 +0200568 break;
Juan Cespedes5e01f651998-03-08 22:31:44 +0100569 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100570 switch (c) {
571 case 'a':
Petr Machata68818a82012-11-30 21:42:59 +0100572 options.align = parse_int(optarg, 'a', 0, 0);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100573 break;
Juan Cespedesaee09312007-08-31 18:49:48 +0200574 case 'A':
Petr Machata68818a82012-11-30 21:42:59 +0100575 options.arraylen = parse_int(optarg, 'A', 0, 0);
Steve Fink1150bc42006-08-07 06:04:43 +0200576 break;
Joe Damato535e7382010-11-08 15:47:43 -0800577 case 'b':
578 options.no_signals = 1;
579 break;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100580 case 'c':
Juan Cespedesda9b9532009-04-07 15:33:50 +0200581 options.summary++;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100582 break;
Juan Cespedesd914a202004-11-10 00:15:33 +0100583#ifdef USE_DEMANGLE
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100584 case 'C':
Juan Cespedesce377d52008-12-16 19:38:10 +0100585 options.demangle++;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100586 break;
Juan Cespedesac3db291998-04-25 14:31:58 +0200587#endif
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200588 case 'D':
589 if (optarg[0]=='h') {
590 usage_debug();
591 exit(0);
592 }
593 options.debug = strtoul(optarg,&p,8);
594 if (*p) {
595 fprintf(stderr, "%s: --debug requires an octal argument\n", progname);
596 err_usage();
597 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100598 break;
Petr Machata1e4fed22012-04-01 00:45:22 +0200599
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100600 case 'e':
Petr Machatab5f80ac2012-04-04 01:46:18 +0200601 parse_filter_chain(optarg, &options.plt_filter);
Petr Machata1e4fed22012-04-01 00:45:22 +0200602 break;
603
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100604 case 'f':
Juan Cespedescc813cd2009-04-07 15:45:53 +0200605 options.follow = 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100606 break;
Juan Cespedesaee09312007-08-31 18:49:48 +0200607 case 'F':
Petr Machata418584d2012-12-04 17:53:56 +0100608 parse_colon_separated_list(optarg, &opt_F);
609 break;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100610 case 'h':
611 usage();
612 exit(0);
613 case 'i':
614 opt_i++;
615 break;
Petr Machata51e74ac2012-09-27 23:43:25 +0200616
617 case 'l': {
618 size_t patlen = strlen(optarg);
619 char buf[patlen + 2];
620 sprintf(buf, "@%s", optarg);
621 *slist_chase_end(&options.export_filter)
Petr Machataec446692013-01-06 17:14:09 +0100622 = recursive_parse_chain(buf, buf, 0);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100623 break;
Petr Machata51e74ac2012-09-27 23:43:25 +0200624 }
625
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100626 case 'L':
Petr Machata67fa52f2012-04-05 02:11:39 +0200627 libcalls = 0;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100628 break;
Petr Machata68818a82012-11-30 21:42:59 +0100629 case 'n':
630 options.indent = parse_int(optarg, 'n', 0, 20);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100631 break;
632 case 'o':
Juan Cespedesb65bdc52008-12-16 19:50:16 +0100633 options.output = fopen(optarg, "w");
634 if (!options.output) {
Petr Machatacc0e1e42012-04-25 13:42:07 +0200635 fprintf(stderr,
636 "can't open %s for writing: %s\n",
637 optarg, strerror(errno));
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100638 exit(1);
639 }
Juan Cespedesb65bdc52008-12-16 19:50:16 +0100640 setvbuf(options.output, (char *)NULL, _IOLBF, 0);
641 fcntl(fileno(options.output), F_SETFD, FD_CLOEXEC);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100642 break;
643 case 'p':
644 {
Juan Cespedesaee09312007-08-31 18:49:48 +0200645 struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t));
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100646 if (!tmp) {
647 perror("ltrace: malloc");
648 exit(1);
649 }
Petr Machata68818a82012-11-30 21:42:59 +0100650 tmp->pid = parse_int(optarg, 'p', 1, 0);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100651 tmp->next = opt_p;
652 opt_p = tmp;
653 break;
654 }
655 case 'r':
656 opt_r++;
657 break;
658 case 's':
Petr Machata68818a82012-11-30 21:42:59 +0100659 options.strlen = parse_int(optarg, 's', 0, 0);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100660 break;
661 case 'S':
Juan Cespedesce377d52008-12-16 19:38:10 +0100662 options.syscalls = 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100663 break;
664 case 't':
665 opt_t++;
666 break;
667 case 'T':
668 opt_T++;
669 break;
670 case 'u':
Juan Cespedesce377d52008-12-16 19:38:10 +0100671 options.user = optarg;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100672 break;
673 case 'V':
Petr Machata5cde20b2012-12-15 23:54:54 +0100674 printf("ltrace " PACKAGE_VERSION "\n"
Petr Machata3992a0b2013-10-23 00:44:52 +0200675 "Copyright (C) 2010-2013 Petr Machata, Red Hat Inc.\n"
Petr Machata5cde20b2012-12-15 23:54:54 +0100676 "Copyright (C) 1997-2009 Juan Cespedes <cespedes@debian.org>.\n"
677 "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
678 "This is free software: you are free to change and redistribute it.\n"
679 "There is NO WARRANTY, to the extent permitted by law.\n");
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100680 exit(0);
Joe Damatofa2aefc2010-10-30 19:56:50 -0700681 break;
Joe Damato535e7382010-11-08 15:47:43 -0800682#if defined(HAVE_LIBUNWIND)
683 case 'w':
Petr Machata68818a82012-11-30 21:42:59 +0100684 options.bt_depth = parse_int(optarg, 'w', 1, 0);
Joe Damato59e3fb12009-11-06 19:45:10 -0800685 break;
Joe Damato535e7382010-11-08 15:47:43 -0800686#endif /* defined(HAVE_LIBUNWIND) */
Juan Cespedesac3db291998-04-25 14:31:58 +0200687
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100688 case 'x':
Petr Machatada3edbf2012-04-04 02:20:21 +0200689 parse_filter_chain(optarg, &options.static_filter);
690 break;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100691
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100692 default:
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200693 err_usage();
Juan Cespedes5e01f651998-03-08 22:31:44 +0100694 }
695 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100696 argc -= optind;
697 argv += optind;
Juan Cespedes5e01f651998-03-08 22:31:44 +0100698
Petr Machata51e74ac2012-09-27 23:43:25 +0200699 /* If neither -e, nor -l, nor -L are used, set default -e.
700 * Use @MAIN for now, as that's what ltrace used to have in
701 * the past. XXX Maybe we should make this "*" instead. */
702 if (libcalls
703 && options.plt_filter == NULL
704 && options.export_filter == NULL) {
Petr Machatab5f80ac2012-04-04 01:46:18 +0200705 parse_filter_chain("@MAIN", &options.plt_filter);
Petr Machata03673892012-04-03 13:51:09 +0200706 options.hide_caller = 1;
707 }
Petr Machata51e74ac2012-09-27 23:43:25 +0200708 if (!libcalls && options.plt_filter != NULL) {
709 fprintf(stderr,
710 "%s: Option -L can't be used with -e or -l.\n",
711 progname);
712 err_usage();
713 }
Petr Machatae0973cb2012-04-01 19:36:41 +0200714
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100715 if (!opt_p && argc < 1) {
Juan Cespedesac3db291998-04-25 14:31:58 +0200716 fprintf(stderr, "%s: too few arguments\n", progname);
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200717 err_usage();
Juan Cespedes5e01f651998-03-08 22:31:44 +0100718 }
Juan Cespedesf666d191998-09-20 23:04:34 +0200719 if (opt_r && opt_t) {
Petr Machata20a411d2012-09-25 22:45:26 +0200720 fprintf(stderr,
721 "%s: Options -r and -t can't be used together\n",
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100722 progname);
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200723 err_usage();
Juan Cespedesf666d191998-09-20 23:04:34 +0200724 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100725 if (argc > 0) {
Juan Cespedesac3db291998-04-25 14:31:58 +0200726 command = search_for_command(argv[0]);
Juan Cespedes1fe93d51998-03-13 00:29:21 +0100727 }
Juan Cespedesac3db291998-04-25 14:31:58 +0200728 return &argv[0];
Juan Cespedes5e01f651998-03-08 22:31:44 +0100729}