Fix tracking of unknown syscalls for 'truss -c'.
[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 <time.h>
48 #include <unistd.h>
49
50 #include "truss.h"
51 #include "extern.h"
52 #include "syscall.h"
53
54 static void
55 usage(void)
56 {
57         fprintf(stderr, "%s\n%s\n",
58             "usage: truss [-cfaedDS] [-o file] [-s strsize] -p pid",
59             "       truss [-cfaedDS] [-o file] [-s strsize] command [args]");
60         exit(1);
61 }
62
63 char *
64 strsig(int sig)
65 {
66         static char tmp[64];
67
68         if (sig > 0 && sig < NSIG) {
69                 snprintf(tmp, sizeof(tmp), "SIG%s", sys_signame[sig]);
70                 return (tmp);
71         }
72         return (NULL);
73 }
74
75 int
76 main(int ac, char **av)
77 {
78         struct sigaction sa;
79         struct trussinfo *trussinfo;
80         char *fname;
81         char **command;
82         pid_t pid;
83         int c;
84
85         fname = NULL;
86
87         /* Initialize the trussinfo struct */
88         trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo));
89         if (trussinfo == NULL)
90                 errx(1, "calloc() failed");
91
92         pid = 0;
93         trussinfo->outfile = stderr;
94         trussinfo->strsize = 32;
95         trussinfo->curthread = NULL;
96         LIST_INIT(&trussinfo->proclist);
97         init_syscalls();
98         while ((c = getopt(ac, av, "p:o:facedDs:S")) != -1) {
99                 switch (c) {
100                 case 'p':       /* specified pid */
101                         pid = atoi(optarg);
102                         /* make sure i don't trace me */
103                         if (pid == getpid()) {
104                                 errx(2, "attempt to grab self.");
105                         }
106                         break;
107                 case 'f': /* Follow fork()'s */
108                         trussinfo->flags |= FOLLOWFORKS;
109                         break;
110                 case 'a': /* Print execve() argument strings. */
111                         trussinfo->flags |= EXECVEARGS;
112                         break;
113                 case 'c': /* Count number of system calls and time. */
114                         trussinfo->flags |= COUNTONLY;
115                         break;
116                 case 'e': /* Print execve() environment strings. */
117                         trussinfo->flags |= EXECVEENVS;
118                         break;
119                 case 'd': /* Absolute timestamps */
120                         trussinfo->flags |= ABSOLUTETIMESTAMPS;
121                         break;
122                 case 'D': /* Relative timestamps */
123                         trussinfo->flags |= RELATIVETIMESTAMPS;
124                         break;
125                 case 'o':       /* Specified output file */
126                         fname = optarg;
127                         break;
128                 case 's':       /* Specified string size */
129                         trussinfo->strsize = atoi(optarg);
130                         break;
131                 case 'S':       /* Don't trace signals */
132                         trussinfo->flags |= NOSIGS;
133                         break;
134                 default:
135                         usage();
136                 }
137         }
138
139         ac -= optind; av += optind;
140         if ((pid == 0 && ac == 0) ||
141             (pid != 0 && ac != 0))
142                 usage();
143
144         if (fname != NULL) { /* Use output file */
145                 /*
146                  * Set close-on-exec ('e'), so that the output file is not
147                  * shared with the traced process.
148                  */
149                 if ((trussinfo->outfile = fopen(fname, "we")) == NULL)
150                         err(1, "cannot open %s", fname);
151         }
152
153         /*
154          * If truss starts the process itself, it will ignore some signals --
155          * they should be passed off to the process, which may or may not
156          * exit.  If, however, we are examining an already-running process,
157          * then we restore the event mask on these same signals.
158          */
159         if (pid == 0) {
160                 /* Start a command ourselves */
161                 command = av;
162                 setup_and_wait(trussinfo, command);
163                 signal(SIGINT, SIG_IGN);
164                 signal(SIGTERM, SIG_IGN);
165                 signal(SIGQUIT, SIG_IGN);
166         } else {
167                 sa.sa_handler = restore_proc;
168                 sa.sa_flags = 0;
169                 sigemptyset(&sa.sa_mask);
170                 sigaction(SIGINT, &sa, NULL);
171                 sigaction(SIGQUIT, &sa, NULL);
172                 sigaction(SIGTERM, &sa, NULL);
173                 start_tracing(trussinfo, pid);
174         }
175
176         /*
177          * At this point, if we started the process, it is stopped waiting to
178          * be woken up, either in exit() or in execve().
179          */
180         if (LIST_FIRST(&trussinfo->proclist)->abi == NULL) {
181                 /*
182                  * If we are not able to handle this ABI, detach from the
183                  * process and exit.  If we just created a new process to
184                  * run a command, kill the new process rather than letting
185                  * it run untraced.
186                  */
187                 if (pid == 0)
188                         kill(LIST_FIRST(&trussinfo->proclist)->pid, SIGKILL);
189                 ptrace(PT_DETACH, LIST_FIRST(&trussinfo->proclist)->pid, NULL,
190                     0);
191                 return (1);
192         }
193         ptrace(PT_SYSCALL, LIST_FIRST(&trussinfo->proclist)->pid, (caddr_t)1,
194             0);
195
196         /*
197          * At this point, it's a simple loop, waiting for the process to
198          * stop, finding out why, printing out why, and then continuing it.
199          * All of the grunt work is done in the support routines.
200          */
201         clock_gettime(CLOCK_REALTIME, &trussinfo->start_time);
202
203         eventloop(trussinfo);
204
205         if (trussinfo->flags & COUNTONLY)
206                 print_summary(trussinfo);
207
208         fflush(trussinfo->outfile);
209
210         return (0);
211 }