xref: /freebsd/usr.bin/truss/main.c (revision bbeaf6c0c988a72995af5a9283488e02546bebbd)
1bbeaf6c0SSean Eric Fagan /*
2bbeaf6c0SSean Eric Fagan  * The main module for truss.  Suprisingly simple, but, then, the other
3bbeaf6c0SSean Eric Fagan  * files handle the bulk of the work.  And, of course, the kernel has to
4bbeaf6c0SSean Eric Fagan  * do a lot of the work :).
5bbeaf6c0SSean Eric Fagan  */
6bbeaf6c0SSean Eric Fagan /*
7bbeaf6c0SSean Eric Fagan  * $Id$
8bbeaf6c0SSean Eric Fagan  */
9bbeaf6c0SSean Eric Fagan 
10bbeaf6c0SSean Eric Fagan #include <stdio.h>
11bbeaf6c0SSean Eric Fagan #include <stdlib.h>
12bbeaf6c0SSean Eric Fagan #include <string.h>
13bbeaf6c0SSean Eric Fagan #include <errno.h>
14bbeaf6c0SSean Eric Fagan #include <err.h>
15bbeaf6c0SSean Eric Fagan #include <signal.h>
16bbeaf6c0SSean Eric Fagan #include <fcntl.h>
17bbeaf6c0SSean Eric Fagan #include <sys/ioctl.h>
18bbeaf6c0SSean Eric Fagan #include <sys/pioctl.h>
19bbeaf6c0SSean Eric Fagan 
20bbeaf6c0SSean Eric Fagan extern int setup_and_wait(char **);
21bbeaf6c0SSean Eric Fagan extern int start_tracing(int, int);
22bbeaf6c0SSean Eric Fagan extern void i386_syscall_entry(int, int);
23bbeaf6c0SSean Eric Fagan extern void i386_syscall_exit(int, int);
24bbeaf6c0SSean Eric Fagan extern void i386_linux_syscall_entry(int, int);
25bbeaf6c0SSean Eric Fagan extern void i386_linux_syscall_exit(int, int);
26bbeaf6c0SSean Eric Fagan 
27bbeaf6c0SSean Eric Fagan /*
28bbeaf6c0SSean Eric Fagan  * These should really be parameterized -- I don't like having globals,
29bbeaf6c0SSean Eric Fagan  * but this is the easiest way, right now, to deal with them.
30bbeaf6c0SSean Eric Fagan  */
31bbeaf6c0SSean Eric Fagan 
32bbeaf6c0SSean Eric Fagan int pid = 0;
33bbeaf6c0SSean Eric Fagan int nosigs = 0;
34bbeaf6c0SSean Eric Fagan FILE *outfile = stderr;
35bbeaf6c0SSean Eric Fagan char *prog;
36bbeaf6c0SSean Eric Fagan int Procfd;
37bbeaf6c0SSean Eric Fagan char progtype[50];	/* OS and type of executable */
38bbeaf6c0SSean Eric Fagan 
39bbeaf6c0SSean Eric Fagan static inline void
40bbeaf6c0SSean Eric Fagan usage(void) {
41bbeaf6c0SSean Eric Fagan   fprintf(stderr, "usage:  %s [-o <file>] [-S] { [-p <pid> ] | "
42bbeaf6c0SSean Eric Fagan 	  "[ <command> <args>] }\n", prog);
43bbeaf6c0SSean Eric Fagan   exit(1);
44bbeaf6c0SSean Eric Fagan }
45bbeaf6c0SSean Eric Fagan 
46bbeaf6c0SSean Eric Fagan struct ex_types {
47bbeaf6c0SSean Eric Fagan   char *type;
48bbeaf6c0SSean Eric Fagan   void (*enter_syscall)(int, int);
49bbeaf6c0SSean Eric Fagan   void (*exit_syscall)(int, int);
50bbeaf6c0SSean Eric Fagan } ex_types[] = {
51bbeaf6c0SSean Eric Fagan   { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit },
52bbeaf6c0SSean Eric Fagan   { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit },
53bbeaf6c0SSean Eric Fagan   { 0, 0, 0 },
54bbeaf6c0SSean Eric Fagan };
55bbeaf6c0SSean Eric Fagan 
56bbeaf6c0SSean Eric Fagan /*
57bbeaf6c0SSean Eric Fagan  * Set the execution type.  This is called after every exec, and when
58bbeaf6c0SSean Eric Fagan  * a process is first monitored.  The procfs pseudo-file "etype" has
59bbeaf6c0SSean Eric Fagan  * the execution module type -- see /proc/curproc/etype for an example.
60bbeaf6c0SSean Eric Fagan  */
61bbeaf6c0SSean Eric Fagan 
62bbeaf6c0SSean Eric Fagan static struct ex_types *
63bbeaf6c0SSean Eric Fagan set_etype() {
64bbeaf6c0SSean Eric Fagan   struct ex_types *funcs;
65bbeaf6c0SSean Eric Fagan   char etype[24];
66bbeaf6c0SSean Eric Fagan   char progtype[32];
67bbeaf6c0SSean Eric Fagan   int fd;
68bbeaf6c0SSean Eric Fagan 
69bbeaf6c0SSean Eric Fagan   sprintf(etype, "/proc/%d/etype", pid);
70bbeaf6c0SSean Eric Fagan   if ((fd = open(etype, O_RDONLY)) == -1) {
71bbeaf6c0SSean Eric Fagan     strcpy(progtype, "FreeBSD a.out");
72bbeaf6c0SSean Eric Fagan   } else {
73bbeaf6c0SSean Eric Fagan     int len = read(fd, progtype, sizeof(progtype));
74bbeaf6c0SSean Eric Fagan     progtype[len-1] = '\0';
75bbeaf6c0SSean Eric Fagan     close(etype);
76bbeaf6c0SSean Eric Fagan   }
77bbeaf6c0SSean Eric Fagan 
78bbeaf6c0SSean Eric Fagan   for (funcs = ex_types; funcs->type; funcs++)
79bbeaf6c0SSean Eric Fagan     if (!strcmp(funcs->type, progtype))
80bbeaf6c0SSean Eric Fagan       break;
81bbeaf6c0SSean Eric Fagan 
82bbeaf6c0SSean Eric Fagan   return funcs;
83bbeaf6c0SSean Eric Fagan }
84bbeaf6c0SSean Eric Fagan 
85bbeaf6c0SSean Eric Fagan main(int ac, char **av) {
86bbeaf6c0SSean Eric Fagan   int mask;
87bbeaf6c0SSean Eric Fagan   int c;
88bbeaf6c0SSean Eric Fagan   int i;
89bbeaf6c0SSean Eric Fagan   char **command;
90bbeaf6c0SSean Eric Fagan   struct procfs_status pfs;
91bbeaf6c0SSean Eric Fagan   char etype[25];
92bbeaf6c0SSean Eric Fagan   struct ex_types *funcs;
93bbeaf6c0SSean Eric Fagan   int fd;
94bbeaf6c0SSean Eric Fagan   int in_exec = 0;
95bbeaf6c0SSean Eric Fagan 
96bbeaf6c0SSean Eric Fagan   prog = av[0];
97bbeaf6c0SSean Eric Fagan 
98bbeaf6c0SSean Eric Fagan   while ((c = getopt(ac, av, "p:o:S")) != EOF) {
99bbeaf6c0SSean Eric Fagan     switch (c) {
100bbeaf6c0SSean Eric Fagan     case 'p':	/* specified pid */
101bbeaf6c0SSean Eric Fagan       pid = atoi(optarg);
102bbeaf6c0SSean Eric Fagan       break;
103bbeaf6c0SSean Eric Fagan     case 'o':	/* Specified output file */
104bbeaf6c0SSean Eric Fagan       if ((outfile = fopen(optarg, "w")) == NULL) {
105bbeaf6c0SSean Eric Fagan 	fprintf (stderr, "%s:  cannot open %s\n", av[0], optarg);
106bbeaf6c0SSean Eric Fagan 	exit(1);
107bbeaf6c0SSean Eric Fagan       }
108bbeaf6c0SSean Eric Fagan       break;
109bbeaf6c0SSean Eric Fagan     case 'S':	/* Don't trace signals */
110bbeaf6c0SSean Eric Fagan       nosigs = 1;
111bbeaf6c0SSean Eric Fagan       break;
112bbeaf6c0SSean Eric Fagan     default:
113bbeaf6c0SSean Eric Fagan       usage();
114bbeaf6c0SSean Eric Fagan     }
115bbeaf6c0SSean Eric Fagan   }
116bbeaf6c0SSean Eric Fagan 
117bbeaf6c0SSean Eric Fagan   ac -= optind; av += optind;
118bbeaf6c0SSean Eric Fagan   if (ac && pid != 0)
119bbeaf6c0SSean Eric Fagan     usage();
120bbeaf6c0SSean Eric Fagan 
121bbeaf6c0SSean Eric Fagan   /*
122bbeaf6c0SSean Eric Fagan    * If truss starts the process itself, it will ignore some signals --
123bbeaf6c0SSean Eric Fagan    * they should be passed off to the process, which may or may not
124bbeaf6c0SSean Eric Fagan    * exit.  If, however, we are examining an already-running process,
125bbeaf6c0SSean Eric Fagan    * then we restore the event mask on these same signals.
126bbeaf6c0SSean Eric Fagan    */
127bbeaf6c0SSean Eric Fagan 
128bbeaf6c0SSean Eric Fagan   if (pid == 0) {	/* Start a command ourselves */
129bbeaf6c0SSean Eric Fagan     command = av;
130bbeaf6c0SSean Eric Fagan     pid = setup_and_wait(command);
131bbeaf6c0SSean Eric Fagan     signal(SIGINT, SIG_IGN);
132bbeaf6c0SSean Eric Fagan     signal(SIGTERM, SIG_IGN);
133bbeaf6c0SSean Eric Fagan     signal(SIGQUIT, SIG_IGN);
134bbeaf6c0SSean Eric Fagan   } else {
135bbeaf6c0SSean Eric Fagan     extern void restore_proc(int);
136bbeaf6c0SSean Eric Fagan     signal(SIGINT, restore_proc);
137bbeaf6c0SSean Eric Fagan     signal(SIGTERM, restore_proc);
138bbeaf6c0SSean Eric Fagan     signal(SIGQUIT, restore_proc);
139bbeaf6c0SSean Eric Fagan   }
140bbeaf6c0SSean Eric Fagan 
141bbeaf6c0SSean Eric Fagan 
142bbeaf6c0SSean Eric Fagan   /*
143bbeaf6c0SSean Eric Fagan    * At this point, if we started the process, it is stopped waiting to
144bbeaf6c0SSean Eric Fagan    * be woken up, either in exit() or in execve().
145bbeaf6c0SSean Eric Fagan    */
146bbeaf6c0SSean Eric Fagan 
147bbeaf6c0SSean Eric Fagan   Procfd = start_tracing(pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT |
148bbeaf6c0SSean Eric Fagan 		     (nosigs ? 0 : S_SIG));
149bbeaf6c0SSean Eric Fagan   pfs.why = 0;
150bbeaf6c0SSean Eric Fagan 
151bbeaf6c0SSean Eric Fagan   funcs = set_etype();
152bbeaf6c0SSean Eric Fagan   /*
153bbeaf6c0SSean Eric Fagan    * At this point, it's a simple loop, waiting for the process to
154bbeaf6c0SSean Eric Fagan    * stop, finding out why, printing out why, and then continuing it.
155bbeaf6c0SSean Eric Fagan    * All of the grunt work is done in the support routines.
156bbeaf6c0SSean Eric Fagan    */
157bbeaf6c0SSean Eric Fagan 
158bbeaf6c0SSean Eric Fagan   do {
159bbeaf6c0SSean Eric Fagan     int val = 0;
160bbeaf6c0SSean Eric Fagan 
161bbeaf6c0SSean Eric Fagan     if (ioctl(Procfd, PIOCWAIT, &pfs) == -1)
162bbeaf6c0SSean Eric Fagan       perror("PIOCWAIT top of loop");
163bbeaf6c0SSean Eric Fagan     else {
164bbeaf6c0SSean Eric Fagan       switch(i = pfs.why) {
165bbeaf6c0SSean Eric Fagan       case S_SCE:
166bbeaf6c0SSean Eric Fagan 	funcs->enter_syscall(pid, pfs.val);
167bbeaf6c0SSean Eric Fagan 	break;
168bbeaf6c0SSean Eric Fagan       case S_SCX:
169bbeaf6c0SSean Eric Fagan 	/*
170bbeaf6c0SSean Eric Fagan 	 * This is so we don't get two messages for an exec -- one
171bbeaf6c0SSean Eric Fagan 	 * for the S_EXEC, and one for the syscall exit.  It also,
172bbeaf6c0SSean Eric Fagan 	 * conveniently, ensures that the first message printed out
173bbeaf6c0SSean Eric Fagan 	 * isn't the return-from-syscall used to create the process.
174bbeaf6c0SSean Eric Fagan 	 */
175bbeaf6c0SSean Eric Fagan 
176bbeaf6c0SSean Eric Fagan 	if (in_exec) {
177bbeaf6c0SSean Eric Fagan 	  in_exec = 0;
178bbeaf6c0SSean Eric Fagan 	  break;
179bbeaf6c0SSean Eric Fagan 	}
180bbeaf6c0SSean Eric Fagan 	funcs->exit_syscall(pid, pfs.val);
181bbeaf6c0SSean Eric Fagan 	break;
182bbeaf6c0SSean Eric Fagan       case S_SIG:
183bbeaf6c0SSean Eric Fagan 	fprintf(outfile, "SIGNAL %d\n", pfs.val);
184bbeaf6c0SSean Eric Fagan 	break;
185bbeaf6c0SSean Eric Fagan       case S_EXIT:
186bbeaf6c0SSean Eric Fagan 	fprintf (outfile, "process exit, rval = %d\n", pfs.val);
187bbeaf6c0SSean Eric Fagan 	break;
188bbeaf6c0SSean Eric Fagan       case S_EXEC:
189bbeaf6c0SSean Eric Fagan 	funcs = set_etype();
190bbeaf6c0SSean Eric Fagan 	in_exec = 1;
191bbeaf6c0SSean Eric Fagan 	break;
192bbeaf6c0SSean Eric Fagan       default:
193bbeaf6c0SSean Eric Fagan 	fprintf (outfile, "Process stopped because of:  %d\n", i);
194bbeaf6c0SSean Eric Fagan 	break;
195bbeaf6c0SSean Eric Fagan       }
196bbeaf6c0SSean Eric Fagan     }
197bbeaf6c0SSean Eric Fagan     if (ioctl(Procfd, PIOCCONT, &val) == -1)
198bbeaf6c0SSean Eric Fagan       perror("PIOCCONT");
199bbeaf6c0SSean Eric Fagan   } while (pfs.why != S_EXIT);
200bbeaf6c0SSean Eric Fagan   return 0;
201bbeaf6c0SSean Eric Fagan }
202