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