blob: 6ad77e483dd5d6ddd06af62514d57c6f8c39740a [file] [log] [blame]
Juan Cespedesac3db291998-04-25 14:31:58 +02001#include "config.h"
Juan Cespedesac3db291998-04-25 14:31:58 +02002
Juan Cespedesce377d52008-12-16 19:38:10 +01003#include <sys/ioctl.h>
Petr Machata2b46cfc2012-02-18 11:17:29 +01004#include <assert.h>
Petr Machata1e4fed22012-04-01 00:45:22 +02005#include <errno.h>
6#include <error.h>
7#include <fcntl.h>
8#include <getopt.h>
9#include <limits.h>
10#include <stdlib.h>
11#include <string.h>
12#include <unistd.h>
Juan Cespedesac3db291998-04-25 14:31:58 +020013
Juan Cespedesf7281232009-06-25 16:11:21 +020014#include "common.h"
Petr Machata1e4fed22012-04-01 00:45:22 +020015#include "filter.h"
16#include "glob.h"
Juan Cespedes5e01f651998-03-08 22:31:44 +010017
Steve Fink58c73a72006-07-17 23:18:35 +020018#ifndef SYSCONFDIR
19#define SYSCONFDIR "/etc"
20#endif
21
Olaf Heringe948e582006-10-12 23:53:44 +020022#define SYSTEM_CONFIG_FILE SYSCONFDIR "/ltrace.conf"
Steve Fink58c73a72006-07-17 23:18:35 +020023#define USER_CONFIG_FILE "~/.ltrace.conf"
24
Juan Cespedesce377d52008-12-16 19:38:10 +010025struct options_t options = {
Juan Cespedesda9b9532009-04-07 15:33:50 +020026 .align = DEFAULT_ALIGN, /* alignment column for results */
27 .user = NULL, /* username to run command as */
28 .syscalls = 0, /* display syscalls */
29 .libcalls = 1, /* display library calls */
Juan Cespedesce377d52008-12-16 19:38:10 +010030#ifdef USE_DEMANGLE
Juan Cespedesda9b9532009-04-07 15:33:50 +020031 .demangle = 0, /* Demangle low-level symbol names */
Juan Cespedesce377d52008-12-16 19:38:10 +010032#endif
Juan Cespedesda9b9532009-04-07 15:33:50 +020033 .indent = 0, /* indent output according to program flow */
34 .output = NULL, /* output to a specific file */
Juan Cespedescc813cd2009-04-07 15:45:53 +020035 .summary = 0, /* Report a summary on program exit */
36 .debug = 0, /* debug */
37 .arraylen = DEFAULT_ARRAYLEN, /* maximum # array elements to print */
38 .strlen = DEFAULT_STRLEN, /* maximum # of bytes printed in strings */
39 .follow = 0, /* trace child processes */
Juan Cespedesce377d52008-12-16 19:38:10 +010040};
41
Juan Cespedesac3db291998-04-25 14:31:58 +020042static char *progname; /* Program name (`ltrace') */
Juan Cespedes5e01f651998-03-08 22:31:44 +010043int opt_i = 0; /* instruction pointer */
Juan Cespedesf666d191998-09-20 23:04:34 +020044int opt_r = 0; /* print relative timestamp */
Juan Cespedes5e0acdb1998-04-04 08:34:07 +020045int opt_t = 0; /* print absolute timestamp */
Juan Cespedesd65efa32003-02-03 00:22:30 +010046int opt_T = 0; /* show the time spent inside each call */
Juan Cespedes5e01f651998-03-08 22:31:44 +010047
48/* List of pids given to option -p: */
Ian Wienand2d45b1a2006-02-20 22:48:07 +010049struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */
Juan Cespedes5e01f651998-03-08 22:31:44 +010050
Steve Fink58c73a72006-07-17 23:18:35 +020051/* List of filenames give to option -F: */
52struct opt_F_t *opt_F = NULL; /* alternate configuration file(s) */
53
Paul Gilliambe320772006-04-24 22:06:23 +020054#ifdef PLT_REINITALISATION_BP
Ian Wienand9a2ad352006-02-20 22:44:45 +010055/* Set a break on the routine named here in order to re-initialize breakpoints
56 after all the PLTs have been initialzed */
Paul Gilliambe320772006-04-24 22:06:23 +020057char *PLTs_initialized_by_here = PLT_REINITALISATION_BP;
58#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +010059
Juan Cespedesf1350522008-12-16 18:19:58 +010060static void
Juan Cespedesc5c744a2009-07-23 18:22:58 +020061err_usage(void) {
Juan Cespedesc5c744a2009-07-23 18:22:58 +020062 fprintf(stderr, "Try `%s --help' for more information\n", progname);
Juan Cespedesc5c744a2009-07-23 18:22:58 +020063 exit(1);
64}
65
66static void
Juan Cespedesf1350522008-12-16 18:19:58 +010067usage(void) {
Juan Cespedesac3db291998-04-25 14:31:58 +020068 fprintf(stdout, "Usage: %s [option ...] [command [arg ...]]\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010069 "Trace library calls of a given program.\n\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010070 " -a, --align=COLUMN align return values in a secific column.\n"
Juan Cespedesaee09312007-08-31 18:49:48 +020071 " -A ARRAYLEN maximum number of array elements to print.\n"
Joe Damato59e3fb12009-11-06 19:45:10 -080072 " -b, --no-signals don't print signals.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010073 " -c count time and calls, and report a summary on exit.\n"
Juan Cespedesd914a202004-11-10 00:15:33 +010074# ifdef USE_DEMANGLE
Ian Wienand2d45b1a2006-02-20 22:48:07 +010075 " -C, --demangle decode low-level symbol names into user-level names.\n"
Juan Cespedesac3db291998-04-25 14:31:58 +020076# endif
Juan Cespedesc5c744a2009-07-23 18:22:58 +020077 " -D, --debug=LEVEL enable debugging (see -Dh or --debug=help).\n"
Juan Cespedesc5c744a2009-07-23 18:22:58 +020078 " -Dh, --debug=help show help on debugging.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010079 " -e expr modify which events to trace.\n"
Juan Cespedesc4e53a92009-05-06 20:36:42 +020080 " -f trace children (fork() and clone()).\n"
Juan Cespedescc813cd2009-04-07 15:45:53 +020081 " -F, --config=FILE load alternate configuration file (may be repeated).\n"
Joe Damatofa2aefc2010-10-30 19:56:50 -070082 " -g, --no-plt disable breakpoints on PLT entries.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010083 " -h, --help display this help and exit.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010084 " -i print instruction pointer at time of library call.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010085 " -l, --library=FILE print library calls from this library only.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010086 " -L do NOT display library calls.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010087 " -n, --indent=NR indent output by NR spaces for each call level nesting.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010088 " -o, --output=FILE write the trace output to that file.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010089 " -p PID attach to the process with the process ID pid.\n"
90 " -r print relative timestamps.\n"
91 " -s STRLEN specify the maximum string size to print.\n"
92 " -S display system calls.\n"
93 " -t, -tt, -ttt print absolute timestamps.\n"
94 " -T show the time spent inside each call.\n"
95 " -u USERNAME run command with the userid, groupid of username.\n"
Ian Wienand2d45b1a2006-02-20 22:48:07 +010096 " -V, --version output version information and exit.\n"
Joe Damatoab3b72c2010-10-31 00:21:53 -070097#if defined(HAVE_LIBUNWIND)
98 " -w=NR, --where=NR print backtrace showing NR stack frames at most.\n"
99#endif /* defined(HAVE_LIBUNWIND) */
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100100 " -x NAME treat the global NAME like a library subroutine.\n"
Paul Gilliambe320772006-04-24 22:06:23 +0200101#ifdef PLT_REINITALISATION_BP
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100102 " -X NAME same as -x; and PLT's will be initialized by here.\n"
Paul Gilliambe320772006-04-24 22:06:23 +0200103#endif
104 "\nReport bugs to ltrace-devel@lists.alioth.debian.org\n",
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100105 progname);
Juan Cespedes5e01f651998-03-08 22:31:44 +0100106}
107
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200108static void
109usage_debug(void) {
110 fprintf(stdout, "%s debugging option, --debug=<octal> or -D<octal>:\n", progname);
111 fprintf(stdout,
112 "\n"
113 " number ref. in source description\n"
114 " 1 general Generally helpful progress information\n"
115 " 10 event Shows every event received by a traced process\n"
116 " 20 process Shows actions carried upon a traced processes\n"
117 " 40 function Shows every entry to internal functions\n"
118 "\n"
119 "Debugging options are mixed using bitwise-or.\n"
120 "Note that the meanings and values are subject to change.\n"
121 );
122}
123
Juan Cespedesf1350522008-12-16 18:19:58 +0100124static char *
125search_for_command(char *filename) {
Juan Cespedesf1bfe202002-03-27 00:22:23 +0100126 static char pathname[PATH_MAX];
Juan Cespedes5e01f651998-03-08 22:31:44 +0100127 char *path;
128 int m, n;
129
130 if (strchr(filename, '/')) {
131 return filename;
132 }
133 for (path = getenv("PATH"); path && *path; path += m) {
134 if (strchr(path, ':')) {
135 n = strchr(path, ':') - path;
136 m = n + 1;
137 } else {
138 m = n = strlen(path);
139 }
Juan Cespedesf1bfe202002-03-27 00:22:23 +0100140 if (n + strlen(filename) + 1 >= PATH_MAX) {
141 fprintf(stderr, "Error: filename too long\n");
142 exit(1);
143 }
144 strncpy(pathname, path, n);
Juan Cespedes5e01f651998-03-08 22:31:44 +0100145 if (n && pathname[n - 1] != '/') {
146 pathname[n++] = '/';
147 }
148 strcpy(pathname + n, filename);
149 if (!access(pathname, X_OK)) {
150 return pathname;
151 }
152 }
153 return filename;
154}
155
Juan Cespedesce377d52008-12-16 19:38:10 +0100156static void
157guess_cols(void) {
158 struct winsize ws;
159 char *c;
160
161 options.align = DEFAULT_ALIGN;
162 c = getenv("COLUMNS");
163 if (c && *c) {
164 char *endptr;
165 int cols;
166 cols = strtol(c, &endptr, 0);
167 if (cols > 0 && !*endptr) {
168 options.align = cols * 5 / 8;
169 }
170 } else if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) {
171 options.align = ws.ws_col * 5 / 8;
Juan Cespedes43739a62009-04-07 13:10:08 +0200172 } else if (ioctl(2, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) {
173 options.align = ws.ws_col * 5 / 8;
Juan Cespedesce377d52008-12-16 19:38:10 +0100174 }
175}
176
Petr Machata1e4fed22012-04-01 00:45:22 +0200177static void
178add_filter_rule(struct filter *filt, const char *expr,
179 enum filter_rule_type type,
180 const char *sym, int sym_re_p,
181 const char *lib, int lib_re_p)
182{
183 fprintf(stderr, "add_filter_rule, type = %d\n", type);
184 fprintf(stderr, "+ symname = %s (re=%d)\n", sym, sym_re_p);
185 fprintf(stderr, "+ libname = %s (re=%d)\n", lib, lib_re_p);
186 struct filter_rule *rule = malloc(sizeof(*rule));
187 struct filter_lib_matcher *matcher = malloc(sizeof(*matcher));
188
189 if (rule == NULL || matcher == NULL) {
190 error(0, errno, "rule near '%s' will be ignored", expr);
191 fail:
192 free(rule);
193 free(matcher);
194 return;
195 }
196
197 regex_t symbol_re;
198 int status = (sym_re_p ? regcomp : globcomp)(&symbol_re, sym, 0);
199 if (status != 0) {
200 char buf[100];
201 regerror(status, &symbol_re, buf, sizeof buf);
202 error(0, 0, "rule near '%s' will be ignored: %s", expr, buf);
203 goto fail;
204 }
205
Petr Machata0b55b582012-04-02 00:38:46 +0200206 if (strcmp(lib, "MAIN") == 0) {
207 filter_lib_matcher_main_init(matcher);
208 } else {
209 enum filter_lib_matcher_type type
210 = lib[0] == '/' ? FLM_PATHNAME : FLM_SONAME;
Petr Machata1e4fed22012-04-01 00:45:22 +0200211
Petr Machata0b55b582012-04-02 00:38:46 +0200212 regex_t lib_re;
213 status = (lib_re_p ? regcomp : globcomp)(&lib_re, lib, 0);
214 if (status != 0) {
215 char buf[100];
216 regerror(status, &lib_re, buf, sizeof buf);
217 error(0, 0, "rule near '%s' will be ignored: %s",
218 expr, buf);
219
220 regfree(&symbol_re);
221 goto fail;
222 }
223 filter_lib_matcher_name_init(matcher, type, lib_re);
Petr Machata1e4fed22012-04-01 00:45:22 +0200224 }
225
Petr Machata1e4fed22012-04-01 00:45:22 +0200226 filter_rule_init(rule, type, matcher, symbol_re);
Petr Machatae0973cb2012-04-01 19:36:41 +0200227 filter_add_rule(filt, rule);
Petr Machata1e4fed22012-04-01 00:45:22 +0200228}
229
230static int
231parse_filter(struct filter *filt, char *expr)
232{
233 fprintf(stderr, "filter '%s'\n", expr);
234
235 /* Filter is a chain of sym@lib rules separated by '-'. If
236 * the filter expression starts with '-', the missing initial
237 * rule is implicitly *@*. */
238
239 enum filter_rule_type type = FR_ADD;
240
241 while (*expr != 0) {
242 size_t s = strcspn(expr, "@-");
243 char *symname = expr;
244 char *libname;
245 char *next = expr + s + 1;
246 enum filter_rule_type this_type = type;
247
248 if (expr[s] == 0) {
249 libname = "*";
250 expr = next - 1;
251
252 } else if (expr[s] == '-') {
253 libname = "*";
254 expr = next;
255 type = FR_SUBTRACT;
256
257 } else {
258 assert(expr[s] == '@');
259 expr[s] = 0;
260 s = strcspn(next, "-");
261 if (s == 0) {
262 libname = "*";
263 expr = next;
264 } else if (next[s] == 0) {
265 expr = next + s;
266 libname = next;
267
268 } else {
269 assert(next[s] == '-');
270 type = FR_SUBTRACT;
271 next[s] = 0;
272 expr = next + s + 1;
273 libname = next;
274 }
275 }
276
277 assert(*libname != 0);
278 char *symend = symname + strlen(symname) - 1;
279 char *libend = libname + strlen(libname) - 1;
280 int sym_is_re = 0;
281 int lib_is_re = 0;
282
283 /*
284 * /xxx/@... and ...@/xxx/ means that xxx are regular
285 * expressions. They are globs otherwise.
286 *
287 * /xxx@yyy/ is the same as /xxx/@/yyy/
288 *
289 * @/xxx matches library path name
290 * @.xxx matches library relative path name
291 */
292 if (symname[0] == '/') {
293 if (symname != symend && symend[0] == '/') {
294 ++symname;
295 *symend-- = 0;
296 sym_is_re = 1;
297
298 } else {
299 sym_is_re = 1;
300 lib_is_re = 1;
301 ++symname;
302
303 /* /XXX@YYY/ is the same as
304 * /XXX/@/YYY/. */
305 if (libend[0] != '/')
306 error(0, 0, "unmatched '/'"
307 " in symbol name");
308 else
309 *libend-- = 0;
310 }
311 }
312
313 /* If libname ends in '/', then we expect '/' in the
314 * beginning too. Otherwise the initial '/' is part
315 * of absolute file name. */
316 if (!lib_is_re && libend[0] == '/') {
317 lib_is_re = 1;
318 *libend-- = 0;
319 if (libname != libend && libname[0] == '/')
320 ++libname;
321 else
322 error(0, 0, "unmatched '/' in library name");
323 }
324
325 if (*symname == 0) /* /@AA/ */
326 symname = "*";
327 if (*libname == 0) /* /aa@/ */
328 libname = "*";
329
330 add_filter_rule(filt, expr, this_type,
331 symname, sym_is_re,
332 libname, lib_is_re);
333 }
334
335 return 0;
336}
337
338static struct filter *
339recursive_parse_chain(char *expr)
340{
341 /* Event expression grammar:
342 *
343 * Chain ::= Filter FilterList
344 * FilterList ::= eps | ',' Filter FilterList
345 * Filter ::= Rule RuleList
346 * RuleList ::= eps | '-' Rule RuleList
347 * Rule ::= eps | Glob Soname
348 * Soname ::= eps | '@' Glob | '@' '/' Filename
349 * Glob ::= '/' Regex '/'
350 */
351
352 struct filter *filt = malloc(sizeof(*filt));
353 if (filt == NULL) {
354 error(0, errno, "(part of) filter will be ignored: '%s'", expr);
355 return NULL;
356 }
357
Petr Machata35c88142012-04-04 00:54:43 +0200358 filter_init(filt);
Petr Machata1e4fed22012-04-01 00:45:22 +0200359 struct filter *next = NULL;
360 char *it;
361 int escape = 0;
362 for (it = expr; ; ++it) {
363 if (*it == 0)
364 goto done;
365
366 if (escape) {
367 escape = 0;
368 continue;
369 }
370
371 if (*it == '\\') {
372 escape = 1;
373
374 } else if (*it == ',') {
375 *it = 0;
376 next = recursive_parse_chain(it + 1);
377 done:
378 filt->next = next;
379 if (parse_filter(filt, expr) < 0) {
380 fprintf(stderr,
381 "Filter '%s' will be ignored.\n", expr);
382 free(filt);
383 filt = next;
384 }
385 return filt;
386 }
387 }
388}
389
390static void
Petr Machatab5f80ac2012-04-04 01:46:18 +0200391parse_filter_chain(const char *expr, struct filter **retp)
Petr Machata1e4fed22012-04-01 00:45:22 +0200392{
393 char *str = strdup(expr);
394 if (str == NULL) {
395 error(0, errno, "filter '%s' will be ignored", expr);
396 return;
397 }
Petr Machatab5f80ac2012-04-04 01:46:18 +0200398 *retp = recursive_parse_chain(str);
Petr Machata1e4fed22012-04-01 00:45:22 +0200399}
400
Juan Cespedesf1350522008-12-16 18:19:58 +0100401char **
402process_options(int argc, char **argv) {
Juan Cespedesac3db291998-04-25 14:31:58 +0200403 progname = argv[0];
Juan Cespedesb65bdc52008-12-16 19:50:16 +0100404 options.output = stderr;
Joe Damato59e3fb12009-11-06 19:45:10 -0800405 options.no_signals = 0;
Joe Damatoab3b72c2010-10-31 00:21:53 -0700406#if defined(HAVE_LIBUNWIND)
407 options.bt_depth = -1;
408#endif /* defined(HAVE_LIBUNWIND) */
Juan Cespedes5e01f651998-03-08 22:31:44 +0100409
Juan Cespedesce377d52008-12-16 19:38:10 +0100410 guess_cols();
411
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100412 while (1) {
Juan Cespedesac3db291998-04-25 14:31:58 +0200413 int c;
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200414 char *p;
Juan Cespedesac3db291998-04-25 14:31:58 +0200415 int option_index = 0;
416 static struct option long_options[] = {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100417 {"align", 1, 0, 'a'},
Juan Cespedesaee09312007-08-31 18:49:48 +0200418 {"config", 1, 0, 'F'},
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200419 {"debug", 1, 0, 'D'},
Juan Cespedesd914a202004-11-10 00:15:33 +0100420# ifdef USE_DEMANGLE
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100421 {"demangle", 0, 0, 'C'},
Juan Cespedes8e3e0821998-09-24 13:49:55 +0200422#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100423 {"indent", 1, 0, 'n'},
424 {"help", 0, 0, 'h'},
425 {"library", 1, 0, 'l'},
426 {"output", 1, 0, 'o'},
427 {"version", 0, 0, 'V'},
Joe Damato59e3fb12009-11-06 19:45:10 -0800428 {"no-signals", 0, 0, 'b'},
Joe Damatoab3b72c2010-10-31 00:21:53 -0700429#if defined(HAVE_LIBUNWIND)
430 {"where", 1, 0, 'w'},
431#endif /* defined(HAVE_LIBUNWIND) */
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100432 {0, 0, 0, 0}
Juan Cespedesac3db291998-04-25 14:31:58 +0200433 };
Petr Machata796267f2012-04-04 19:09:37 +0200434 c = getopt_long(argc, argv, "+cfhiLrStTVb"
Juan Cespedesd914a202004-11-10 00:15:33 +0100435# ifdef USE_DEMANGLE
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100436 "C"
Juan Cespedesac3db291998-04-25 14:31:58 +0200437# endif
Joe Damatoab3b72c2010-10-31 00:21:53 -0700438#if defined(HAVE_LIBUNWIND)
439 "a:A:D:e:F:l:n:o:p:s:u:x:X:w:", long_options,
440#else /* !defined(HAVE_LIBUNWIND) */
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200441 "a:A:D:e:F:l:n:o:p:s:u:x:X:", long_options,
Joe Damatoab3b72c2010-10-31 00:21:53 -0700442#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100443 &option_index);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100444 if (c == -1) {
Juan Cespedesac3db291998-04-25 14:31:58 +0200445 break;
Juan Cespedes5e01f651998-03-08 22:31:44 +0100446 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100447 switch (c) {
448 case 'a':
Juan Cespedesce377d52008-12-16 19:38:10 +0100449 options.align = atoi(optarg);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100450 break;
Juan Cespedesaee09312007-08-31 18:49:48 +0200451 case 'A':
Juan Cespedesda9b9532009-04-07 15:33:50 +0200452 options.arraylen = atoi(optarg);
Steve Fink1150bc42006-08-07 06:04:43 +0200453 break;
Joe Damato535e7382010-11-08 15:47:43 -0800454 case 'b':
455 options.no_signals = 1;
456 break;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100457 case 'c':
Juan Cespedesda9b9532009-04-07 15:33:50 +0200458 options.summary++;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100459 break;
Juan Cespedesd914a202004-11-10 00:15:33 +0100460#ifdef USE_DEMANGLE
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100461 case 'C':
Juan Cespedesce377d52008-12-16 19:38:10 +0100462 options.demangle++;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100463 break;
Juan Cespedesac3db291998-04-25 14:31:58 +0200464#endif
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200465 case 'D':
466 if (optarg[0]=='h') {
467 usage_debug();
468 exit(0);
469 }
470 options.debug = strtoul(optarg,&p,8);
471 if (*p) {
472 fprintf(stderr, "%s: --debug requires an octal argument\n", progname);
473 err_usage();
474 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100475 break;
Petr Machata1e4fed22012-04-01 00:45:22 +0200476
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100477 case 'e':
Petr Machatab5f80ac2012-04-04 01:46:18 +0200478 parse_filter_chain(optarg, &options.plt_filter);
Petr Machata1e4fed22012-04-01 00:45:22 +0200479 break;
480
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100481 case 'f':
Juan Cespedescc813cd2009-04-07 15:45:53 +0200482 options.follow = 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100483 break;
Juan Cespedesaee09312007-08-31 18:49:48 +0200484 case 'F':
485 {
486 struct opt_F_t *tmp = malloc(sizeof(struct opt_F_t));
487 if (!tmp) {
488 perror("ltrace: malloc");
489 exit(1);
490 }
491 tmp->filename = strdup(optarg);
492 tmp->next = opt_F;
493 opt_F = tmp;
494 break;
495 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100496 case 'h':
497 usage();
498 exit(0);
499 case 'i':
500 opt_i++;
501 break;
502 case 'l':
Petr Machata2b46cfc2012-02-18 11:17:29 +0100503 assert(!"-l support not yet implemented");
504 abort();
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100505 break;
506 case 'L':
Juan Cespedesce377d52008-12-16 19:38:10 +0100507 options.libcalls = 0;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100508 break;
509 case 'n':
Juan Cespedesb65bdc52008-12-16 19:50:16 +0100510 options.indent = atoi(optarg);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100511 break;
512 case 'o':
Juan Cespedesb65bdc52008-12-16 19:50:16 +0100513 options.output = fopen(optarg, "w");
514 if (!options.output) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100515 fprintf(stderr,
516 "Can't open %s for output: %s\n",
517 optarg, strerror(errno));
518 exit(1);
519 }
Juan Cespedesb65bdc52008-12-16 19:50:16 +0100520 setvbuf(options.output, (char *)NULL, _IOLBF, 0);
521 fcntl(fileno(options.output), F_SETFD, FD_CLOEXEC);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100522 break;
523 case 'p':
524 {
Juan Cespedesaee09312007-08-31 18:49:48 +0200525 struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t));
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100526 if (!tmp) {
527 perror("ltrace: malloc");
528 exit(1);
529 }
530 tmp->pid = atoi(optarg);
531 tmp->next = opt_p;
532 opt_p = tmp;
533 break;
534 }
535 case 'r':
536 opt_r++;
537 break;
538 case 's':
Juan Cespedescc813cd2009-04-07 15:45:53 +0200539 options.strlen = atoi(optarg);
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100540 break;
541 case 'S':
Juan Cespedesce377d52008-12-16 19:38:10 +0100542 options.syscalls = 1;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100543 break;
544 case 't':
545 opt_t++;
546 break;
547 case 'T':
548 opt_T++;
549 break;
550 case 'u':
Juan Cespedesce377d52008-12-16 19:38:10 +0100551 options.user = optarg;
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100552 break;
553 case 'V':
554 printf("ltrace version " PACKAGE_VERSION ".\n"
Juan Cespedes1423f082009-04-07 00:45:33 +0200555 "Copyright (C) 1997-2009 Juan Cespedes <cespedes@debian.org>.\n"
Juan Cespedesaee09312007-08-31 18:49:48 +0200556 "This is free software; see the GNU General Public Licence\n"
557 "version 2 or later for copying conditions. There is NO warranty.\n");
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100558 exit(0);
Joe Damatofa2aefc2010-10-30 19:56:50 -0700559 break;
Joe Damato535e7382010-11-08 15:47:43 -0800560#if defined(HAVE_LIBUNWIND)
561 case 'w':
562 options.bt_depth = atoi(optarg);
Joe Damato59e3fb12009-11-06 19:45:10 -0800563 break;
Joe Damato535e7382010-11-08 15:47:43 -0800564#endif /* defined(HAVE_LIBUNWIND) */
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100565 case 'X':
Paul Gilliambe320772006-04-24 22:06:23 +0200566#ifdef PLT_REINITALISATION_BP
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100567 PLTs_initialized_by_here = optarg;
Paul Gilliambe320772006-04-24 22:06:23 +0200568#else
Juan Cespedesaee09312007-08-31 18:49:48 +0200569 fprintf(stderr, "WARNING: \"-X\" not used for this "
Paul Gilliambe320772006-04-24 22:06:23 +0200570 "architecture: assuming you meant \"-x\"\n");
571#endif
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100572 /* Fall Thru */
Juan Cespedesac3db291998-04-25 14:31:58 +0200573
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100574 case 'x':
Petr Machatada3edbf2012-04-04 02:20:21 +0200575 parse_filter_chain(optarg, &options.static_filter);
576 break;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100577
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100578 default:
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200579 err_usage();
Juan Cespedes5e01f651998-03-08 22:31:44 +0100580 }
581 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100582 argc -= optind;
583 argv += optind;
Juan Cespedes5e01f651998-03-08 22:31:44 +0100584
Juan Cespedesaee09312007-08-31 18:49:48 +0200585 if (!opt_F) {
586 opt_F = malloc(sizeof(struct opt_F_t));
587 opt_F->next = malloc(sizeof(struct opt_F_t));
588 opt_F->next->next = NULL;
589 opt_F->filename = USER_CONFIG_FILE;
590 opt_F->next->filename = SYSTEM_CONFIG_FILE;
591 }
Steve Fink58c73a72006-07-17 23:18:35 +0200592 /* Reverse the config file list since it was built by
593 * prepending, and it would make more sense to process the
594 * files in the order they were given. Probably it would make
595 * more sense to keep a tail pointer instead? */
596 {
Juan Cespedesaee09312007-08-31 18:49:48 +0200597 struct opt_F_t *egg = NULL;
598 struct opt_F_t *chicken;
599 while (opt_F) {
600 chicken = opt_F->next;
601 opt_F->next = egg;
602 egg = opt_F;
603 opt_F = chicken;
604 }
605 opt_F = egg;
Steve Fink58c73a72006-07-17 23:18:35 +0200606 }
607
Petr Machata0b55b582012-04-02 00:38:46 +0200608 /* Set default filter. Use @MAIN for now, as that's what
609 * ltrace used to have in the past. XXX Maybe we should make
610 * this "*" instead. */
Petr Machatab5f80ac2012-04-04 01:46:18 +0200611 if (options.plt_filter == NULL) {
612 parse_filter_chain("@MAIN", &options.plt_filter);
Petr Machata03673892012-04-03 13:51:09 +0200613 options.hide_caller = 1;
614 }
Petr Machatae0973cb2012-04-01 19:36:41 +0200615
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100616 if (!opt_p && argc < 1) {
Juan Cespedesac3db291998-04-25 14:31:58 +0200617 fprintf(stderr, "%s: too few arguments\n", progname);
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200618 err_usage();
Juan Cespedes5e01f651998-03-08 22:31:44 +0100619 }
Juan Cespedesf666d191998-09-20 23:04:34 +0200620 if (opt_r && opt_t) {
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100621 fprintf(stderr, "%s: Incompatible options -r and -t\n",
622 progname);
Juan Cespedesc5c744a2009-07-23 18:22:58 +0200623 err_usage();
Juan Cespedesf666d191998-09-20 23:04:34 +0200624 }
Ian Wienand2d45b1a2006-02-20 22:48:07 +0100625 if (argc > 0) {
Juan Cespedesac3db291998-04-25 14:31:58 +0200626 command = search_for_command(argv[0]);
Juan Cespedes1fe93d51998-03-13 00:29:21 +0100627 }
Juan Cespedesac3db291998-04-25 14:31:58 +0200628 return &argv[0];
Juan Cespedes5e01f651998-03-08 22:31:44 +0100629}