-ff output coherency fixes.
[staptrace.git] / src / staptrace.c
1 /* Copyright (C) 2010 Free Software Foundation, Inc.
2
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 3 of the License, or
6    (at your option) any later version.
7
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12
13    You should have received a copy of the GNU General Public License
14    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
15
16 #include <error.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <libintl.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <signal.h>
24 #include <stdarg.h>
25 #include <fcntl.h>
26 #include <poll.h>
27 #include <assert.h>
28
29 #ifndef STAP_SCRIPT_FILENAME
30 #error "STAP_SCRIPT_FILENAME is required"
31 #endif
32
33 #define STRINGIFY1(lit) #lit
34 #define STRINGIFY(lit) STRINGIFY1 (lit)
35 #define _(x) gettext (x)
36
37 static int opt_d, opt_f, opt_q, opt_p;
38 /* Verified we should run the -ff postprocessor.  */
39 static int opt_ff;
40 static char *opt_o;
41 static size_t opt_o_len;
42
43 /* Script filename.  */
44 static char *opt_Z = STRINGIFY (STAP_SCRIPT_FILENAME);
45
46 static void *
47 xmalloc (size_t size)
48 {
49   void *retval = malloc (size);
50
51   if (retval == NULL)
52     error (EXIT_FAILURE, errno, _("memory allocation of %zu bytes"), size);
53
54   return retval;
55 }
56
57 /* warning: EOF is also an error.  */
58 static size_t
59 xread (int fd, void *buf, size_t count)
60 {
61   ssize_t retval = read (fd, buf, count);
62
63   if (retval <= 0)
64     error (EXIT_FAILURE, errno, _("Error reading data"));
65
66   return retval;
67 }
68
69 /* warning: No events are also an error.  */
70 static void
71 xpoll (struct pollfd *fds, nfds_t nfds, int timeout)
72 {
73   if (poll (fds, nfds, timeout) != 1)
74     error (EXIT_FAILURE, errno, _("poll returned error"));
75 }
76
77 static pid_t
78 xfork (void)
79 {
80   pid_t retval = fork ();
81
82   if (retval == -1)
83     error (EXIT_FAILURE, errno, _("Error calling fork"));
84
85   return retval;
86 }
87
88 static void
89 xsnprintf (char *str, size_t size, const char *format, ...)
90 {
91   int i;
92   va_list ap;
93
94   va_start (ap, format);
95   i = vsnprintf (str, size, format, ap);
96   va_end (ap);
97   if (i <= 0 || strlen (str) >= size - 1)
98     error (EXIT_FAILURE, errno, _("Error calling sprintf"));
99 }
100
101 static void
102 xfflush (FILE *stream)
103 {
104   if (fflush (stream) != 0)
105     error (EXIT_FAILURE, errno, _("Error calling fflush"));
106 }
107
108 static void
109 xdup2 (int oldfd, int newfd)
110 {
111   if (dup2 (oldfd, newfd) != newfd)
112     error (EXIT_FAILURE, errno, _("Error calling dup2 (%d, %d)"), oldfd,
113            newfd);
114 }
115
116 static void
117 xclose (int fd)
118 {
119   if (close (fd) != 0)
120     error (EXIT_FAILURE, errno, _("Error calling close (%d)"), fd);
121 }
122
123 static void
124 xpipe (int pipefd[2])
125 {
126   if (pipe (pipefd) != 0)
127     error (EXIT_FAILURE, errno, _("Error creating internal pipe"));
128 }
129
130 static void
131 xraise (int sig)
132 {
133   if (raise (sig) != 0)
134     error (EXIT_FAILURE, errno, _("Error calling raise (%d)"), sig);
135 }
136
137 static void
138 xfcntl_setfl (int fd, int cmdarg)
139 {
140   if (fcntl (fd, F_SETFL, cmdarg) != 0)
141     error (EXIT_FAILURE, errno,
142            _("Error calling fcntl (%d, F_SETFL, 0%o)"), fd, cmdarg);
143 }
144
145 static void
146 xwrite (int fd, const void *buf, size_t count)
147 {
148   if (!count)
149     return;
150
151   if ((ssize_t) count < 0 || write (fd, buf, count) != (ssize_t) count)
152     error (EXIT_FAILURE, errno,
153            _("Error calling write (%d, ..., %zu)"), fd, count);
154 }
155
156 static int
157 xopen (const char *pathname, int flags, mode_t mode)
158 {
159   int retval = open (pathname, flags, mode);
160
161   if (retval == -1)
162     error (EXIT_FAILURE, errno,
163            _("Error %s file \"%s\" (flags 0x%x, mode 0%o)"),
164            flags & O_APPEND ? _("appending")
165                             : (flags & O_CREAT ? _("creating")
166                                                : _("opening")),
167            pathname, flags, mode);
168
169   return retval;
170 }
171
172 static void
173 usage (int status)
174 {
175   printf ("\
176 usage: staptrace [-dfq] [-o file] [-p pid] [command [arg ...]]\n\
177                  [-Z <stap script>]\n\
178 \n\
179 Default -Z is: %s\n\
180 ",
181           opt_Z);
182   exit (status);
183 }
184
185 static void
186 dump_args (char **args)
187 {
188   char **argp;
189
190   for (argp = args; *argp; argp++)
191     {
192       if (argp > args)
193         fputc (' ', stderr);
194       fputs (*argp, stderr);
195     }
196   fputc ('\n', stderr);
197 }
198
199 static void
200 ff_filter (int fd_read)
201 {
202   char buf[getpagesize ()];
203   size_t buf_len = 0;
204   /* Does the buf start on a line boundary?  */
205   int buf_nl = 1;
206   /* Initial fd if no `\n[' is seen at the start.  */
207   int fd_write = STDOUT_FILENO;
208
209   xfcntl_setfl (fd_read, O_NONBLOCK);
210
211   for (;;)
212     {
213       struct pollfd pollfd;
214
215       pollfd.fd = fd_read;
216       pollfd.events = POLLIN;
217
218       xpoll (&pollfd, 1, -1);
219
220       if (pollfd.revents & ~(POLLIN | POLLHUP) || pollfd.revents == 0)
221         error (EXIT_FAILURE, errno, _("poll returned error revents 0x%x"),
222                pollfd.revents);
223
224       if (pollfd.revents & POLLIN)
225         {
226           ssize_t want, got;
227           char *s;
228           char *start;
229           int start_nl;
230
231           want = sizeof (buf) - buf_len - 1;
232           assert (want > 0);
233           got = xread (fd_read, &buf[buf_len], want);
234           if (memchr (&buf[buf_len], 0, got) != NULL)
235             error (EXIT_FAILURE, 0,
236                    _("Unexpected \\0 in the internal -ff stream"));
237           buf_len += got;
238           buf[buf_len] = 0;
239           if (opt_d >= 2)
240             fprintf (stderr, "buf_len %zu + %zd = %zu, read: {%s}\n",
241                      buf_len - got, got, buf_len, &buf[buf_len - got]);
242           start = buf;
243           start_nl = buf_nl;
244
245           /* Keep the last character for `c' or `n' line continuations.  */
246           while (start + 1 < &buf[buf_len])
247             {
248               unsigned long ul;
249               char *end;
250               static char fname[FILENAME_MAX];
251
252               if (!start_nl)
253                 {
254                   ssize_t len;
255
256                   s = strchr (start, '\n');
257                   if (!s)
258                     {
259                       len = strlen (start);
260                       assert (len > 0);
261                       if (start[len - 1] == 'c' || start[len - 1] == 'n')
262                         len--;
263                       xwrite (fd_write, start, len);
264                       start += len;
265                       continue;
266                     }
267                   len = s - start - 1;
268                   assert (len >= 0);
269                   if (start[len] == 'n')
270                     start[len++] = '\n';
271                   xwrite (fd_write, start, len);
272                   start = s + 1;
273                   start_nl = 1;
274                   continue;
275                 }
276
277               if (*start != '[')
278                 error (EXIT_FAILURE, 0,
279                       _("'[' (at 0x%lx) expected in the internal -ff stream"),
280                        (unsigned long) (start - buf));
281               if (start[1] == 0)
282                 {
283                   /* We need more data.  */
284                   break;
285                 }
286               ul = strtoul (&start[1], &end, 10);
287               assert (end != NULL);
288               if (*end == 0)
289                 {
290                   /* We need more data.  */
291                   break;
292                 }
293               if (end > start + 64 || *end != ']' || end == &start[1])
294                 error (EXIT_FAILURE, 0,
295                        _("Invalid TID in the internal -ff stream: %s"), start);
296               start = end + 1;
297               start_nl = 0;
298
299               xsnprintf (fname, sizeof (fname), "%s.%lu", opt_o, ul);
300               if (fd_write != STDOUT_FILENO)
301                 xclose (fd_write);
302               fd_write = xopen (fname, O_WRONLY | O_APPEND | O_CREAT, 0644);
303
304               continue;
305             }
306
307           want = &buf[buf_len] - start;
308           memmove (buf, start, want);
309           buf_len = want;
310           buf_nl = start_nl;
311           continue;
312         }
313       if (pollfd.revents & POLLHUP)
314         break;
315       assert (0);
316     }
317   if (fd_write != STDOUT_FILENO)
318     xclose (fd_write);
319 }
320
321 int
322 main (int argc, char **argv)
323 {
324   char **args, **argp;
325
326   args = xmalloc (sizeof (*args) * argc * 2 + 32);
327   argp = args;
328   *argp++ = "stap";
329
330   for (;;)
331     {
332       int i;
333
334       i = getopt (argc, argv, "+dfqo:p:hZ:");
335       if (i == EOF)
336         break;
337       switch (i)
338         {
339         case 'd':
340           opt_d++;
341           break;
342         case 'f':
343           opt_f++;
344           break;
345         case 'q':
346           *argp++ = "-G";
347           *argp++ = "opt_q=1";
348           opt_q = 1;
349           break;
350         case 'o':
351           opt_o = optarg;
352           opt_o_len = strlen (opt_o);
353           break;
354         case 'p':
355           *argp++ = "-x";
356           *argp++ = optarg;
357           opt_p = 1;
358           break;
359         case 'Z':
360           opt_Z = optarg;
361           break;
362         case 'h':
363           usage (EXIT_SUCCESS);
364         default:
365           usage (EXIT_FAILURE);
366         }
367     }
368
369   if ((optind == argc) == !opt_p)
370     usage (EXIT_FAILURE);
371
372   if (optind < argc)
373     {
374       pid_t child;
375       static char kill_s[256];
376       static char option_s[256];
377
378       child = xfork ();
379       if (!child)
380         {
381           xraise (SIGSTOP);
382
383           execvp (argv[optind], &argv[optind]);
384           error (EXIT_FAILURE, errno, _("Error executing child command"));
385         }
386
387       /* FIXME: Call `kill -CONT' from the systamtap script.  How?  */
388       xsnprintf (kill_s, sizeof (kill_s),
389                  "kill -CONT %lu; "
390                  "while [ -e /proc/%lu/root ]; do sleep 0.1; done",
391                  (unsigned long) child, (unsigned long) child);
392       *argp++ = "-c";
393       *argp++ = kill_s;
394       xsnprintf (option_s, sizeof (option_s), "opt_child=%lu",
395                  (unsigned long) child);
396       *argp++ = "-G";
397       *argp++ = option_s;
398     }
399
400   xfflush (stdout);
401   xdup2 (STDERR_FILENO, STDOUT_FILENO);
402
403   switch (opt_f)
404     {
405     case 1:
406       *argp++ = "-G";
407       *argp++ = "opt_f=1";
408       /* FALLTHRU */
409     case 0:
410       if (opt_o)
411         {
412           *argp++ = "-o";
413           *argp++ = opt_o;
414         }
415       break;
416     default:
417       *argp++ = "-G";
418       *argp++ = "opt_f=1";
419       if (opt_o)
420         {
421           *argp++ = "-G";
422           *argp++ = "opt_ff=1";
423           opt_ff = 1;
424         }
425       else
426         {
427           /* -ff is the same like -f when no -o is specified.  */
428         }
429       break;
430     }
431
432   *argp++ = "-g";
433   *argp++ = opt_Z;
434   *argp = NULL;
435
436   if (opt_d)
437     dump_args (args);
438
439   if (opt_ff)
440     {
441       int pipefd[2];
442
443       xpipe (pipefd);
444
445       if (xfork ())
446         {
447           xclose (pipefd[1]);
448           ff_filter (pipefd[0]);
449           exit (EXIT_SUCCESS);
450         }
451
452       xclose (pipefd[0]);
453       xdup2 (pipefd[1], STDOUT_FILENO);
454       xclose (pipefd[1]);
455     }
456
457   execvp ("stap", args);
458
459   if (!opt_d)
460     dump_args (args);
461   error (EXIT_FAILURE, errno, _("Error executing stap"));
462 }