xref: /illumos-gate/usr/src/cmd/abi/apptracecmd/apptrace.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <sys/wait.h>
35 #include <apptrace.h>
36 #include <libintl.h>
37 #include <locale.h>
38 
39 #ifdef TRUE
40 #undef TRUE
41 #endif
42 #ifdef FALSE
43 #undef FALSE
44 #endif
45 #define	TRUE  1
46 #define	FALSE 0
47 
48 /* Various list pointers */
49 static char *fromlist;
50 static char *fromexcl;
51 static char *tolist;
52 static char *toexcl;
53 
54 static char *iflist;
55 static char *ifexcl;
56 static char *viflist;
57 static char *vifexcl;
58 
59 /* The supported options */
60 static char const *optlet = "F:fo:T:t:v:";
61 /* basename(argv[0]) */
62 static char const *command;
63 
64 /* The environment variables that'll get picked up by apptrace.so.1 */
65 static char const *APPTRACE_BINDTO = "APPTRACE_BINDTO=";
66 static char const *APPTRACE_BINDTO_EXCLUDE = "APPTRACE_BINDTO_EXCLUDE=";
67 static char const *APPTRACE_BINDFROM = "APPTRACE_BINDFROM=";
68 static char const *APPTRACE_BINDFROM_EXCLUDE = "APPTRACE_BINDFROM_EXCLUDE=";
69 static char const *APPTRACE_OUTPUT = "APPTRACE_OUTPUT=";
70 static char const *APPTRACE_PID = "APPTRACE_PID=";
71 static char const *APPTRACE_INTERFACES = "APPTRACE_INTERFACES=";
72 static char const *APPTRACE_INTERFACES_EXCLUDE = "APPTRACE_INTERFACES_EXCLUDE=";
73 static char const *APPTRACE_VERBOSE = "APPTRACE_VERBOSE=";
74 static char const *APPTRACE_VERBOSE_EXCLUDE = "APPTRACE_VERBOSE_EXCLUDE=";
75 
76 /* Some default values for the above */
77 static char *LD_AUDIT = "LD_AUDIT=/usr/lib/abi/apptrace.so.1";
78 #if	defined(sparc) || defined(__sparcv9)
79 static char *LD_AUDIT_64 =
80 	"LD_AUDIT_64=/usr/lib/abi/sparcv9/apptrace.so.1";
81 #elif	defined(i386) || defined(__amd64)
82 static char *LD_AUDIT_64 =
83 	"LD_AUDIT_64=/usr/lib/abi/amd64/apptrace.so.1";
84 #else
85 #error Unsupported Platform
86 #endif
87 
88 static char const *one = "1";
89 
90 /* The local support functions */
91 static void usage(char const *);
92 static void stuffenv(char const *, char const *);
93 static char *buildlist(char **, char const *);
94 
95 int
96 main(int argc, char **argv)
97 {
98 	int	opt;
99 	int	fflag = FALSE;
100 	int	errflg = FALSE;
101 	char	*outfile = NULL;
102 	int	stat_loc;
103 	pid_t	wret, pid;
104 
105 	(void) setlocale(LC_ALL, "");
106 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
107 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
108 #endif
109 	(void) textdomain(TEXT_DOMAIN);
110 
111 
112 	/* Squirrel the basename of the command name away. */
113 	if ((command = strrchr(argv[0], '/')) != NULL)
114 		command++;
115 	else
116 		command = argv[0];
117 
118 	while ((opt = getopt(argc, argv, optlet)) != EOF) {
119 		switch (opt) {
120 		case 'F':
121 			if (*optarg == '!')
122 				(void) buildlist(&fromexcl, optarg + 1);
123 			else
124 				(void) buildlist(&fromlist, optarg);
125 			break;
126 		case 'f':
127 			fflag = TRUE;
128 			break;
129 		case 'o':
130 			outfile = optarg;
131 			break;
132 		case 'T':
133 			if (*optarg == '!')
134 				(void) buildlist(&toexcl, optarg + 1);
135 			else
136 				(void) buildlist(&tolist, optarg);
137 			break;
138 		case 't':
139 			if (*optarg == '!')
140 				(void) buildlist(&ifexcl, optarg + 1);
141 			else
142 				(void) buildlist(&iflist, optarg);
143 			break;
144 		case 'v':
145 			if (*optarg == '!')
146 				(void) buildlist(&vifexcl, optarg + 1);
147 			else
148 				(void) buildlist(&viflist, optarg);
149 			break;
150 		default:
151 			errflg = TRUE;
152 			break;
153 		}
154 	}
155 
156 	/*
157 	 * Whack the argument vector so that the remainder will be
158 	 * ready for passing to exec
159 	 */
160 	argc -= optind;
161 	argv += optind;
162 
163 	/*
164 	 * If there was a problem with the options, or there was no command
165 	 * to be run, then give the usage message and bugout.
166 	 */
167 	if (errflg || argc <= 0) {
168 		usage(command);
169 		exit(EXIT_FAILURE);
170 	}
171 
172 	/*
173 	 * This is where the environment gets setup.
174 	 */
175 	if (fflag == TRUE)
176 		stuffenv(APPTRACE_PID, one);
177 
178 	if (fromexcl != NULL)
179 		stuffenv(APPTRACE_BINDFROM_EXCLUDE, fromexcl);
180 	if (fromlist != NULL)
181 		stuffenv(APPTRACE_BINDFROM, fromlist);
182 
183 	if (tolist != NULL)
184 		stuffenv(APPTRACE_BINDTO, tolist);
185 	if (toexcl != NULL)
186 		stuffenv(APPTRACE_BINDTO_EXCLUDE, toexcl);
187 
188 	if (iflist != NULL)
189 		stuffenv(APPTRACE_INTERFACES, iflist);
190 	if (ifexcl != NULL)
191 		stuffenv(APPTRACE_INTERFACES_EXCLUDE, ifexcl);
192 
193 	if (viflist != NULL)
194 		stuffenv(APPTRACE_VERBOSE, viflist);
195 	if (vifexcl != NULL)
196 		stuffenv(APPTRACE_VERBOSE_EXCLUDE, vifexcl);
197 
198 	if (outfile != NULL)
199 		stuffenv(APPTRACE_OUTPUT, outfile);
200 
201 	/*
202 	 * It is the setting of the LD_AUDIT environment variable
203 	 * that tells ld.so.1 to enable link auditing when the child
204 	 * is exec()ed.
205 	 */
206 	(void) putenv(LD_AUDIT);
207 	(void) putenv(LD_AUDIT_64);
208 
209 	/*
210 	 * The environment is now all setup.
211 	 * For those about to rock, we salute you!
212 	 */
213 	pid = fork();
214 	switch (pid) {
215 		/* Error */
216 	case -1:
217 		(void) fprintf(stderr, gettext("%s: fork failed: %s\n"),
218 		    command, strerror(errno));
219 		exit(EXIT_FAILURE);
220 		break;
221 		/* Child */
222 	case 0:
223 		/*
224 		 * Usual failure is argv[0] does not exist or is
225 		 * not executable.
226 		 */
227 		if (execvp(argv[0], argv)) {
228 			(void) fprintf(stderr, gettext("%s: %s: %s\n"),
229 			    command, argv[0], strerror(errno));
230 			_exit(EXIT_FAILURE);
231 		}
232 		break;
233 		/* Parent */
234 	default:
235 		wret = waitpid(pid, &stat_loc, 0);
236 		if (wret == -1) {
237 			(void) fprintf(stderr,
238 			    gettext("%s: waitpid failed: %s\n"),
239 			    command, strerror(errno));
240 			exit(EXIT_FAILURE);
241 		}
242 
243 		if (wret != pid) {
244 			(void) fprintf(stderr,
245 			    gettext("%s: "
246 			    "waitpid returned %ld when child pid was %ld\n"),
247 			    command, wret, pid);
248 			exit(EXIT_FAILURE);
249 		}
250 
251 		if (WIFSIGNALED(stat_loc)) {
252 			(void) fprintf(stderr, gettext("\n%s: %s: %s"),
253 			    command, argv[0], strsignal(WTERMSIG(stat_loc)));
254 			if (WCOREDUMP(stat_loc)) {
255 				(void) fputs(gettext("(Core dump)"), stderr);
256 #ifdef DEBUG
257 				(void) fputs(gettext("\nRunning pstack:\n"),
258 				    stderr);
259 				(void) putenv("LD_AUDIT=");
260 				(void) putenv("LD_AUDIT_64=");
261 				(void) system("/usr/proc/bin/pstack core");
262 #endif
263 			}
264 			(void) putc('\n', stderr);
265 		}
266 
267 		/* Normal return from main() */
268 		return (WEXITSTATUS(stat_loc));
269 	}
270 	/* NOTREACHED */
271 }
272 
273 /*
274  * Take a string in the form "VAR=" and another in the
275  * form "value" and paste them together.
276  */
277 static void
278 stuffenv(char const *var, char const *val)
279 {
280 	int lenvar, lenval;
281 	char *stuff;
282 
283 	lenvar = strlen(var);
284 	lenval = strlen(val);
285 
286 	if ((stuff = malloc(lenvar + lenval + 1)) == NULL) {
287 		(void) fprintf(stderr, gettext("%s: malloc failed\n"), command);
288 		exit(EXIT_FAILURE);
289 	}
290 	(void) sprintf(stuff, "%s%s", var, val);
291 	(void) putenv(stuff);
292 }
293 
294 /*
295  * If *dst is empty, use strdup to duplicate src.
296  * Otherwise:  dst = dst + "," + src;
297  */
298 static char *
299 buildlist(char **dst, char const *src)
300 {
301 	int len;
302 	char *p;
303 
304 	/*
305 	 * If dst is still empty then dup,
306 	 * if dup succeeds set dst.
307 	 */
308 	if (*dst == NULL) {
309 		p = strdup(src);
310 		if (p == NULL)
311 			goto error;
312 		*dst = p;
313 		return (p);
314 	}
315 
316 	len = strlen(*dst);
317 
318 	/* +2 because of the comma we add below */
319 	if ((p = realloc(*dst, len + strlen(src) + 2)) == NULL)
320 		goto error;
321 
322 	*dst = p;
323 
324 	*(*dst + len) = ',';
325 	(void) strcpy((*dst + len + 1), src);
326 
327 	return (*dst);
328 
329 error:
330 	(void) fprintf(stderr, gettext("%s: allocation failed: %s\n"),
331 	    command, strerror(errno));
332 	exit(EXIT_FAILURE);
333 	/* NOTREACHED */
334 }
335 
336 static void
337 usage(char const *prog)
338 {
339 	(void) fprintf(stderr, gettext("Usage: %s [-f][-F [!]tracefromlist]"
340 	    "[-T [!]tracetolist][-o outputfile]\n"
341 	    "	[-t calls][-v calls] prog [prog arguments]\n"
342 
343 	    "	-F <bindfromlist>\n"
344 	    "		A comma separated list of libraries that are to be\n"
345 	    "		traced.  Only calls from these libraries will be\n"
346 	    "		traced.  The default is to trace calls from the\n"
347 	    "		main executable.\n"
348 	    "		If <bindfromlist> begins with a ! then it defines\n"
349 	    "		a list of libraries to exclude from the trace.\n"
350 	    "	-T <bindtolist>\n"
351 	    "		A comma separated list of libraries that are to be\n"
352 	    "		traced.  Only calls to these libraries will be\n"
353 	    "		traced.  The default is to trace all calls.\n"
354 	    "		If <bindtolist> begins with a ! then it defines\n"
355 	    "		a list of libraries to exclude from the trace.\n"
356 	    "	-o <outputfile>\n"
357 	    "		%s output will be directed to 'outputfile'.\n"
358 	    "		by default it is placed on stderr\n"
359 	    "	-f\n"
360 	    "		Follow all children created by fork() and also\n"
361 	    "		print apptrace output for the children.  This also\n"
362 	    "		causes a 'pid' to be added to each output line\n"
363 	    "	-t <tracelist>\n"
364 	    "		A comma separated list of interfaces to trace.\n"
365 	    "		A list preceded by ! is an exlusion list.\n"
366 	    "	-v <verboselist>\n"
367 	    "		A comman separated list of interfaces to trace\n"
368 	    "		verbosely.\n"
369 	    "		A list preceded by ! is an exclusion list.\n"
370 	    "		Interfaces matched in -v do not also need to be\n"
371 	    "		named by -t\n"
372 	    "	All lists may use shell style wild cards.\n"
373 	    "	Leading path components or suffixes are not required when\n"
374 	    "	listing libraries (ie. libc will match /usr/lib/libc.so.1).\n"),
375 	    prog, prog);
376 }
377