xref: /freebsd/usr.bin/truss/main.c (revision c2b51d4457560eed58e28bcc230e42722aa01a4f)
1d5303c80SXin LI /*-
20a6c71f8SWarner Losh  * Copyright 1997 Sean Eric Fagan
309d64da3SSean Eric Fagan  *
409d64da3SSean Eric Fagan  * Redistribution and use in source and binary forms, with or without
509d64da3SSean Eric Fagan  * modification, are permitted provided that the following conditions
609d64da3SSean Eric Fagan  * are met:
709d64da3SSean Eric Fagan  * 1. Redistributions of source code must retain the above copyright
809d64da3SSean Eric Fagan  *    notice, this list of conditions and the following disclaimer.
909d64da3SSean Eric Fagan  * 2. Redistributions in binary form must reproduce the above copyright
1009d64da3SSean Eric Fagan  *    notice, this list of conditions and the following disclaimer in the
1109d64da3SSean Eric Fagan  *    documentation and/or other materials provided with the distribution.
1209d64da3SSean Eric Fagan  * 3. All advertising materials mentioning features or use of this software
1309d64da3SSean Eric Fagan  *    must display the following acknowledgement:
1409d64da3SSean Eric Fagan  *	This product includes software developed by Sean Eric Fagan
1509d64da3SSean Eric Fagan  * 4. Neither the name of the author may be used to endorse or promote
1609d64da3SSean Eric Fagan  *    products derived from this software without specific prior written
1709d64da3SSean Eric Fagan  *    permission.
1809d64da3SSean Eric Fagan  *
1909d64da3SSean Eric Fagan  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2009d64da3SSean Eric Fagan  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2109d64da3SSean Eric Fagan  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2209d64da3SSean Eric Fagan  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2309d64da3SSean Eric Fagan  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2409d64da3SSean Eric Fagan  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2509d64da3SSean Eric Fagan  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2609d64da3SSean Eric Fagan  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2709d64da3SSean Eric Fagan  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2809d64da3SSean Eric Fagan  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2909d64da3SSean Eric Fagan  * SUCH DAMAGE.
3009d64da3SSean Eric Fagan  */
3109d64da3SSean Eric Fagan 
32b956c13cSPhilippe Charnier #include <sys/cdefs.h>
33b956c13cSPhilippe Charnier __FBSDID("$FreeBSD$");
343cf51049SPhilippe Charnier 
3509d64da3SSean Eric Fagan /*
36b2bf146eSBenedict Reuschling  * The main module for truss.  Surprisingly simple, but, then, the other
37bbeaf6c0SSean Eric Fagan  * files handle the bulk of the work.  And, of course, the kernel has to
38bbeaf6c0SSean Eric Fagan  * do a lot of the work :).
39bbeaf6c0SSean Eric Fagan  */
40bbeaf6c0SSean Eric Fagan 
41580e0a2bSDag-Erling Smørgrav #include <sys/param.h>
425cdf6a13SMartin Cracauer #include <sys/types.h>
431d631f7eSMike Barcroft #include <sys/time.h>
445cdf6a13SMartin Cracauer #include <sys/resource.h>
455d2d083cSXin LI #include <sys/sysctl.h>
46d5303c80SXin LI #include <sys/wait.h>
47580e0a2bSDag-Erling Smørgrav 
483cf51049SPhilippe Charnier #include <err.h>
49821df508SXin LI #include <errno.h>
50821df508SXin LI #include <fcntl.h>
513cf51049SPhilippe Charnier #include <signal.h>
52bbeaf6c0SSean Eric Fagan #include <stdio.h>
53bbeaf6c0SSean Eric Fagan #include <stdlib.h>
54bbeaf6c0SSean Eric Fagan #include <string.h>
5537169f94SMatthew N. Dodd #include <time.h>
5695c4ef65SPeter Wemm #include <unistd.h>
57bbeaf6c0SSean Eric Fagan 
58ec0bed25SMatthew N. Dodd #include "truss.h"
591be5d704SMark Murray #include "extern.h"
60ee3b0f6eSDiomidis Spinellis #include "syscall.h"
61bbeaf6c0SSean Eric Fagan 
623e1b6078SMarcel Moolenaar #define	MAXARGS	6
63bbeaf6c0SSean Eric Fagan 
645321ae86SAlfred Perlstein static void
653cf51049SPhilippe Charnier usage(void)
663cf51049SPhilippe Charnier {
673cf51049SPhilippe Charnier 	fprintf(stderr, "%s\n%s\n",
68ee3b0f6eSDiomidis Spinellis 	    "usage: truss [-cfaedDS] [-o file] [-s strsize] -p pid",
69ee3b0f6eSDiomidis Spinellis 	    "       truss [-cfaedDS] [-o file] [-s strsize] command [args]");
70bbeaf6c0SSean Eric Fagan 	exit(1);
71bbeaf6c0SSean Eric Fagan }
72bbeaf6c0SSean Eric Fagan 
733625b514SSean Eric Fagan /*
743625b514SSean Eric Fagan  * WARNING! "FreeBSD a.out" must be first, or set_etype will not
753625b514SSean Eric Fagan  * work correctly.
763625b514SSean Eric Fagan  */
77a02c83afSEd Schouten static struct ex_types {
781be5d704SMark Murray 	const char *type;
79ec0bed25SMatthew N. Dodd 	void (*enter_syscall)(struct trussinfo *, int);
801bcb5f5aSMarcel Moolenaar 	long (*exit_syscall)(struct trussinfo *, int);
81bbeaf6c0SSean Eric Fagan } ex_types[] = {
82a5d31d16SDavid Malone #ifdef __amd64__
83a5d31d16SDavid Malone 	{ "FreeBSD ELF64", amd64_syscall_entry, amd64_syscall_exit },
849a55503eSJohn Baldwin 	{ "FreeBSD ELF32", amd64_fbsd32_syscall_entry, amd64_fbsd32_syscall_exit },
859a55503eSJohn Baldwin 	{ "Linux ELF32", amd64_linux32_syscall_entry, amd64_linux32_syscall_exit },
86a5d31d16SDavid Malone #endif
8750cc4492SSean Eric Fagan #ifdef __i386__
88bbeaf6c0SSean Eric Fagan 	{ "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit },
8987893934SPeter Wemm 	{ "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit },
900629483cSMatthew N. Dodd 	{ "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit },
91bbeaf6c0SSean Eric Fagan 	{ "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit },
9250cc4492SSean Eric Fagan #endif
93a3e32192SMarcel Moolenaar #ifdef __ia64__
94a3e32192SMarcel Moolenaar 	{ "FreeBSD ELF64", ia64_syscall_entry, ia64_syscall_exit },
95a3e32192SMarcel Moolenaar #endif
967fa9dc1cSPeter Grehan #ifdef __powerpc__
977fa9dc1cSPeter Grehan 	{ "FreeBSD ELF", powerpc_syscall_entry, powerpc_syscall_exit },
987fa9dc1cSPeter Grehan 	{ "FreeBSD ELF32", powerpc_syscall_entry, powerpc_syscall_exit },
994e583321SNathan Whitehorn #ifdef __powerpc64__
1004e583321SNathan Whitehorn 	{ "FreeBSD ELF64", powerpc64_syscall_entry, powerpc64_syscall_exit },
1014e583321SNathan Whitehorn #endif
1027fa9dc1cSPeter Grehan #endif
103f84c971aSJake Burkholder #ifdef __sparc64__
104f84c971aSJake Burkholder 	{ "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit },
105f84c971aSJake Burkholder #endif
10689fe547fSWarner Losh #ifdef __mips__
10789fe547fSWarner Losh 	{ "FreeBSD ELF", mips_syscall_entry, mips_syscall_exit },
10889fe547fSWarner Losh 	{ "FreeBSD ELF32", mips_syscall_entry, mips_syscall_exit },
10989fe547fSWarner Losh 	{ "FreeBSD ELF64", mips_syscall_entry, mips_syscall_exit }, // XXX
11089fe547fSWarner Losh #endif
111bbeaf6c0SSean Eric Fagan 	{ 0, 0, 0 },
112bbeaf6c0SSean Eric Fagan };
113bbeaf6c0SSean Eric Fagan 
114bbeaf6c0SSean Eric Fagan /*
115bbeaf6c0SSean Eric Fagan  * Set the execution type.  This is called after every exec, and when
1165d2d083cSXin LI  * a process is first monitored.
117bbeaf6c0SSean Eric Fagan  */
118bbeaf6c0SSean Eric Fagan 
119bbeaf6c0SSean Eric Fagan static struct ex_types *
1205321ae86SAlfred Perlstein set_etype(struct trussinfo *trussinfo)
1215321ae86SAlfred Perlstein {
122bbeaf6c0SSean Eric Fagan 	struct ex_types *funcs;
12394355cfdSAndrey Zonov 	size_t len;
12494355cfdSAndrey Zonov 	int error;
12594355cfdSAndrey Zonov 	int mib[4];
1261be5d704SMark Murray 	char progt[32];
127bbeaf6c0SSean Eric Fagan 
12894355cfdSAndrey Zonov 	len = sizeof(progt);
1295d2d083cSXin LI 	mib[0] = CTL_KERN;
1305d2d083cSXin LI 	mib[1] = KERN_PROC;
1315d2d083cSXin LI 	mib[2] = KERN_PROC_SV_NAME;
1325d2d083cSXin LI 	mib[3] = trussinfo->pid;
1335d2d083cSXin LI 	error = sysctl(mib, 4, progt, &len, NULL, 0);
1345d2d083cSXin LI 	if (error != 0)
1355d2d083cSXin LI 		err(2, "can not get etype");
136bbeaf6c0SSean Eric Fagan 
137bbeaf6c0SSean Eric Fagan 	for (funcs = ex_types; funcs->type; funcs++)
13894355cfdSAndrey Zonov 		if (strcmp(funcs->type, progt) == 0)
139bbeaf6c0SSean Eric Fagan 			break;
140bbeaf6c0SSean Eric Fagan 
1414525f3a8SDag-Erling Smørgrav 	if (funcs->type == NULL) {
1423625b514SSean Eric Fagan 		funcs = &ex_types[0];
143b956c13cSPhilippe Charnier 		warn("execution type %s is not supported -- using %s",
1444525f3a8SDag-Erling Smørgrav 		    progt, funcs->type);
1453625b514SSean Eric Fagan 	}
1465321ae86SAlfred Perlstein 	return (funcs);
147bbeaf6c0SSean Eric Fagan }
148bbeaf6c0SSean Eric Fagan 
149d75300bfSAlfred Perlstein char *
150d75300bfSAlfred Perlstein strsig(int sig)
151d75300bfSAlfred Perlstein {
152d75300bfSAlfred Perlstein 	char *ret;
153d75300bfSAlfred Perlstein 
154d75300bfSAlfred Perlstein 	ret = NULL;
155d75300bfSAlfred Perlstein 	if (sig > 0 && sig < NSIG) {
15612dacf62SJilles Tjoelker 		asprintf(&ret, "SIG%s", sys_signame[sig]);
157d75300bfSAlfred Perlstein 		if (ret == NULL)
158d75300bfSAlfred Perlstein 			return (NULL);
159d75300bfSAlfred Perlstein 	}
160d75300bfSAlfred Perlstein 	return (ret);
161d75300bfSAlfred Perlstein }
162d75300bfSAlfred Perlstein 
1633cf51049SPhilippe Charnier int
1645321ae86SAlfred Perlstein main(int ac, char **av)
1655321ae86SAlfred Perlstein {
1665695afdeSAndrey Zonov 	struct timespec timediff;
167896fc463SAndrey Zonov 	struct sigaction sa;
168bbeaf6c0SSean Eric Fagan 	struct ex_types *funcs;
169ec0bed25SMatthew N. Dodd 	struct trussinfo *trussinfo;
17094355cfdSAndrey Zonov 	char *fname;
171d75300bfSAlfred Perlstein 	char *signame;
17294355cfdSAndrey Zonov 	char **command;
17394355cfdSAndrey Zonov 	pid_t childpid;
1745695afdeSAndrey Zonov 	int c, initial_open, status;
175bbeaf6c0SSean Eric Fagan 
17672aa911aSAlfred Perlstein 	fname = NULL;
17772aa911aSAlfred Perlstein 	initial_open = 1;
17872aa911aSAlfred Perlstein 
179ec0bed25SMatthew N. Dodd 	/* Initialize the trussinfo struct */
180216fa4c6SXin LI 	trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo));
181ec0bed25SMatthew N. Dodd 	if (trussinfo == NULL)
182216fa4c6SXin LI 		errx(1, "calloc() failed");
1835d2d083cSXin LI 
184ec0bed25SMatthew N. Dodd 	trussinfo->outfile = stderr;
1850cf21b4fSBrian Somers 	trussinfo->strsize = 32;
1865d2d083cSXin LI 	trussinfo->pr_why = S_NONE;
1875d2d083cSXin LI 	trussinfo->curthread = NULL;
1885d2d083cSXin LI 	SLIST_INIT(&trussinfo->threadlist);
189ee3b0f6eSDiomidis Spinellis 	while ((c = getopt(ac, av, "p:o:facedDs:S")) != -1) {
190bbeaf6c0SSean Eric Fagan 		switch (c) {
191bbeaf6c0SSean Eric Fagan 		case 'p':	/* specified pid */
192ec0bed25SMatthew N. Dodd 			trussinfo->pid = atoi(optarg);
193ef29ac7fSXin LI 			/* make sure i don't trace me */
194ef29ac7fSXin LI 			if (trussinfo->pid == getpid()) {
195ef29ac7fSXin LI 				fprintf(stderr, "attempt to grab self.\n");
196ef29ac7fSXin LI 				exit(2);
197ef29ac7fSXin LI 			}
198bbeaf6c0SSean Eric Fagan 			break;
199c03bfcc8SMatthew N. Dodd 		case 'f': /* Follow fork()'s */
200c03bfcc8SMatthew N. Dodd 			trussinfo->flags |= FOLLOWFORKS;
201c03bfcc8SMatthew N. Dodd 			break;
2029897b203SMatthew N. Dodd 		case 'a': /* Print execve() argument strings. */
2039897b203SMatthew N. Dodd 			trussinfo->flags |= EXECVEARGS;
2049897b203SMatthew N. Dodd 			break;
205ee3b0f6eSDiomidis Spinellis 		case 'c': /* Count number of system calls and time. */
206ee3b0f6eSDiomidis Spinellis 			trussinfo->flags |= COUNTONLY;
207ee3b0f6eSDiomidis Spinellis 			break;
2089897b203SMatthew N. Dodd 		case 'e': /* Print execve() environment strings. */
2099897b203SMatthew N. Dodd 			trussinfo->flags |= EXECVEENVS;
2109897b203SMatthew N. Dodd 			break;
2110d0bd00eSMatthew N. Dodd 		case 'd': /* Absolute timestamps */
2120d0bd00eSMatthew N. Dodd 			trussinfo->flags |= ABSOLUTETIMESTAMPS;
2130d0bd00eSMatthew N. Dodd 			break;
2140d0bd00eSMatthew N. Dodd 		case 'D': /* Relative timestamps */
2150d0bd00eSMatthew N. Dodd 			trussinfo->flags |= RELATIVETIMESTAMPS;
2160d0bd00eSMatthew N. Dodd 			break;
217bbeaf6c0SSean Eric Fagan 		case 'o':	/* Specified output file */
2183cf51049SPhilippe Charnier 			fname = optarg;
219bbeaf6c0SSean Eric Fagan 			break;
2200cf21b4fSBrian Somers 		case 's':	/* Specified string size */
2210cf21b4fSBrian Somers 			trussinfo->strsize = atoi(optarg);
2220cf21b4fSBrian Somers 			break;
223bbeaf6c0SSean Eric Fagan 		case 'S':	/* Don't trace signals */
224ec0bed25SMatthew N. Dodd 			trussinfo->flags |= NOSIGS;
225bbeaf6c0SSean Eric Fagan 			break;
226bbeaf6c0SSean Eric Fagan 		default:
227bbeaf6c0SSean Eric Fagan 			usage();
228bbeaf6c0SSean Eric Fagan 		}
229bbeaf6c0SSean Eric Fagan 	}
230bbeaf6c0SSean Eric Fagan 
231bbeaf6c0SSean Eric Fagan 	ac -= optind; av += optind;
2325321ae86SAlfred Perlstein 	if ((trussinfo->pid == 0 && ac == 0) ||
2335321ae86SAlfred Perlstein 	    (trussinfo->pid != 0 && ac != 0))
234bbeaf6c0SSean Eric Fagan 		usage();
235bbeaf6c0SSean Eric Fagan 
2363cf51049SPhilippe Charnier 	if (fname != NULL) { /* Use output file */
237ecbb6d34SJaakko Heinonen 		/*
238*c2b51d44SMateusz Guzik 		 * Set close-on-exec ('e'), so that the output file is not
239*c2b51d44SMateusz Guzik 		 * shared with the traced process.
240ecbb6d34SJaakko Heinonen 		 */
241*c2b51d44SMateusz Guzik 		if ((trussinfo->outfile = fopen(fname, "we")) == NULL)
242*c2b51d44SMateusz Guzik 			err(1, "cannot open %s", fname);
243e04c3786SJaakko Heinonen 	}
2443cf51049SPhilippe Charnier 
245bbeaf6c0SSean Eric Fagan 	/*
246bbeaf6c0SSean Eric Fagan 	 * If truss starts the process itself, it will ignore some signals --
247bbeaf6c0SSean Eric Fagan 	 * they should be passed off to the process, which may or may not
248bbeaf6c0SSean Eric Fagan 	 * exit.  If, however, we are examining an already-running process,
249bbeaf6c0SSean Eric Fagan 	 * then we restore the event mask on these same signals.
250bbeaf6c0SSean Eric Fagan 	 */
251bbeaf6c0SSean Eric Fagan 
252ec0bed25SMatthew N. Dodd 	if (trussinfo->pid == 0) {	/* Start a command ourselves */
253bbeaf6c0SSean Eric Fagan 		command = av;
254ec0bed25SMatthew N. Dodd 		trussinfo->pid = setup_and_wait(command);
255bbeaf6c0SSean Eric Fagan 		signal(SIGINT, SIG_IGN);
256bbeaf6c0SSean Eric Fagan 		signal(SIGTERM, SIG_IGN);
257bbeaf6c0SSean Eric Fagan 		signal(SIGQUIT, SIG_IGN);
258bbeaf6c0SSean Eric Fagan 	} else {
259896fc463SAndrey Zonov 		sa.sa_handler = restore_proc;
260896fc463SAndrey Zonov 		sa.sa_flags = 0;
261896fc463SAndrey Zonov 		sigemptyset(&sa.sa_mask);
262896fc463SAndrey Zonov 		sigaction(SIGINT, &sa, NULL);
263896fc463SAndrey Zonov 		sigaction(SIGQUIT, &sa, NULL);
264896fc463SAndrey Zonov 		sigaction(SIGTERM, &sa, NULL);
2655d2d083cSXin LI 		start_tracing(trussinfo->pid);
266bbeaf6c0SSean Eric Fagan 	}
267bbeaf6c0SSean Eric Fagan 
268bbeaf6c0SSean Eric Fagan 
269bbeaf6c0SSean Eric Fagan 	/*
270bbeaf6c0SSean Eric Fagan 	 * At this point, if we started the process, it is stopped waiting to
271bbeaf6c0SSean Eric Fagan 	 * be woken up, either in exit() or in execve().
272bbeaf6c0SSean Eric Fagan 	 */
273bbeaf6c0SSean Eric Fagan 
274c03bfcc8SMatthew N. Dodd START_TRACE:
275ec0bed25SMatthew N. Dodd 	funcs = set_etype(trussinfo);
2765d2d083cSXin LI 
2775d2d083cSXin LI 	initial_open = 0;
278bbeaf6c0SSean Eric Fagan 	/*
279bbeaf6c0SSean Eric Fagan 	 * At this point, it's a simple loop, waiting for the process to
280bbeaf6c0SSean Eric Fagan 	 * stop, finding out why, printing out why, and then continuing it.
281bbeaf6c0SSean Eric Fagan 	 * All of the grunt work is done in the support routines.
282bbeaf6c0SSean Eric Fagan 	 */
283bbeaf6c0SSean Eric Fagan 
284203098d8SMatthew N. Dodd 	clock_gettime(CLOCK_REALTIME, &trussinfo->start_time);
2850d0bd00eSMatthew N. Dodd 
286bbeaf6c0SSean Eric Fagan 	do {
2875d2d083cSXin LI 		waitevent(trussinfo);
288bbeaf6c0SSean Eric Fagan 
2895695afdeSAndrey Zonov 		switch (trussinfo->pr_why) {
290bbeaf6c0SSean Eric Fagan 		case S_SCE:
2915d2d083cSXin LI 			funcs->enter_syscall(trussinfo, MAXARGS);
2925321ae86SAlfred Perlstein 			clock_gettime(CLOCK_REALTIME,
2935695afdeSAndrey Zonov 			    &trussinfo->curthread->before);
294bbeaf6c0SSean Eric Fagan 			break;
295bbeaf6c0SSean Eric Fagan 		case S_SCX:
2965321ae86SAlfred Perlstein 			clock_gettime(CLOCK_REALTIME,
2975695afdeSAndrey Zonov 			    &trussinfo->curthread->after);
298c03bfcc8SMatthew N. Dodd 
2995d2d083cSXin LI 			if (trussinfo->curthread->in_fork &&
3005321ae86SAlfred Perlstein 			    (trussinfo->flags & FOLLOWFORKS)) {
3015d2d083cSXin LI 				trussinfo->curthread->in_fork = 0;
30294355cfdSAndrey Zonov 				childpid = funcs->exit_syscall(trussinfo,
3035d2d083cSXin LI 				    trussinfo->pr_data);
304c03bfcc8SMatthew N. Dodd 
305c03bfcc8SMatthew N. Dodd 				/*
3065321ae86SAlfred Perlstein 				 * Fork a new copy of ourself to trace
3075321ae86SAlfred Perlstein 				 * the child of the original traced
3085321ae86SAlfred Perlstein 				 * process.
309c03bfcc8SMatthew N. Dodd 				 */
310c03bfcc8SMatthew N. Dodd 				if (fork() == 0) {
311c03bfcc8SMatthew N. Dodd 					trussinfo->pid = childpid;
3125d2d083cSXin LI 					start_tracing(trussinfo->pid);
313c03bfcc8SMatthew N. Dodd 					goto START_TRACE;
314c03bfcc8SMatthew N. Dodd 				}
315c03bfcc8SMatthew N. Dodd 				break;
316c03bfcc8SMatthew N. Dodd 			}
3175d2d083cSXin LI 			funcs->exit_syscall(trussinfo, MAXARGS);
318bbeaf6c0SSean Eric Fagan 			break;
319bbeaf6c0SSean Eric Fagan 		case S_SIG:
3205d2d083cSXin LI 			if (trussinfo->flags & NOSIGS)
3215d2d083cSXin LI 				break;
322081e5c48SPav Lucistnik 			if (trussinfo->flags & FOLLOWFORKS)
323081e5c48SPav Lucistnik 				fprintf(trussinfo->outfile, "%5d: ",
324081e5c48SPav Lucistnik 				    trussinfo->pid);
325081e5c48SPav Lucistnik 			if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
3265695afdeSAndrey Zonov 				timespecsubt(&trussinfo->curthread->after,
327081e5c48SPav Lucistnik 				    &trussinfo->start_time, &timediff);
328081e5c48SPav Lucistnik 				fprintf(trussinfo->outfile, "%ld.%09ld ",
329081e5c48SPav Lucistnik 				    (long)timediff.tv_sec,
330081e5c48SPav Lucistnik 				    timediff.tv_nsec);
331081e5c48SPav Lucistnik 			}
332081e5c48SPav Lucistnik 			if (trussinfo->flags & RELATIVETIMESTAMPS) {
3335695afdeSAndrey Zonov 				timespecsubt(&trussinfo->curthread->after,
3345695afdeSAndrey Zonov 				    &trussinfo->curthread->before, &timediff);
335081e5c48SPav Lucistnik 				fprintf(trussinfo->outfile, "%ld.%09ld ",
336081e5c48SPav Lucistnik 				    (long)timediff.tv_sec,
337081e5c48SPav Lucistnik 				    timediff.tv_nsec);
338081e5c48SPav Lucistnik 			}
3395d2d083cSXin LI 			signame = strsig(trussinfo->pr_data);
3405321ae86SAlfred Perlstein 			fprintf(trussinfo->outfile,
3415d2d083cSXin LI 			    "SIGNAL %u (%s)\n", trussinfo->pr_data,
342d75300bfSAlfred Perlstein 			    signame == NULL ? "?" : signame);
343d75300bfSAlfred Perlstein 			free(signame);
344bbeaf6c0SSean Eric Fagan 			break;
345bbeaf6c0SSean Eric Fagan 		case S_EXIT:
346ee3b0f6eSDiomidis Spinellis 			if (trussinfo->flags & COUNTONLY)
347ee3b0f6eSDiomidis Spinellis 				break;
348081e5c48SPav Lucistnik 			if (trussinfo->flags & FOLLOWFORKS)
349081e5c48SPav Lucistnik 				fprintf(trussinfo->outfile, "%5d: ",
350081e5c48SPav Lucistnik 				    trussinfo->pid);
351081e5c48SPav Lucistnik 			if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
3525695afdeSAndrey Zonov 				timespecsubt(&trussinfo->curthread->after,
353081e5c48SPav Lucistnik 				    &trussinfo->start_time, &timediff);
354081e5c48SPav Lucistnik 				fprintf(trussinfo->outfile, "%ld.%09ld ",
355081e5c48SPav Lucistnik 				    (long)timediff.tv_sec,
356081e5c48SPav Lucistnik 				    timediff.tv_nsec);
357081e5c48SPav Lucistnik 			}
358081e5c48SPav Lucistnik 			if (trussinfo->flags & RELATIVETIMESTAMPS) {
3595695afdeSAndrey Zonov 				timespecsubt(&trussinfo->curthread->after,
3605695afdeSAndrey Zonov 				    &trussinfo->curthread->before, &timediff);
361081e5c48SPav Lucistnik 				fprintf(trussinfo->outfile, "%ld.%09ld ",
362081e5c48SPav Lucistnik 				    (long)timediff.tv_sec, timediff.tv_nsec);
363081e5c48SPav Lucistnik 			}
3645321ae86SAlfred Perlstein 			fprintf(trussinfo->outfile,
3655d2d083cSXin LI 			    "process exit, rval = %u\n", trussinfo->pr_data);
366bbeaf6c0SSean Eric Fagan 			break;
367bbeaf6c0SSean Eric Fagan 		default:
368bbeaf6c0SSean Eric Fagan 			break;
369bbeaf6c0SSean Eric Fagan 		}
370896fc463SAndrey Zonov 	} while (trussinfo->pr_why != S_EXIT &&
371896fc463SAndrey Zonov 	    trussinfo->pr_why != S_DETACHED);
3725d2d083cSXin LI 
37394355cfdSAndrey Zonov 	if (trussinfo->flags & FOLLOWFORKS) {
374d5303c80SXin LI 		do {
375d5303c80SXin LI 			childpid = wait(&status);
376d5303c80SXin LI 		} while (childpid != -1);
37794355cfdSAndrey Zonov 	}
378d5303c80SXin LI 
379ee3b0f6eSDiomidis Spinellis 	if (trussinfo->flags & COUNTONLY)
380ee3b0f6eSDiomidis Spinellis 		print_summary(trussinfo);
381ee3b0f6eSDiomidis Spinellis 
382ee3b0f6eSDiomidis Spinellis 	fflush(trussinfo->outfile);
383ee3b0f6eSDiomidis Spinellis 
3845321ae86SAlfred Perlstein 	return (0);
385bbeaf6c0SSean Eric Fagan }
386