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