]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/truss/main.c
MFC r308250:
[FreeBSD/FreeBSD.git] / usr.bin / truss / main.c
1 /*-
2  * Copyright 1997 Sean Eric Fagan
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 3. All advertising materials mentioning features or use of this software
13  *    must display the following acknowledgement:
14  *      This product includes software developed by Sean Eric Fagan
15  * 4. Neither the name of the author may be used to endorse or promote
16  *    products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 /*
36  * The main module for truss.  Surprisingly simple, but, then, the other
37  * files handle the bulk of the work.  And, of course, the kernel has to
38  * do a lot of the work :).
39  */
40
41 #include <sys/ptrace.h>
42
43 #include <err.h>
44 #include <signal.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <sysdecode.h>
48 #include <time.h>
49 #include <unistd.h>
50
51 #include "truss.h"
52 #include "extern.h"
53 #include "syscall.h"
54
55 static void
56 usage(void)
57 {
58         fprintf(stderr, "%s\n%s\n",
59             "usage: truss [-cfaedDS] [-o file] [-s strsize] -p pid",
60             "       truss [-cfaedDS] [-o file] [-s strsize] command [args]");
61         exit(1);
62 }
63
64 char *
65 strsig(int sig)
66 {
67         static char tmp[64];
68
69         if (sig > 0 && sig < NSIG) {
70                 snprintf(tmp, sizeof(tmp), "SIG%s", sys_signame[sig]);
71                 return (tmp);
72         }
73         return (NULL);
74 }
75
76 int
77 main(int ac, char **av)
78 {
79         struct sigaction sa;
80         struct trussinfo *trussinfo;
81         char *fname;
82         char **command;
83         pid_t pid;
84         int c;
85
86         fname = NULL;
87
88         /* Initialize the trussinfo struct */
89         trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo));
90         if (trussinfo == NULL)
91                 errx(1, "calloc() failed");
92
93         pid = 0;
94         trussinfo->outfile = stderr;
95         trussinfo->strsize = 32;
96         trussinfo->curthread = NULL;
97         LIST_INIT(&trussinfo->proclist);
98         init_syscalls();
99         while ((c = getopt(ac, av, "p:o:facedDs:SH")) != -1) {
100                 switch (c) {
101                 case 'p':       /* specified pid */
102                         pid = atoi(optarg);
103                         /* make sure i don't trace me */
104                         if (pid == getpid()) {
105                                 errx(2, "attempt to grab self.");
106                         }
107                         break;
108                 case 'f': /* Follow fork()'s */
109                         trussinfo->flags |= FOLLOWFORKS;
110                         break;
111                 case 'a': /* Print execve() argument strings. */
112                         trussinfo->flags |= EXECVEARGS;
113                         break;
114                 case 'c': /* Count number of system calls and time. */
115                         trussinfo->flags |= (COUNTONLY | NOSIGS);
116                         break;
117                 case 'e': /* Print execve() environment strings. */
118                         trussinfo->flags |= EXECVEENVS;
119                         break;
120                 case 'd': /* Absolute timestamps */
121                         trussinfo->flags |= ABSOLUTETIMESTAMPS;
122                         break;
123                 case 'D': /* Relative timestamps */
124                         trussinfo->flags |= RELATIVETIMESTAMPS;
125                         break;
126                 case 'o':       /* Specified output file */
127                         fname = optarg;
128                         break;
129                 case 's':       /* Specified string size */
130                         trussinfo->strsize = atoi(optarg);
131                         break;
132                 case 'S':       /* Don't trace signals */
133                         trussinfo->flags |= NOSIGS;
134                         break;
135                 case 'H':
136                         trussinfo->flags |= DISPLAYTIDS;
137                         break;
138                 default:
139                         usage();
140                 }
141         }
142
143         ac -= optind; av += optind;
144         if ((pid == 0 && ac == 0) ||
145             (pid != 0 && ac != 0))
146                 usage();
147
148         if (fname != NULL) { /* Use output file */
149                 /*
150                  * Set close-on-exec ('e'), so that the output file is not
151                  * shared with the traced process.
152                  */
153                 if ((trussinfo->outfile = fopen(fname, "we")) == NULL)
154                         err(1, "cannot open %s", fname);
155         }
156
157         /*
158          * If truss starts the process itself, it will ignore some signals --
159          * they should be passed off to the process, which may or may not
160          * exit.  If, however, we are examining an already-running process,
161          * then we restore the event mask on these same signals.
162          */
163         if (pid == 0) {
164                 /* Start a command ourselves */
165                 command = av;
166                 setup_and_wait(trussinfo, command);
167                 signal(SIGINT, SIG_IGN);
168                 signal(SIGTERM, SIG_IGN);
169                 signal(SIGQUIT, SIG_IGN);
170         } else {
171                 sa.sa_handler = restore_proc;
172                 sa.sa_flags = 0;
173                 sigemptyset(&sa.sa_mask);
174                 sigaction(SIGINT, &sa, NULL);
175                 sigaction(SIGQUIT, &sa, NULL);
176                 sigaction(SIGTERM, &sa, NULL);
177                 start_tracing(trussinfo, pid);
178         }
179
180         /*
181          * At this point, if we started the process, it is stopped waiting to
182          * be woken up, either in exit() or in execve().
183          */
184         if (LIST_FIRST(&trussinfo->proclist)->abi == NULL) {
185                 /*
186                  * If we are not able to handle this ABI, detach from the
187                  * process and exit.  If we just created a new process to
188                  * run a command, kill the new process rather than letting
189                  * it run untraced.
190                  */
191                 if (pid == 0)
192                         kill(LIST_FIRST(&trussinfo->proclist)->pid, SIGKILL);
193                 ptrace(PT_DETACH, LIST_FIRST(&trussinfo->proclist)->pid, NULL,
194                     0);
195                 return (1);
196         }
197         ptrace(PT_SYSCALL, LIST_FIRST(&trussinfo->proclist)->pid, (caddr_t)1,
198             0);
199
200         /*
201          * At this point, it's a simple loop, waiting for the process to
202          * stop, finding out why, printing out why, and then continuing it.
203          * All of the grunt work is done in the support routines.
204          */
205         clock_gettime(CLOCK_REALTIME, &trussinfo->start_time);
206
207         eventloop(trussinfo);
208
209         if (trussinfo->flags & COUNTONLY)
210                 print_summary(trussinfo);
211
212         fflush(trussinfo->outfile);
213
214         return (0);
215 }