OSDN Git Service

gas/opcodes: blackfin: move dsp mac func defines to common header
[pf3gnuchains/sourceware.git] / winsup / utils / strace.cc
1 /* strace.cc
2
3    Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
4    2009, 2010 Red Hat Inc.
5
6    Written by Chris Faylor <cgf@redhat.com>
7
8 This file is part of Cygwin.
9
10 This software is a copyrighted work licensed under the terms of the
11 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
12 details. */
13
14 #define cygwin_internal cygwin_internal_dontuse
15 #include <stdio.h>
16 #include <fcntl.h>
17 #include <getopt.h>
18 #include <stdarg.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <time.h>
22 #include <windows.h>
23 #include <signal.h>
24 #include <errno.h>
25 #include "cygwin/include/sys/strace.h"
26 #include "cygwin/include/sys/cygwin.h"
27 #include "path.h"
28 #undef cygwin_internal
29 #include "loadlib.h"
30
31 /* we *know* we're being built with GCC */
32 #define alloca __builtin_alloca
33
34 // Version string.
35 static const char version[] = "$Revision: 1.21 $";
36
37 static const char *pgm;
38 static int forkdebug = 1;
39 static int numerror = 1;
40 static int show_usecs = 1;
41 static int delta = 1;
42 static int hhmmss;
43 static int bufsize;
44 static int new_window;
45 static long flush_period;
46 static int include_hex;
47 static int quiet = -1;
48
49 static unsigned char strace_active = 1;
50 static int processes;
51
52 static BOOL close_handle (HANDLE h, DWORD ok);
53
54 #define CloseHandle(h) close_handle(h, 0)
55
56 struct child_list
57 {
58   DWORD id;
59   HANDLE hproc;
60   int saw_stars;
61   char nfields;
62   long long start_time;
63   DWORD last_usecs;
64   struct child_list *next;
65     child_list ():id (0), hproc (NULL), saw_stars (0), nfields (0),
66     start_time (0), last_usecs (0), next (NULL)
67   {
68   }
69 };
70
71 child_list children;
72
73 static void
74 warn (int geterrno, const char *fmt, ...)
75 {
76   va_list args;
77   char buf[4096];
78
79   va_start (args, fmt);
80   sprintf (buf, "%s: ", pgm);
81   vsprintf (strchr (buf, '\0'), fmt, args);
82   if (geterrno)
83     perror (buf);
84   else
85     {
86       fputs (buf, stderr);
87       fputs ("\n", stderr);
88     }
89 }
90
91 static void __attribute__ ((noreturn))
92 error (int geterrno, const char *fmt, ...)
93 {
94   va_list args;
95   char buf[4096];
96
97   va_start (args, fmt);
98   sprintf (buf, "%s: ", pgm);
99   vsprintf (strchr (buf, '\0'), fmt, args);
100   if (geterrno)
101     perror (buf);
102   else
103     {
104       fputs (buf, stderr);
105       fputs ("\n", stderr);
106     }
107   exit (1);
108 }
109
110 DWORD lastid = 0;
111 HANDLE lasth;
112
113 static child_list *
114 get_child (DWORD id)
115 {
116   child_list *c;
117   for (c = &children; (c = c->next) != NULL;)
118     if (c->id == id)
119       return c;
120
121   return NULL;
122 }
123
124 static void
125 add_child (DWORD id, HANDLE hproc)
126 {
127   if (!get_child (id))
128     {
129       child_list *c = children.next;
130       children.next = (child_list *) calloc (1, sizeof (child_list));
131       children.next->next = c;
132       lastid = children.next->id = id;
133       lasth = children.next->hproc = hproc;
134       processes++;
135       if (!quiet)
136         fprintf (stderr, "Windows process %lu attached\n", id);
137     }
138 }
139
140 static void
141 remove_child (DWORD id)
142 {
143   child_list *c;
144   if (id == lastid)
145     lastid = 0;
146   for (c = &children; c->next != NULL; c = c->next)
147     if (c->next->id == id)
148       {
149         child_list *c1 = c->next;
150         c->next = c1->next;
151         free (c1);
152         if (!quiet)
153           fprintf (stderr, "Windows process %lu detached\n", id);
154         processes--;
155         return;
156       }
157
158   error (0, "no process id %d found", id);
159 }
160
161 #define LINE_BUF_CHUNK 128
162
163 class linebuf
164 {
165   size_t alloc;
166 public:
167     size_t ix;
168   char *buf;
169   linebuf ()
170   {
171     ix = 0;
172     alloc = 0;
173     buf = NULL;
174   }
175  ~linebuf ()
176   {
177     if (buf)
178       free (buf);
179   }
180   void add (const char *what, int len);
181   void add (const char *what)
182   {
183     add (what, strlen (what));
184   }
185   void prepend (const char *what, int len);
186 };
187
188 void
189 linebuf::add (const char *what, int len)
190 {
191   size_t newix;
192   if ((newix = ix + len) >= alloc)
193     {
194       alloc += LINE_BUF_CHUNK + len;
195       buf = (char *) realloc (buf, alloc + 1);
196     }
197   memcpy (buf + ix, what, len);
198   ix = newix;
199   buf[ix] = '\0';
200 }
201
202 void
203 linebuf::prepend (const char *what, int len)
204 {
205   int buflen;
206   size_t newix;
207   if ((newix = ix + len) >= alloc)
208     {
209       alloc += LINE_BUF_CHUNK + len;
210       buf = (char *) realloc (buf, alloc + 1);
211       buf[ix] = '\0';
212     }
213   if ((buflen = strlen (buf)))
214     memmove (buf + len, buf, buflen + 1);
215   else
216     buf[newix] = '\0';
217   memcpy (buf, what, len);
218   ix = newix;
219 }
220
221 static void
222 make_command_line (linebuf & one_line, char **argv)
223 {
224   for (; *argv; argv++)
225     {
226       char *p = NULL;
227       const char *a = *argv;
228
229       int len = strlen (a);
230       if (len != 0 && !(p = strpbrk (a, " \t\n\r\"")))
231         one_line.add (a, len);
232       else
233         {
234           one_line.add ("\"", 1);
235           for (; p; a = p, p = strchr (p, '"'))
236             {
237               one_line.add (a, ++p - a);
238               if (p[-1] == '"')
239                 one_line.add ("\"", 1);
240             }
241           if (*a)
242             one_line.add (a);
243           one_line.add ("\"", 1);
244         }
245       one_line.add (" ", 1);
246     }
247
248   if (one_line.ix)
249     one_line.buf[one_line.ix - 1] = '\0';
250   else
251     one_line.add ("", 1);
252 }
253
254 static DWORD child_pid;
255
256 static BOOL WINAPI
257 ctrl_c (DWORD)
258 {
259   static int tic = 1;
260   if ((tic ^= 1) && !GenerateConsoleCtrlEvent (CTRL_C_EVENT, 0))
261     error (0, "couldn't send CTRL-C to child, win32 error %d\n",
262            GetLastError ());
263   return TRUE;
264 }
265
266 extern "C" {
267 unsigned long (*cygwin_internal) (int, ...);
268 WCHAR cygwin_dll_path[32768];
269 };
270
271 static int
272 load_cygwin ()
273 {
274   static HMODULE h;
275
276   if (cygwin_internal)
277     return 1;
278
279   if (h)
280     return 0;
281
282   if (!(h = LoadLibrary ("cygwin1.dll")))
283     {
284       errno = ENOENT;
285       return 0;
286     }
287   GetModuleFileNameW (h, cygwin_dll_path, 32768);
288   if (!(cygwin_internal = (DWORD (*) (int, ...)) GetProcAddress (h, "cygwin_internal")))
289     {
290       errno = ENOSYS;
291       return 0;
292     }
293   return 1;
294 }
295
296 static void
297 attach_process (pid_t pid)
298 {
299   child_pid = (DWORD) cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid);
300   if (!child_pid)
301     child_pid = pid;
302
303   if (!DebugActiveProcess (child_pid))
304     error (0, "couldn't attach to pid %d for debugging", child_pid);
305
306   return;
307 }
308
309
310 static void
311 create_child (char **argv)
312 {
313   linebuf one_line;
314
315   STARTUPINFO si;
316   PROCESS_INFORMATION pi;
317   BOOL ret;
318   DWORD flags;
319
320   if (strchr (*argv, '/'))
321       *argv = cygpath (*argv, NULL);
322   memset (&si, 0, sizeof (si));
323   si.cb = sizeof (si);
324
325   flags = CREATE_DEFAULT_ERROR_MODE
326           | (forkdebug ? DEBUG_PROCESS : DEBUG_ONLY_THIS_PROCESS);
327   if (new_window)
328     flags |= CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP;
329
330   make_command_line (one_line, argv);
331
332   SetConsoleCtrlHandler (NULL, 0);
333   const char *cygwin_env = getenv ("CYGWIN");
334   const char *space;
335   if (cygwin_env)
336     space = " ";
337   else
338     space = cygwin_env = "";
339   char *newenv = (char *) malloc (sizeof ("CYGWIN=noglob") + strlen (space) + strlen (cygwin_env));
340   sprintf (newenv, "CYGWIN=noglob%s%s", space, cygwin_env);
341   _putenv (newenv);
342   ret = CreateProcess (0, one_line.buf, /* command line */
343                        NULL,    /* Security */
344                        NULL,    /* thread */
345                        TRUE,    /* inherit handles */
346                        flags,   /* start flags */
347                        NULL,    /* default environment */
348                        NULL,    /* current directory */
349                        &si, &pi);
350   if (!ret)
351     error (0, "error creating process %s, (error %d)", *argv,
352            GetLastError ());
353
354   CloseHandle (pi.hThread);
355   CloseHandle (pi.hProcess);
356   child_pid = pi.dwProcessId;
357   SetConsoleCtrlHandler (ctrl_c, 1);
358 }
359
360 static int
361 output_winerror (FILE *ofile, char *s)
362 {
363   char *winerr = strstr (s, "Win32 error ");
364   if (!winerr)
365     return 0;
366
367   DWORD errnum = atoi (winerr + sizeof ("Win32 error ") - 1);
368   if (!errnum)
369     return 0;
370
371   /*
372    * NOTE: Currently there is no policy for how long the
373    * the buffers are, and looks like 256 is a smallest one
374    * (dlfcn.cc). Other than error 1395 (length 213) and
375    * error 1015 (length 249), the rest are all under 188
376    * characters, and so I'll use 189 as the buffer length.
377    * For those longer error messages, FormatMessage will
378    * return FALSE, and we'll get the old behaviour such as
379    * ``Win32 error 1395'' etc.
380    */
381   char buf[4096];
382   if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
383                       | FORMAT_MESSAGE_IGNORE_INSERTS,
384                       NULL,
385                       errnum,
386                       MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
387                       (LPTSTR) buf, sizeof (buf), NULL))
388     return 0;
389
390   /* Get rid the trailing CR/NL pair. */
391   char *p = strchr (buf, '\0');
392   p[-2] = '\n';
393   p[-1] = '\0';
394
395   *winerr = '\0';
396   fputs (s, ofile);
397   fputs (buf, ofile);
398   return 1;
399 }
400
401 static SYSTEMTIME *
402 syst (long long t)
403 {
404   FILETIME n;
405   static SYSTEMTIME st;
406   long long now = t /*+ ((long long) usecs * 10)*/;
407   n.dwHighDateTime = now >> 32;
408   n.dwLowDateTime = now & 0xffffffff;
409   FileTimeToSystemTime (&n, &st);
410   return &st;
411 }
412
413 static void __stdcall
414 handle_output_debug_string (DWORD id, LPVOID p, unsigned mask, FILE *ofile)
415 {
416   int len;
417   int special;
418   char alen[3 + 8 + 1];
419   DWORD nbytes;
420   child_list *child = get_child (id);
421   if (!child)
422     error (0, "no process id %d found", id);
423   HANDLE hchild = child->hproc;
424 #define INTROLEN (sizeof (alen) - 1)
425
426   if (id == lastid && hchild != lasth)
427     warn (0, "%p != %p", hchild, lasth);
428
429   alen[INTROLEN] = '\0';
430   if (!ReadProcessMemory (hchild, p, alen, INTROLEN, &nbytes))
431 #ifndef DEBUGGING
432     return;
433 #else
434     error (0,
435            "couldn't get message length from subprocess %d<%p>, windows error %d",
436            id, hchild, GetLastError ());
437 #endif
438
439   if (strncmp (alen, "cYg", 3))
440     return;
441   len = (int) strtoul (alen + 3, NULL, 16);
442   if (!len)
443     return;
444
445   if (len > 0)
446     special = 0;
447   else
448     {
449       special = len;
450       if (special == _STRACE_INTERFACE_ACTIVATE_ADDR || special == _STRACE_CHILD_PID)
451         len = 17;
452     }
453
454   char *buf;
455   buf = (char *) alloca (len + 85) + 20;
456
457   if (!ReadProcessMemory (hchild, ((char *) p) + INTROLEN, buf, len, &nbytes))
458     error (0, "couldn't get message from subprocess, windows error %d",
459            GetLastError ());
460
461   buf[len] = '\0';
462   char *s = strtok (buf, " ");
463
464   unsigned long n = strtoul (s, NULL, 16);
465
466   s = strchr (s, '\0') + 1;
467
468   if (special == _STRACE_CHILD_PID)
469     {
470       if (!DebugActiveProcess (n))
471         error (0, "couldn't attach to subprocess %d for debugging, "
472                "windows error %d", n, GetLastError ());
473       return;
474     }
475
476   if (special == _STRACE_INTERFACE_ACTIVATE_ADDR)
477     {
478       if (!WriteProcessMemory (hchild, (LPVOID) n, &strace_active,
479                                sizeof (strace_active), &nbytes))
480         error (0, "couldn't write strace flag to subprocess at %p, "
481                "windows error %d", n, GetLastError ());
482       return;
483     }
484
485   char *origs = s;
486
487   if (mask & n)
488     /* got it */ ;
489   else if (!(mask & _STRACE_ALL) || (n & _STRACE_NOTALL))
490     return;                     /* This should not be included in "all" output */
491
492   DWORD dusecs, usecs;
493   char *ptusec, *ptrest;
494
495   dusecs = strtoul (s, &ptusec, 10);
496   char *q = ptusec;
497   while (*q == ' ')
498     q++;
499   if (*q != '[')
500     {
501       usecs = strtoul (q, &ptrest, 10);
502       while (*ptrest == ' ')
503         ptrest++;
504     }
505   else
506     {
507       ptrest = q;
508       ptusec = show_usecs ? s : ptrest;
509       usecs = dusecs;
510     }
511
512   if (child->saw_stars == 0)
513     {
514       FILETIME st;
515       char *news;
516
517       GetSystemTimeAsFileTime (&st);
518       FileTimeToLocalFileTime (&st, &st);
519       child->start_time = st.dwHighDateTime;
520       child->start_time <<= 32;
521       child->start_time |= st.dwLowDateTime;
522       if (*(news = ptrest) != '[')
523         child->saw_stars = 2;
524       else
525         {
526           child->saw_stars++;
527           while ((news = strchr (news, ' ')) != NULL && *++news != '*')
528             child->nfields++;
529           if (news == NULL)
530             child->saw_stars++;
531           else
532             {
533               s = news;
534               child->nfields++;
535             }
536         }
537     }
538   else if (child->saw_stars < 2)
539     {
540       int i;
541       char *news;
542       if (*(news = ptrest) != '[')
543         child->saw_stars = 2;
544       else
545         {
546           for (i = 0; i < child->nfields; i++)
547             if ((news = strchr (news, ' ')) == NULL)
548               break;            // Should never happen
549             else
550               news++;
551
552           if (news == NULL)
553             child->saw_stars = 2;
554           else
555             {
556               s = news;
557               if (*s == '*')
558                 {
559                   SYSTEMTIME *st = syst (child->start_time);
560                   fprintf (ofile,
561                            "Date/Time:    %d-%02d-%02d %02d:%02d:%02d\n",
562                            st->wYear, st->wMonth, st->wDay, st->wHour,
563                            st->wMinute, st->wSecond);
564                   child->saw_stars++;
565                 }
566             }
567         }
568     }
569
570   long long d = usecs - child->last_usecs;
571   char intbuf[40];
572
573   if (child->saw_stars < 2 || s != origs)
574     /* Nothing */ ;
575   else if (hhmmss)
576     {
577       s = ptrest - 9;
578       SYSTEMTIME *st = syst (child->start_time + (long long) usecs * 10);
579       sprintf (s, "%02d:%02d:%02d", st->wHour, st->wMinute, st->wSecond);
580       *strchr (s, '\0') = ' ';
581     }
582   else if (!delta)
583     s = ptusec;
584   else
585     {
586       s = ptusec;
587       sprintf (intbuf, "%5d ", (int) d);
588       int len = strlen (intbuf);
589
590       memcpy ((s -= len), intbuf, len);
591     }
592
593   if (include_hex)
594     {
595       s -= 8;
596       sprintf (s, "%p", (void *) n);
597       strchr (s, '\0')[0] = ' ';
598     }
599   child->last_usecs = usecs;
600   if (numerror || !output_winerror (ofile, s))
601     fputs (s, ofile);
602   if (!bufsize)
603     fflush (ofile);
604 }
605
606 static DWORD
607 proc_child (unsigned mask, FILE *ofile, pid_t pid)
608 {
609   DWORD res = 0;
610   DEBUG_EVENT ev;
611   time_t cur_time, last_time;
612
613   SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
614   last_time = time (NULL);
615   while (1)
616     {
617       BOOL debug_event = WaitForDebugEvent (&ev, 1000);
618       DWORD status = DBG_CONTINUE;
619
620       if (bufsize && flush_period > 0 &&
621           (cur_time = time (NULL)) >= last_time + flush_period)
622         {
623           last_time = cur_time;
624           fflush (ofile);
625         }
626
627       if (!debug_event)
628         continue;
629
630       if (pid)
631         {
632           (void) cygwin_internal (CW_STRACE_TOGGLE, pid);
633           pid = 0;
634         }
635
636       switch (ev.dwDebugEventCode)
637         {
638         case CREATE_PROCESS_DEBUG_EVENT:
639           if (ev.u.CreateProcessInfo.hFile)
640             CloseHandle (ev.u.CreateProcessInfo.hFile);
641           add_child (ev.dwProcessId, ev.u.CreateProcessInfo.hProcess);
642           break;
643
644         case CREATE_THREAD_DEBUG_EVENT:
645           break;
646
647         case LOAD_DLL_DEBUG_EVENT:
648           if (ev.u.LoadDll.hFile)
649             CloseHandle (ev.u.LoadDll.hFile);
650           break;
651
652         case OUTPUT_DEBUG_STRING_EVENT:
653           handle_output_debug_string (ev.dwProcessId,
654                                       ev.u.DebugString.lpDebugStringData,
655                                       mask, ofile);
656           break;
657
658         case EXIT_PROCESS_DEBUG_EVENT:
659           res = ev.u.ExitProcess.dwExitCode >> 8;
660           remove_child (ev.dwProcessId);
661           break;
662         case EXCEPTION_DEBUG_EVENT:
663           if (ev.u.Exception.ExceptionRecord.ExceptionCode != STATUS_BREAKPOINT)
664             {
665               status = DBG_EXCEPTION_NOT_HANDLED;
666               if (ev.u.Exception.dwFirstChance)
667                 fprintf (ofile, "--- Process %lu, exception %p at %p\n", ev.dwProcessId,
668                          (void *) ev.u.Exception.ExceptionRecord.ExceptionCode,
669                          ev.u.Exception.ExceptionRecord.ExceptionAddress);
670             }
671           break;
672         }
673       if (!ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId, status))
674         error (0, "couldn't continue debug event, windows error %d",
675                GetLastError ());
676       if (!processes)
677         break;
678     }
679
680   return res;
681 }
682
683 static void
684 dotoggle (pid_t pid)
685 {
686   child_pid = (DWORD) cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid);
687   if (!child_pid)
688     {
689       warn (0, "no such cygwin pid - %d", pid);
690       child_pid = pid;
691     }
692   if (cygwin_internal (CW_STRACE_TOGGLE, child_pid))
693     error (0, "failed to toggle tracing for process %d<%d>", pid, child_pid);
694
695   return;
696 }
697
698 static DWORD
699 dostrace (unsigned mask, FILE *ofile, pid_t pid, char **argv)
700 {
701   if (!pid)
702     create_child (argv);
703   else
704     attach_process (pid);
705
706   return proc_child (mask, ofile, pid);
707 }
708
709 typedef struct tag_mask_mnemonic
710 {
711   unsigned long val;
712   const char *text;
713 }
714 mask_mnemonic;
715
716 static const mask_mnemonic mnemonic_table[] = {
717   {_STRACE_ALL, "all"},
718   {_STRACE_FLUSH, "flush"},
719   {_STRACE_INHERIT, "inherit"},
720   {_STRACE_UHOH, "uhoh"},
721   {_STRACE_SYSCALL, "syscall"},
722   {_STRACE_STARTUP, "startup"},
723   {_STRACE_DEBUG, "debug"},
724   {_STRACE_PARANOID, "paranoid"},
725   {_STRACE_TERMIOS, "termios"},
726   {_STRACE_SELECT, "select"},
727   {_STRACE_WM, "wm"},
728   {_STRACE_SIGP, "sigp"},
729   {_STRACE_MINIMAL, "minimal"},
730   {_STRACE_EXITDUMP, "exitdump"},
731   {_STRACE_SYSTEM, "system"},
732   {_STRACE_NOMUTEX, "nomutex"},
733   {_STRACE_MALLOC, "malloc"},
734   {_STRACE_THREAD, "thread"},
735   {_STRACE_PTHREAD, "pthread"},
736   {_STRACE_SPECIAL, "special"},
737   {0, NULL}
738 };
739
740 static unsigned long
741 mnemonic2ul (const char *nptr, char **endptr)
742 {
743   // Look up mnemonic in table, return value.
744   // *endptr = ptr to char that breaks match.
745   const mask_mnemonic *mnp = mnemonic_table;
746
747   while (mnp->text != NULL)
748     {
749       if (strcmp (mnp->text, nptr) == 0)
750         {
751           // Found a match.
752           if (endptr != NULL)
753             {
754               *endptr = ((char *) nptr) + strlen (mnp->text);
755             }
756           return mnp->val;
757         }
758       mnp++;
759     }
760
761   // Didn't find it.
762   if (endptr != NULL)
763     {
764       *endptr = (char *) nptr;
765     }
766   return 0;
767 }
768
769 static unsigned long
770 parse_mask (const char *ms, char **endptr)
771 {
772   const char *p = ms;
773   char *newp;
774   unsigned long retval = 0, thisval;
775   const size_t bufsize = 16;
776   char buffer[bufsize];
777   size_t len;
778
779   while (*p != '\0')
780     {
781       // First extract the term, terminate it, and lowercase it.
782       strncpy (buffer, p, bufsize);
783       buffer[bufsize - 1] = '\0';
784       len = strcspn (buffer, "+,\0");
785       buffer[len] = '\0';
786       strlwr (buffer);
787
788       // Check if this is a mnemonic.  We have to do this first or strtoul()
789       // will false-trigger on anything starting with "a" through "f".
790       thisval = mnemonic2ul (buffer, &newp);
791       if (buffer == newp)
792         {
793           // This term isn't mnemonic, check if it's hex.
794           thisval = strtoul (buffer, &newp, 16);
795           if (newp != buffer + len)
796             {
797               // Not hex either, syntax error.
798               *endptr = (char *) p;
799               return 0;
800             }
801         }
802
803       p += len;
804       retval += thisval;
805
806       // Handle operators
807       if (*p == '\0')
808         break;
809       if ((*p == '+') || (*p == ','))
810         {
811           // For now these both equate to addition/ORing.  Until we get
812           // fancy and add things like "all-<something>", all we need do is
813           // continue the looping.
814           p++;
815           continue;
816         }
817       else
818         {
819           // Syntax error
820           *endptr = (char *) p;
821           return 0;
822         }
823     }
824
825   *endptr = (char *) p;
826   return retval;
827 }
828
829 static void
830 usage (FILE *where = stderr)
831 {
832   fprintf (where, "\
833 Usage: %s [OPTIONS] <command-line>\n\
834 Usage: %s [OPTIONS] -p <pid>\n\
835 Trace system calls and signals\n\
836 \n\
837   -b, --buffer-size=SIZE       set size of output file buffer\n\
838   -d, --no-delta               don't display the delta-t microsecond timestamp\n\
839   -f, --trace-children         trace child processes (toggle - default true)\n\
840   -h, --help                   output usage information and exit\n\
841   -m, --mask=MASK              set message filter mask\n\
842   -n, --crack-error-numbers    output descriptive text instead of error\n\
843                                numbers for Windows errors\n\
844   -o, --output=FILENAME        set output file to FILENAME\n\
845   -p, --pid=n                  attach to executing program with cygwin pid n\n\
846   -q, --quiet                  suppress messages about attaching, detaching, etc.\n\
847   -S, --flush-period=PERIOD    flush buffered strace output every PERIOD secs\n\
848   -t, --timestamp              use an absolute hh:mm:ss timestamp insted of \n\
849                                the default microsecond timestamp.  Implies -d\n\
850   -T, --toggle                 toggle tracing in a process already being\n\
851                                traced. Requires -p <pid>\n\
852   -u, --usecs                  toggle printing of microseconds timestamp\n\
853   -v, --version                output version information and exit\n\
854   -w, --new-window             spawn program under test in a new window\n\
855 \n", pgm, pgm);
856   if ( where == stdout)
857     fprintf (stdout, "\
858     MASK can be any combination of the following mnemonics and/or hex values\n\
859     (0x is optional).  Combine masks with '+' or ',' like so:\n\
860 \n\
861                       --mask=wm+system,malloc+0x00800\n\
862 \n\
863     Mnemonic Hex     Corresponding Def  Description\n\
864     =========================================================================\n\
865     all      0x000001 (_STRACE_ALL)      All strace messages.\n\
866     flush    0x000002 (_STRACE_FLUSH)    Flush output buffer after each message.\n\
867     inherit  0x000004 (_STRACE_INHERIT)  Children inherit mask from parent.\n\
868     uhoh     0x000008 (_STRACE_UHOH)     Unusual or weird phenomenon.\n\
869     syscall  0x000010 (_STRACE_SYSCALL)  System calls.\n\
870     startup  0x000020 (_STRACE_STARTUP)  argc/envp printout at startup.\n\
871     debug    0x000040 (_STRACE_DEBUG)    Info to help debugging. \n\
872     paranoid 0x000080 (_STRACE_PARANOID) Paranoid info.\n\
873     termios  0x000100 (_STRACE_TERMIOS)  Info for debugging termios stuff.\n\
874     select   0x000200 (_STRACE_SELECT)   Info on ugly select internals.\n\
875     wm       0x000400 (_STRACE_WM)       Trace Windows msgs (enable _strace_wm).\n\
876     sigp     0x000800 (_STRACE_SIGP)     Trace signal and process handling.\n\
877     minimal  0x001000 (_STRACE_MINIMAL)  Very minimal strace output.\n\
878     pthread  0x002000 (_STRACE_PTHREAD) Pthread calls.\n\
879     exitdump 0x004000 (_STRACE_EXITDUMP) Dump strace cache on exit.\n\
880     system   0x008000 (_STRACE_SYSTEM)   Serious error; goes to console and log.\n\
881     nomutex  0x010000 (_STRACE_NOMUTEX)  Don't use mutex for synchronization.\n\
882     malloc   0x020000 (_STRACE_MALLOC)   Trace malloc calls.\n\
883     thread   0x040000 (_STRACE_THREAD)   Thread-locking calls.\n\
884     special  0x100000 (_STRACE_SPECIAL)  Special debugging printfs for\n\
885                                          non-checked-in code\n\
886 ");
887   if (where == stderr)
888     fprintf (stderr, "Try '%s --help' for more information.\n", pgm);
889   exit (where == stderr ? 1 : 0 );
890 }
891
892 struct option longopts[] = {
893   {"buffer-size", required_argument, NULL, 'b'},
894   {"help", no_argument, NULL, 'h'},
895   {"flush-period", required_argument, NULL, 'S'},
896   {"hex", no_argument, NULL, 'H'},
897   {"mask", required_argument, NULL, 'm'},
898   {"new-window", no_argument, NULL, 'w'},
899   {"output", required_argument, NULL, 'o'},
900   {"no-delta", no_argument, NULL, 'd'},
901   {"pid", required_argument, NULL, 'p'},
902   {"quiet", no_argument, NULL, 'q'},
903   {"timestamp", no_argument, NULL, 't'},
904   {"toggle", no_argument, NULL, 'T'},
905   {"trace-children", no_argument, NULL, 'f'},
906   {"translate-error-numbers", no_argument, NULL, 'n'},
907   {"usecs", no_argument, NULL, 'u'},
908   {"version", no_argument, NULL, 'v'},
909   {NULL, 0, NULL, 0}
910 };
911
912 static const char *const opts = "+b:dhHfm:no:p:qS:tTuvw";
913
914 static void
915 print_version ()
916 {
917   const char *v = strchr (version, ':');
918   int len;
919   if (!v)
920     {
921       v = "?";
922       len = 1;
923     }
924   else
925     {
926       v += 2;
927       len = strchr (v, ' ') - v;
928     }
929   printf ("\
930 %s (cygwin) %.*s\n\
931 System Trace\n\
932 Copyright 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.\n\
933 Compiled on %s\n\
934 ", pgm, len, v, __DATE__);
935 }
936
937 int
938 main (int argc, char **argv)
939 {
940   unsigned mask = 0;
941   FILE *ofile = NULL;
942   pid_t pid = 0;
943   int opt;
944   int toggle = 0;
945   int sawquiet = -1;
946
947   if (load_cygwin ())
948     {
949       char **av = (char **) cygwin_internal (CW_ARGV);
950       if (av && (DWORD) av != (DWORD) -1)
951         for (argc = 0, argv = av; *av; av++)
952           argc++;
953     }
954
955   if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/')))
956     pgm = *argv;
957   else
958     pgm++;
959
960   while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
961     switch (opt)
962       {
963       case 'b':
964         bufsize = atoi (optarg);
965         break;
966       case 'd':
967         delta ^= 1;
968         break;
969       case 'f':
970         forkdebug ^= 1;
971         break;
972       case 'h':
973         // Print help and exit
974         usage (stdout);
975         break;
976       case 'H':
977         include_hex ^= 1;
978         break;
979       case 'm':
980         {
981           char *endptr;
982           mask = parse_mask (optarg, &endptr);
983           if (*endptr != '\0')
984             {
985               // Bad mask expression.
986               error (0, "syntax error in mask expression \"%s\" near \
987 character #%d.\n", optarg, (int) (endptr - optarg), endptr);
988             }
989           break;
990         }
991       case 'n':
992         numerror ^= 1;
993         break;
994       case 'o':
995         if ((ofile = fopen (cygpath (optarg, NULL), "wb")) == NULL)
996           error (1, "can't open %s", optarg);
997 #ifdef F_SETFD
998         (void) fcntl (fileno (ofile), F_SETFD, 0);
999 #endif
1000         break;
1001       case 'p':
1002         pid = strtoul (optarg, NULL, 10);
1003         strace_active |= 2;
1004         break;
1005       case 'q':
1006         if (sawquiet < 0)
1007           sawquiet = 1;
1008         else
1009           sawquiet ^= 1;
1010         break;
1011       case 'S':
1012         flush_period = strtoul (optarg, NULL, 10);
1013         break;
1014       case 't':
1015         hhmmss ^= 1;
1016         break;
1017       case 'T':
1018         toggle ^= 1;
1019         break;
1020       case 'u':
1021         // FIXME: currently unimplemented
1022         show_usecs ^= 1;
1023         delta ^= 1;
1024         break;
1025       case 'v':
1026         // Print version info and exit
1027         print_version ();
1028         return 0;
1029       case 'w':
1030         new_window ^= 1;
1031         break;
1032       case '?':
1033         fprintf (stderr, "Try '%s --help' for more information.\n", pgm);
1034         exit (1);
1035       }
1036
1037   if (pid && argv[optind])
1038     error (0, "cannot provide both a command line and a process id");
1039
1040   if (!pid && !argv[optind])
1041     error (0, "must provide either a command line or a process id");
1042
1043   if (toggle && !pid)
1044     error (0, "must provide a process id to toggle tracing");
1045
1046   if (!pid)
1047     quiet = sawquiet < 0 || !sawquiet;
1048   else if (sawquiet < 0)
1049     quiet = 0;
1050   else
1051     quiet = sawquiet;
1052
1053   if (!mask)
1054     mask = _STRACE_ALL;
1055
1056   if (bufsize)
1057     setvbuf (ofile, (char *) alloca (bufsize), _IOFBF, bufsize);
1058
1059   if (!ofile)
1060     ofile = stdout;
1061
1062   DWORD res = 0;
1063   if (toggle)
1064     dotoggle (pid);
1065   else
1066     res = dostrace (mask, ofile, pid, argv + optind);
1067   return res;
1068 }
1069
1070 #undef CloseHandle
1071
1072 static BOOL
1073 close_handle (HANDLE h, DWORD ok)
1074 {
1075   child_list *c;
1076   for (c = &children; (c = c->next) != NULL;)
1077     if (c->hproc == h && c->id != ok)
1078       error (0, "Closing child handle %p", h);
1079   return CloseHandle (h);
1080 }