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