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