1195 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1195 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* strace.cc
 | |
| 
 | |
|    Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
 | |
|    2009, 2010, 2011, 2012, 2013, 2015 Red Hat Inc.
 | |
| 
 | |
|    Written by Chris Faylor <cgf@redhat.com>
 | |
| 
 | |
| This file is part of Cygwin.
 | |
| 
 | |
| This software is a copyrighted work licensed under the terms of the
 | |
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
| details. */
 | |
| 
 | |
| #include <windows.h>
 | |
| #include <winternl.h>
 | |
| #define cygwin_internal cygwin_internal_dontuse
 | |
| #include <stdio.h>
 | |
| #include <fcntl.h>
 | |
| #include <getopt.h>
 | |
| #include <stdarg.h>
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| #include <time.h>
 | |
| #include <signal.h>
 | |
| #include <errno.h>
 | |
| #include "../cygwin/include/sys/strace.h"
 | |
| #include "../cygwin/include/sys/cygwin.h"
 | |
| #include "../cygwin/include/cygwin/version.h"
 | |
| #include "../cygwin/cygtls_padsize.h"
 | |
| #include "path.h"
 | |
| #undef cygwin_internal
 | |
| #include "loadlib.h"
 | |
| 
 | |
| /* we *know* we're being built with GCC */
 | |
| #ifndef alloca
 | |
| #define alloca __builtin_alloca
 | |
| #endif
 | |
| 
 | |
| static const char *pgm;
 | |
| static int forkdebug = 1;
 | |
| static int numerror = 1;
 | |
| static int show_usecs = 1;
 | |
| static int delta = 1;
 | |
| static int events = 1;
 | |
| static int hhmmss;
 | |
| static int bufsize;
 | |
| static int new_window;
 | |
| static long flush_period;
 | |
| static int include_hex;
 | |
| static int quiet = -1;
 | |
| 
 | |
| static unsigned char strace_active = 1;
 | |
| static int processes;
 | |
| 
 | |
| static BOOL close_handle (HANDLE h, DWORD ok);
 | |
| 
 | |
| #define CloseHandle(h) close_handle(h, 0)
 | |
| 
 | |
| static void *drive_map;
 | |
| 
 | |
| struct child_list
 | |
| {
 | |
|   DWORD id;
 | |
|   HANDLE hproc;
 | |
|   int saw_stars;
 | |
|   char nfields;
 | |
|   long long start_time;
 | |
|   DWORD last_usecs;
 | |
|   struct child_list *next;
 | |
|     child_list ():id (0), hproc (NULL), saw_stars (0), nfields (0),
 | |
|     start_time (0), last_usecs (0), next (NULL)
 | |
|   {
 | |
|   }
 | |
| };
 | |
| 
 | |
| child_list children;
 | |
| 
 | |
| static void
 | |
| warn (int geterrno, const char *fmt, ...)
 | |
| {
 | |
|   va_list args;
 | |
|   char buf[4096];
 | |
| 
 | |
|   va_start (args, fmt);
 | |
|   sprintf (buf, "%s: ", pgm);
 | |
|   vsprintf (strchr (buf, '\0'), fmt, args);
 | |
|   if (geterrno)
 | |
|     perror (buf);
 | |
|   else
 | |
|     {
 | |
|       fputs (buf, stderr);
 | |
|       fputs ("\n", stderr);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void __attribute__ ((noreturn))
 | |
| error (int geterrno, const char *fmt, ...)
 | |
| {
 | |
|   va_list args;
 | |
|   char buf[4096];
 | |
| 
 | |
|   va_start (args, fmt);
 | |
|   sprintf (buf, "%s: ", pgm);
 | |
|   vsprintf (strchr (buf, '\0'), fmt, args);
 | |
|   if (geterrno)
 | |
|     perror (buf);
 | |
|   else
 | |
|     {
 | |
|       fputs (buf, stderr);
 | |
|       fputs ("\n", stderr);
 | |
|     }
 | |
|   exit (1);
 | |
| }
 | |
| 
 | |
| DWORD lastid = 0;
 | |
| HANDLE lasth;
 | |
| 
 | |
| static child_list *
 | |
| get_child (DWORD id)
 | |
| {
 | |
|   child_list *c;
 | |
|   for (c = &children; (c = c->next) != NULL;)
 | |
|     if (c->id == id)
 | |
|       return c;
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| static void
 | |
| add_child (DWORD id, HANDLE hproc)
 | |
| {
 | |
|   if (!get_child (id))
 | |
|     {
 | |
|       child_list *c = children.next;
 | |
|       children.next = (child_list *) calloc (1, sizeof (child_list));
 | |
|       children.next->next = c;
 | |
|       lastid = children.next->id = id;
 | |
|       lasth = children.next->hproc = hproc;
 | |
|       processes++;
 | |
|       if (!quiet)
 | |
| 	fprintf (stderr, "Windows process %lu attached\n", id);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| remove_child (DWORD id)
 | |
| {
 | |
|   child_list *c;
 | |
|   if (id == lastid)
 | |
|     lastid = 0;
 | |
|   for (c = &children; c->next != NULL; c = c->next)
 | |
|     if (c->next->id == id)
 | |
|       {
 | |
| 	child_list *c1 = c->next;
 | |
| 	c->next = c1->next;
 | |
| 	free (c1);
 | |
| 	if (!quiet)
 | |
| 	  fprintf (stderr, "Windows process %lu detached\n", id);
 | |
| 	processes--;
 | |
| 	return;
 | |
|       }
 | |
| 
 | |
|   error (0, "no process id %d found", id);
 | |
| }
 | |
| 
 | |
| #define LINE_BUF_CHUNK 128
 | |
| 
 | |
| class linebuf
 | |
| {
 | |
|   size_t alloc;
 | |
| public:
 | |
|     size_t ix;
 | |
|   char *buf;
 | |
|   linebuf ()
 | |
|   {
 | |
|     ix = 0;
 | |
|     alloc = 0;
 | |
|     buf = NULL;
 | |
|   }
 | |
|  ~linebuf ()
 | |
|   {
 | |
|     if (buf)
 | |
|       free (buf);
 | |
|   }
 | |
|   void add (const char *what, int len);
 | |
|   void add (const char *what)
 | |
|   {
 | |
|     add (what, strlen (what));
 | |
|   }
 | |
|   void prepend (const char *what, int len);
 | |
| };
 | |
| 
 | |
| void
 | |
| linebuf::add (const char *what, int len)
 | |
| {
 | |
|   size_t newix;
 | |
|   if ((newix = ix + len) >= alloc)
 | |
|     {
 | |
|       alloc += LINE_BUF_CHUNK + len;
 | |
|       buf = (char *) realloc (buf, alloc + 1);
 | |
|     }
 | |
|   memcpy (buf + ix, what, len);
 | |
|   ix = newix;
 | |
|   buf[ix] = '\0';
 | |
| }
 | |
| 
 | |
| void
 | |
| linebuf::prepend (const char *what, int len)
 | |
| {
 | |
|   int buflen;
 | |
|   size_t newix;
 | |
|   if ((newix = ix + len) >= alloc)
 | |
|     {
 | |
|       alloc += LINE_BUF_CHUNK + len;
 | |
|       buf = (char *) realloc (buf, alloc + 1);
 | |
|       buf[ix] = '\0';
 | |
|     }
 | |
|   if ((buflen = strlen (buf)))
 | |
|     memmove (buf + len, buf, buflen + 1);
 | |
|   else
 | |
|     buf[newix] = '\0';
 | |
|   memcpy (buf, what, len);
 | |
|   ix = newix;
 | |
| }
 | |
| 
 | |
| static void
 | |
| make_command_line (linebuf & one_line, char **argv)
 | |
| {
 | |
|   for (; *argv; argv++)
 | |
|     {
 | |
|       char *p = NULL;
 | |
|       const char *a = *argv;
 | |
| 
 | |
|       int len = strlen (a);
 | |
|       if (len != 0 && !(p = strpbrk (a, " \t\n\r\"")))
 | |
| 	one_line.add (a, len);
 | |
|       else
 | |
| 	{
 | |
| 	  one_line.add ("\"", 1);
 | |
| 	  for (; p; a = p, p = strchr (p, '"'))
 | |
| 	    {
 | |
| 	      one_line.add (a, ++p - a);
 | |
| 	      if (p[-1] == '"')
 | |
| 		one_line.add ("\"", 1);
 | |
| 	    }
 | |
| 	  if (*a)
 | |
| 	    one_line.add (a);
 | |
| 	  one_line.add ("\"", 1);
 | |
| 	}
 | |
|       one_line.add (" ", 1);
 | |
|     }
 | |
| 
 | |
|   if (one_line.ix)
 | |
|     one_line.buf[one_line.ix - 1] = '\0';
 | |
|   else
 | |
|     one_line.add ("", 1);
 | |
| }
 | |
| 
 | |
| static DWORD child_pid;
 | |
| 
 | |
| static BOOL WINAPI
 | |
| ctrl_c (DWORD)
 | |
| {
 | |
|   static int tic = 1;
 | |
|   if ((tic ^= 1) && !GenerateConsoleCtrlEvent (CTRL_C_EVENT, 0))
 | |
|     error (0, "couldn't send CTRL-C to child, win32 error %d\n",
 | |
| 	   GetLastError ());
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| extern "C" {
 | |
| uintptr_t (*cygwin_internal) (int, ...);
 | |
| WCHAR cygwin_dll_path[32768];
 | |
| };
 | |
| 
 | |
| static int
 | |
| load_cygwin ()
 | |
| {
 | |
|   static HMODULE h;
 | |
| 
 | |
|   if (cygwin_internal)
 | |
|     return 1;
 | |
| 
 | |
|   if (h)
 | |
|     return 0;
 | |
| 
 | |
|   if (!(h = LoadLibrary ("cygwin1.dll")))
 | |
|     {
 | |
|       errno = ENOENT;
 | |
|       return 0;
 | |
|     }
 | |
|   GetModuleFileNameW (h, cygwin_dll_path, 32768);
 | |
|   if (!(cygwin_internal = (uintptr_t (*) (int, ...)) GetProcAddress (h, "cygwin_internal")))
 | |
|     {
 | |
|       errno = ENOSYS;
 | |
|       return 0;
 | |
|     }
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| #define DEBUG_PROCESS_DETACH_ON_EXIT    0x00000001
 | |
| #define DEBUG_PROCESS_ONLY_THIS_PROCESS 0x00000002
 | |
| 
 | |
| static void
 | |
| attach_process (pid_t pid)
 | |
| {
 | |
|   child_pid = (DWORD) cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid);
 | |
|   if (!child_pid)
 | |
|     child_pid = pid;
 | |
| 
 | |
|   if (!DebugActiveProcess (child_pid))
 | |
|     error (0, "couldn't attach to pid %d for debugging", child_pid);
 | |
| 
 | |
|   if (forkdebug)
 | |
|     {
 | |
|       HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, child_pid);
 | |
| 
 | |
|       if (h)
 | |
| 	{
 | |
| 	  /* Try to turn off DEBUG_ONLY_THIS_PROCESS so we can follow forks */
 | |
| 	  /* This is only supported on XP and later */
 | |
| 	  ULONG DebugFlags = DEBUG_PROCESS_DETACH_ON_EXIT;
 | |
| 	  NTSTATUS status = NtSetInformationProcess (h, ProcessDebugFlags, &DebugFlags, sizeof (DebugFlags));
 | |
| 	  if (!NT_SUCCESS (status))
 | |
| 	    warn (0, "Could not clear DEBUG_ONLY_THIS_PROCESS (%x), will not trace child processes", status);
 | |
| 
 | |
| 	  CloseHandle(h);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| create_child (char **argv)
 | |
| {
 | |
|   linebuf one_line;
 | |
| 
 | |
|   STARTUPINFO si;
 | |
|   PROCESS_INFORMATION pi;
 | |
|   BOOL ret;
 | |
|   DWORD flags;
 | |
| 
 | |
|   if (strchr (*argv, '/'))
 | |
|       *argv = cygpath (*argv, NULL);
 | |
|   memset (&si, 0, sizeof (si));
 | |
|   si.cb = sizeof (si);
 | |
| 
 | |
|   flags = CREATE_DEFAULT_ERROR_MODE
 | |
| 	  | (forkdebug ? DEBUG_PROCESS : DEBUG_ONLY_THIS_PROCESS);
 | |
|   if (new_window)
 | |
|     flags |= CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP;
 | |
| 
 | |
|   make_command_line (one_line, argv);
 | |
| 
 | |
|   SetConsoleCtrlHandler (NULL, 0);
 | |
|   const char *cygwin_env = getenv ("CYGWIN");
 | |
|   const char *space;
 | |
|   if (cygwin_env)
 | |
|     space = " ";
 | |
|   else
 | |
|     space = cygwin_env = "";
 | |
|   char *newenv = (char *) malloc (sizeof ("CYGWIN=noglob") + strlen (space) + strlen (cygwin_env));
 | |
|   sprintf (newenv, "CYGWIN=noglob%s%s", space, cygwin_env);
 | |
|   _putenv (newenv);
 | |
|   ret = CreateProcess (0, one_line.buf,	/* command line */
 | |
| 		       NULL,	/* Security */
 | |
| 		       NULL,	/* thread */
 | |
| 		       TRUE,	/* inherit handles */
 | |
| 		       flags,	/* start flags */
 | |
| 		       NULL,	/* default environment */
 | |
| 		       NULL,	/* current directory */
 | |
| 		       &si, &pi);
 | |
|   if (!ret)
 | |
|     error (0, "error creating process %s, (error %d)", *argv,
 | |
| 	   GetLastError ());
 | |
| 
 | |
|   CloseHandle (pi.hThread);
 | |
|   CloseHandle (pi.hProcess);
 | |
|   child_pid = pi.dwProcessId;
 | |
|   SetConsoleCtrlHandler (ctrl_c, 1);
 | |
| }
 | |
| 
 | |
| static int
 | |
| output_winerror (FILE *ofile, char *s)
 | |
| {
 | |
|   char *winerr = strstr (s, "Win32 error ");
 | |
|   if (!winerr)
 | |
|     return 0;
 | |
| 
 | |
|   DWORD errnum = atoi (winerr + sizeof ("Win32 error ") - 1);
 | |
|   if (!errnum)
 | |
|     return 0;
 | |
| 
 | |
|   /*
 | |
|    * NOTE: Currently there is no policy for how long the
 | |
|    * the buffers are, and looks like 256 is a smallest one
 | |
|    * (dlfcn.cc). Other than error 1395 (length 213) and
 | |
|    * error 1015 (length 249), the rest are all under 188
 | |
|    * characters, and so I'll use 189 as the buffer length.
 | |
|    * For those longer error messages, FormatMessage will
 | |
|    * return FALSE, and we'll get the old behaviour such as
 | |
|    * ``Win32 error 1395'' etc.
 | |
|    */
 | |
|   char buf[4096];
 | |
|   if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
 | |
| 		      | FORMAT_MESSAGE_IGNORE_INSERTS,
 | |
| 		      NULL,
 | |
| 		      errnum,
 | |
| 		      MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
 | |
| 		      (LPTSTR) buf, sizeof (buf), NULL))
 | |
|     return 0;
 | |
| 
 | |
|   /* Get rid the trailing CR/NL pair. */
 | |
|   char *p = strchr (buf, '\0');
 | |
|   p[-2] = '\n';
 | |
|   p[-1] = '\0';
 | |
| 
 | |
|   *winerr = '\0';
 | |
|   fputs (s, ofile);
 | |
|   fputs (buf, ofile);
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| static SYSTEMTIME *
 | |
| syst (long long t)
 | |
| {
 | |
|   FILETIME n;
 | |
|   static SYSTEMTIME st;
 | |
|   long long now = t /*+ ((long long) usecs * 10)*/;
 | |
|   n.dwHighDateTime = now >> 32;
 | |
|   n.dwLowDateTime = now & 0xffffffff;
 | |
|   FileTimeToSystemTime (&n, &st);
 | |
|   return &st;
 | |
| }
 | |
| 
 | |
| static void __stdcall
 | |
| handle_output_debug_string (DWORD id, LPVOID p, unsigned mask, FILE *ofile)
 | |
| {
 | |
|   int len;
 | |
|   int special;
 | |
|   char alen[3 + 8 + 1];
 | |
|   SIZE_T nbytes;
 | |
|   child_list *child = get_child (id);
 | |
|   if (!child)
 | |
|     error (0, "no process id %d found", id);
 | |
|   HANDLE hchild = child->hproc;
 | |
| #define INTROLEN (sizeof (alen) - 1)
 | |
| 
 | |
|   if (id == lastid && hchild != lasth)
 | |
|     warn (0, "%p != %p", hchild, lasth);
 | |
| 
 | |
|   alen[INTROLEN] = '\0';
 | |
|   if (!ReadProcessMemory (hchild, p, alen, INTROLEN, &nbytes))
 | |
| #ifndef DEBUGGING
 | |
|     return;
 | |
| #else
 | |
|     error (0,
 | |
| 	   "couldn't get message length from subprocess %d<%p>, windows error %d",
 | |
| 	   id, hchild, GetLastError ());
 | |
| #endif
 | |
| 
 | |
|   if (strncmp (alen, "cYg", 3))
 | |
|     return;
 | |
|   len = (int) strtoul (alen + 3, NULL, 16);
 | |
|   if (!len)
 | |
|     return;
 | |
| 
 | |
|   if (len > 0)
 | |
|     special = 0;
 | |
|   else
 | |
|     {
 | |
|       special = len;
 | |
|       if (special == _STRACE_INTERFACE_ACTIVATE_ADDR || special == _STRACE_CHILD_PID)
 | |
| 	len = 17;
 | |
|     }
 | |
| 
 | |
|   char *buf;
 | |
|   buf = (char *) alloca (len + 85) + 20;
 | |
| 
 | |
|   if (!ReadProcessMemory (hchild, ((char *) p) + INTROLEN, buf, len, &nbytes))
 | |
|     error (0, "couldn't get message from subprocess, windows error %d",
 | |
| 	   GetLastError ());
 | |
| 
 | |
|   buf[len] = '\0';
 | |
|   char *s = strtok (buf, " ");
 | |
| 
 | |
| #ifdef __x86_64__
 | |
|   unsigned long long n = strtoull (s, NULL, 16);
 | |
| #else
 | |
|   unsigned long n = strtoul (s, NULL, 16);
 | |
| #endif
 | |
| 
 | |
|   s = strchr (s, '\0') + 1;
 | |
| 
 | |
|   if (special == _STRACE_CHILD_PID)
 | |
|     {
 | |
|       DebugActiveProcess (n);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   if (special == _STRACE_INTERFACE_ACTIVATE_ADDR)
 | |
|     {
 | |
|       s = strtok (NULL, " ");
 | |
|       if (*s && *s == '1' && !forkdebug)
 | |
| 	/* don't activate since we are not following forks */;
 | |
|       else if (!WriteProcessMemory (hchild, (LPVOID) n, &strace_active,
 | |
| 			       sizeof (strace_active), &nbytes))
 | |
| 	error (0, "couldn't write strace flag to subprocess at %p, "
 | |
| 	       "windows error %d", n, GetLastError ());
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   char *origs = s;
 | |
| 
 | |
|   if (mask & n)
 | |
|     /* got it */ ;
 | |
|   else if (!(mask & _STRACE_ALL) || (n & _STRACE_NOTALL))
 | |
|     return;			/* This should not be included in "all" output */
 | |
| 
 | |
|   DWORD dusecs, usecs;
 | |
|   char *ptusec, *ptrest;
 | |
| 
 | |
|   dusecs = strtoul (s, &ptusec, 10);
 | |
|   char *q = ptusec;
 | |
|   while (*q == ' ')
 | |
|     q++;
 | |
|   if (*q != '[')
 | |
|     {
 | |
|       usecs = strtoul (q, &ptrest, 10);
 | |
|       while (*ptrest == ' ')
 | |
| 	ptrest++;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       ptrest = q;
 | |
|       ptusec = show_usecs ? s : ptrest;
 | |
|       usecs = dusecs;
 | |
|     }
 | |
| 
 | |
| #if 1
 | |
|   child->saw_stars = 2;
 | |
| #else
 | |
|   if (child->saw_stars == 0)
 | |
|     {
 | |
|       FILETIME st;
 | |
|       char *news;
 | |
| 
 | |
|       GetSystemTimeAsFileTime (&st);
 | |
|       FileTimeToLocalFileTime (&st, &st);
 | |
|       child->start_time = st.dwHighDateTime;
 | |
|       child->start_time <<= 32;
 | |
|       child->start_time |= st.dwLowDateTime;
 | |
|       if (*(news = ptrest) != '[')
 | |
| 	child->saw_stars = 2;
 | |
|       else
 | |
| 	{
 | |
| 	  child->saw_stars++;
 | |
| 	  while ((news = strchr (news, ' ')) != NULL && *++news != '*')
 | |
| 	    child->nfields++;
 | |
| 	  if (news == NULL)
 | |
| 	    child->saw_stars++;
 | |
| 	  else
 | |
| 	    {
 | |
| 	      s = news;
 | |
| 	      child->nfields++;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|   else if (child->saw_stars < 2)
 | |
|     {
 | |
|       int i;
 | |
|       char *news;
 | |
|       if (*(news = ptrest) != '[')
 | |
| 	child->saw_stars = 2;
 | |
|       else
 | |
| 	{
 | |
| 	  for (i = 0; i < child->nfields; i++)
 | |
| 	    if ((news = strchr (news, ' ')) == NULL)
 | |
| 	      break;		// Should never happen
 | |
| 	    else
 | |
| 	      news++;
 | |
| 
 | |
| 	  if (news == NULL)
 | |
| 	    child->saw_stars = 2;
 | |
| 	  else
 | |
| 	    {
 | |
| 	      s = news;
 | |
| 	      if (*s == '*')
 | |
| 		{
 | |
| 		  SYSTEMTIME *st = syst (child->start_time);
 | |
| 		  fprintf (ofile,
 | |
| 			   "Date/Time:    %d-%02d-%02d %02d:%02d:%02d\n",
 | |
| 			   st->wYear, st->wMonth, st->wDay, st->wHour,
 | |
| 			   st->wMinute, st->wSecond);
 | |
| 		  child->saw_stars++;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   long long d = usecs - child->last_usecs;
 | |
|   char intbuf[40];
 | |
| 
 | |
|   if (child->saw_stars < 2 || s != origs)
 | |
|     /* Nothing */ ;
 | |
|   else if (hhmmss)
 | |
|     {
 | |
|       s = ptrest - 9;
 | |
|       SYSTEMTIME *st = syst (child->start_time + (long long) usecs * 10);
 | |
|       sprintf (s, "%02d:%02d:%02d", st->wHour, st->wMinute, st->wSecond);
 | |
|       *strchr (s, '\0') = ' ';
 | |
|     }
 | |
|   else if (!delta)
 | |
|     s = ptusec;
 | |
|   else
 | |
|     {
 | |
|       s = ptusec;
 | |
|       sprintf (intbuf, "%5d ", (int) d);
 | |
|       int len = strlen (intbuf);
 | |
| 
 | |
|       memcpy ((s -= len), intbuf, len);
 | |
|     }
 | |
| 
 | |
|   if (include_hex)
 | |
|     {
 | |
|       s -= 8;
 | |
| #ifdef __x86_64__
 | |
|       sprintf (s, "%012I64x", n);
 | |
| #else
 | |
|       sprintf (s, "%08lx", n);
 | |
| #endif
 | |
|       strchr (s, '\0')[0] = ' ';
 | |
|     }
 | |
|   child->last_usecs = usecs;
 | |
|   if (numerror || !output_winerror (ofile, s))
 | |
|     fputs (s, ofile);
 | |
|   if (!bufsize)
 | |
|     fflush (ofile);
 | |
| }
 | |
| 
 | |
| static BOOL
 | |
| GetFileNameFromHandle(HANDLE hFile, WCHAR pszFilename[MAX_PATH+1])
 | |
| {
 | |
|   BOOL result = FALSE;
 | |
|   ULONG len = 0;
 | |
|   OBJECT_NAME_INFORMATION *ntfn = (OBJECT_NAME_INFORMATION *) alloca (65536);
 | |
|   NTSTATUS status = NtQueryObject (hFile, ObjectNameInformation,
 | |
| 				   ntfn, 65536, &len);
 | |
|   if (NT_SUCCESS (status))
 | |
|     {
 | |
|       PWCHAR win32path = ntfn->Name.Buffer;
 | |
|       win32path[ntfn->Name.Length / sizeof (WCHAR)] = L'\0';
 | |
| 
 | |
|       /* NtQueryObject returns a native NT path.  (Try to) convert to Win32. */
 | |
|       if (drive_map)
 | |
| 	win32path = (PWCHAR) cygwin_internal (CW_MAP_DRIVE_MAP, drive_map,
 | |
| 					      win32path);
 | |
|       pszFilename[0] = L'\0';
 | |
|       wcsncat (pszFilename, win32path, MAX_PATH);
 | |
|       result = TRUE;
 | |
|     }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| static DWORD
 | |
| proc_child (unsigned mask, FILE *ofile, pid_t pid)
 | |
| {
 | |
|   DWORD res = 0;
 | |
|   DEBUG_EVENT ev;
 | |
|   time_t cur_time, last_time;
 | |
| 
 | |
|   SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
 | |
|   last_time = time (NULL);
 | |
|   while (1)
 | |
|     {
 | |
|       BOOL debug_event = WaitForDebugEvent (&ev, 1000);
 | |
|       DWORD status = DBG_CONTINUE;
 | |
| 
 | |
|       if (bufsize && flush_period > 0 &&
 | |
| 	  (cur_time = time (NULL)) >= last_time + flush_period)
 | |
| 	{
 | |
| 	  last_time = cur_time;
 | |
| 	  fflush (ofile);
 | |
| 	}
 | |
| 
 | |
|       if (!debug_event)
 | |
| 	continue;
 | |
| 
 | |
|       if (pid)
 | |
| 	{
 | |
| 	  (void) cygwin_internal (CW_STRACE_TOGGLE, pid);
 | |
| 	  pid = 0;
 | |
| 	}
 | |
| 
 | |
|       switch (ev.dwDebugEventCode)
 | |
| 	{
 | |
| 	case CREATE_PROCESS_DEBUG_EVENT:
 | |
| 	  if (events)
 | |
| 	    fprintf (ofile, "--- Process %lu created\n", ev.dwProcessId);
 | |
| 	  if (ev.u.CreateProcessInfo.hFile)
 | |
| 	    CloseHandle (ev.u.CreateProcessInfo.hFile);
 | |
| 	  add_child (ev.dwProcessId, ev.u.CreateProcessInfo.hProcess);
 | |
| 	  break;
 | |
| 
 | |
| 	case CREATE_THREAD_DEBUG_EVENT:
 | |
| 	  if (events)
 | |
| 	    fprintf (ofile, "--- Process %lu thread %lu created\n",
 | |
| 		     ev.dwProcessId, ev.dwThreadId);
 | |
| 	  break;
 | |
| 
 | |
| 	case LOAD_DLL_DEBUG_EVENT:
 | |
| 	  if (events)
 | |
| 	    {
 | |
| 	      // lpImageName is not always populated, so find the filename for
 | |
| 	      // hFile instead
 | |
| 	      WCHAR dllname[MAX_PATH+1];
 | |
| 	      if (!GetFileNameFromHandle(ev.u.LoadDll.hFile, dllname))
 | |
| 		wcscpy(dllname, L"(unknown)");
 | |
| 
 | |
| 	      fprintf (ofile, "--- Process %lu loaded %ls at %p\n",
 | |
| 		       ev.dwProcessId, dllname, ev.u.LoadDll.lpBaseOfDll);
 | |
| 	    }
 | |
| 
 | |
| 	  if (ev.u.LoadDll.hFile)
 | |
| 	    CloseHandle (ev.u.LoadDll.hFile);
 | |
| 	  break;
 | |
| 
 | |
| 	case UNLOAD_DLL_DEBUG_EVENT:
 | |
| 	  if (events)
 | |
| 	    fprintf (ofile, "--- Process %lu unloaded DLL at %p\n",
 | |
| 		     ev.dwProcessId, ev.u.UnloadDll.lpBaseOfDll);
 | |
| 	  break;
 | |
| 
 | |
| 	case OUTPUT_DEBUG_STRING_EVENT:
 | |
| 	  handle_output_debug_string (ev.dwProcessId,
 | |
| 				      ev.u.DebugString.lpDebugStringData,
 | |
| 				      mask, ofile);
 | |
| 	  break;
 | |
| 
 | |
| 	case EXIT_PROCESS_DEBUG_EVENT:
 | |
| 	  if (events)
 | |
| 	    fprintf (ofile, "--- Process %lu exited with status 0x%lx\n",
 | |
| 		     ev.dwProcessId, ev.u.ExitProcess.dwExitCode);
 | |
| 	  res = ev.u.ExitProcess.dwExitCode;
 | |
| 	  remove_child (ev.dwProcessId);
 | |
| 	  break;
 | |
| 
 | |
| 	case EXIT_THREAD_DEBUG_EVENT:
 | |
| 	  if (events)
 | |
| 	    fprintf (ofile, "--- Process %lu thread %lu exited with status 0x%lx\n",
 | |
| 		     ev.dwProcessId, ev.dwThreadId, ev.u.ExitThread.dwExitCode);
 | |
| 	  break;
 | |
| 
 | |
| 	case EXCEPTION_DEBUG_EVENT:
 | |
| 	  if (ev.u.Exception.ExceptionRecord.ExceptionCode
 | |
| 	      != (DWORD) STATUS_BREAKPOINT)
 | |
| 	    {
 | |
| 	      status = DBG_EXCEPTION_NOT_HANDLED;
 | |
| 	      if (ev.u.Exception.dwFirstChance)
 | |
| 		fprintf (ofile, "--- Process %lu, exception %08lx at %p\n",
 | |
| 			 ev.dwProcessId,
 | |
| 			 ev.u.Exception.ExceptionRecord.ExceptionCode,
 | |
| 			 ev.u.Exception.ExceptionRecord.ExceptionAddress);
 | |
| 	    }
 | |
| 	  break;
 | |
| 	}
 | |
|       if (!ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId, status))
 | |
| 	error (0, "couldn't continue debug event, windows error %d",
 | |
| 	       GetLastError ());
 | |
|       if (!processes)
 | |
| 	break;
 | |
|     }
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| static void
 | |
| dotoggle (pid_t pid)
 | |
| {
 | |
|   child_pid = (DWORD) cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid);
 | |
|   if (!child_pid)
 | |
|     {
 | |
|       warn (0, "no such cygwin pid - %d", pid);
 | |
|       child_pid = pid;
 | |
|     }
 | |
|   if (cygwin_internal (CW_STRACE_TOGGLE, child_pid))
 | |
|     error (0, "failed to toggle tracing for process %d<%d>", pid, child_pid);
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| static DWORD
 | |
| dostrace (unsigned mask, FILE *ofile, pid_t pid, char **argv)
 | |
| {
 | |
|   if (!pid)
 | |
|     create_child (argv);
 | |
|   else
 | |
|     attach_process (pid);
 | |
| 
 | |
|   return proc_child (mask, ofile, pid);
 | |
| }
 | |
| 
 | |
| typedef struct tag_mask_mnemonic
 | |
| {
 | |
|   unsigned long val;
 | |
|   const char *text;
 | |
| }
 | |
| mask_mnemonic;
 | |
| 
 | |
| static const mask_mnemonic mnemonic_table[] = {
 | |
|   {_STRACE_ALL, "all"},
 | |
|   {_STRACE_FLUSH, "flush"},
 | |
|   {_STRACE_INHERIT, "inherit"},
 | |
|   {_STRACE_UHOH, "uhoh"},
 | |
|   {_STRACE_SYSCALL, "syscall"},
 | |
|   {_STRACE_STARTUP, "startup"},
 | |
|   {_STRACE_DEBUG, "debug"},
 | |
|   {_STRACE_PARANOID, "paranoid"},
 | |
|   {_STRACE_TERMIOS, "termios"},
 | |
|   {_STRACE_SELECT, "select"},
 | |
|   {_STRACE_WM, "wm"},
 | |
|   {_STRACE_SIGP, "sigp"},
 | |
|   {_STRACE_MINIMAL, "minimal"},
 | |
|   {_STRACE_EXITDUMP, "exitdump"},
 | |
|   {_STRACE_SYSTEM, "system"},
 | |
|   {_STRACE_NOMUTEX, "nomutex"},
 | |
|   {_STRACE_MALLOC, "malloc"},
 | |
|   {_STRACE_THREAD, "thread"},
 | |
|   {_STRACE_PTHREAD, "pthread"},
 | |
|   {_STRACE_SPECIAL, "special"},
 | |
|   {0, NULL}
 | |
| };
 | |
| 
 | |
| static unsigned long
 | |
| mnemonic2ul (const char *nptr, char **endptr)
 | |
| {
 | |
|   // Look up mnemonic in table, return value.
 | |
|   // *endptr = ptr to char that breaks match.
 | |
|   const mask_mnemonic *mnp = mnemonic_table;
 | |
| 
 | |
|   while (mnp->text != NULL)
 | |
|     {
 | |
|       if (strcmp (mnp->text, nptr) == 0)
 | |
| 	{
 | |
| 	  // Found a match.
 | |
| 	  if (endptr != NULL)
 | |
| 	    {
 | |
| 	      *endptr = ((char *) nptr) + strlen (mnp->text);
 | |
| 	    }
 | |
| 	  return mnp->val;
 | |
| 	}
 | |
|       mnp++;
 | |
|     }
 | |
| 
 | |
|   // Didn't find it.
 | |
|   if (endptr != NULL)
 | |
|     {
 | |
|       *endptr = (char *) nptr;
 | |
|     }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static unsigned long
 | |
| parse_mask (const char *ms, char **endptr)
 | |
| {
 | |
|   const char *p = ms;
 | |
|   char *newp;
 | |
|   unsigned long retval = 0, thisval;
 | |
|   const size_t bufsize = 16;
 | |
|   char buffer[bufsize];
 | |
|   size_t len;
 | |
| 
 | |
|   while (*p != '\0')
 | |
|     {
 | |
|       // First extract the term, terminate it, and lowercase it.
 | |
|       strncpy (buffer, p, bufsize);
 | |
|       buffer[bufsize - 1] = '\0';
 | |
|       len = strcspn (buffer, "+,\0");
 | |
|       buffer[len] = '\0';
 | |
|       strlwr (buffer);
 | |
| 
 | |
|       // Check if this is a mnemonic.  We have to do this first or strtoul()
 | |
|       // will false-trigger on anything starting with "a" through "f".
 | |
|       thisval = mnemonic2ul (buffer, &newp);
 | |
|       if (buffer == newp)
 | |
| 	{
 | |
| 	  // This term isn't mnemonic, check if it's hex.
 | |
| 	  thisval = strtoul (buffer, &newp, 16);
 | |
| 	  if (newp != buffer + len)
 | |
| 	    {
 | |
| 	      // Not hex either, syntax error.
 | |
| 	      *endptr = (char *) p;
 | |
| 	      return 0;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       p += len;
 | |
|       retval += thisval;
 | |
| 
 | |
|       // Handle operators
 | |
|       if (*p == '\0')
 | |
| 	break;
 | |
|       if ((*p == '+') || (*p == ','))
 | |
| 	{
 | |
| 	  // For now these both equate to addition/ORing.  Until we get
 | |
| 	  // fancy and add things like "all-<something>", all we need do is
 | |
| 	  // continue the looping.
 | |
| 	  p++;
 | |
| 	  continue;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  // Syntax error
 | |
| 	  *endptr = (char *) p;
 | |
| 	  return 0;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   *endptr = (char *) p;
 | |
|   return retval;
 | |
| }
 | |
| 
 | |
| static void
 | |
| usage (FILE *where = stderr)
 | |
| {
 | |
|   fprintf (where, "\
 | |
| Usage: %s [OPTIONS] <command-line>\n\
 | |
| Usage: %s [OPTIONS] -p <pid>\n\
 | |
| \n\
 | |
| Trace system calls and signals\n\
 | |
| \n\
 | |
|   -b, --buffer-size=SIZE       set size of output file buffer\n\
 | |
|   -d, --no-delta               don't display the delta-t microsecond timestamp\n\
 | |
|   -e, --events                 log all Windows DEBUG_EVENTS (toggle - default true)\n\
 | |
|   -f, --trace-children         trace child processes (toggle - default true)\n\
 | |
|   -h, --help                   output usage information and exit\n\
 | |
|   -m, --mask=MASK              set message filter mask\n\
 | |
|   -n, --crack-error-numbers    output descriptive text instead of error\n\
 | |
| 			       numbers for Windows errors\n\
 | |
|   -o, --output=FILENAME        set output file to FILENAME\n\
 | |
|   -p, --pid=n                  attach to executing program with cygwin pid n\n\
 | |
|   -q, --quiet                  suppress messages about attaching, detaching, etc.\n\
 | |
|   -S, --flush-period=PERIOD    flush buffered strace output every PERIOD secs\n\
 | |
|   -t, --timestamp              use an absolute hh:mm:ss timestamp insted of \n\
 | |
| 			       the default microsecond timestamp.  Implies -d\n\
 | |
|   -T, --toggle                 toggle tracing in a process already being\n\
 | |
| 			       traced. Requires -p <pid>\n\
 | |
|   -u, --usecs                  toggle printing of microseconds timestamp\n\
 | |
|   -V, --version                output version information and exit\n\
 | |
|   -w, --new-window             spawn program under test in a new window\n\
 | |
| \n", pgm, pgm);
 | |
|   if ( where == stdout)
 | |
|     fprintf (stdout, "\
 | |
|     MASK can be any combination of the following mnemonics and/or hex values\n\
 | |
|     (0x is optional).  Combine masks with '+' or ',' like so:\n\
 | |
| \n\
 | |
| 		      --mask=wm+system,malloc+0x00800\n\
 | |
| \n\
 | |
|     Mnemonic Hex     Corresponding Def  Description\n\
 | |
|     =========================================================================\n\
 | |
|     all      0x000001 (_STRACE_ALL)      All strace messages.\n\
 | |
|     flush    0x000002 (_STRACE_FLUSH)    Flush output buffer after each message.\n\
 | |
|     inherit  0x000004 (_STRACE_INHERIT)  Children inherit mask from parent.\n\
 | |
|     uhoh     0x000008 (_STRACE_UHOH)     Unusual or weird phenomenon.\n\
 | |
|     syscall  0x000010 (_STRACE_SYSCALL)  System calls.\n\
 | |
|     startup  0x000020 (_STRACE_STARTUP)  argc/envp printout at startup.\n\
 | |
|     debug    0x000040 (_STRACE_DEBUG)    Info to help debugging. \n\
 | |
|     paranoid 0x000080 (_STRACE_PARANOID) Paranoid info.\n\
 | |
|     termios  0x000100 (_STRACE_TERMIOS)  Info for debugging termios stuff.\n\
 | |
|     select   0x000200 (_STRACE_SELECT)   Info on ugly select internals.\n\
 | |
|     wm       0x000400 (_STRACE_WM)       Trace Windows msgs (enable _strace_wm).\n\
 | |
|     sigp     0x000800 (_STRACE_SIGP)     Trace signal and process handling.\n\
 | |
|     minimal  0x001000 (_STRACE_MINIMAL)  Very minimal strace output.\n\
 | |
|     pthread  0x002000 (_STRACE_PTHREAD)	Pthread calls.\n\
 | |
|     exitdump 0x004000 (_STRACE_EXITDUMP) Dump strace cache on exit.\n\
 | |
|     system   0x008000 (_STRACE_SYSTEM)   Serious error; goes to console and log.\n\
 | |
|     nomutex  0x010000 (_STRACE_NOMUTEX)  Don't use mutex for synchronization.\n\
 | |
|     malloc   0x020000 (_STRACE_MALLOC)   Trace malloc calls.\n\
 | |
|     thread   0x040000 (_STRACE_THREAD)   Thread-locking calls.\n\
 | |
|     special  0x100000 (_STRACE_SPECIAL)  Special debugging printfs for\n\
 | |
| 					 non-checked-in code\n\
 | |
| ");
 | |
|   if (where == stderr)
 | |
|     fprintf (stderr, "Try `%s --help' for more information.\n", pgm);
 | |
|   exit (where == stderr ? 1 : 0 );
 | |
| }
 | |
| 
 | |
| struct option longopts[] = {
 | |
|   {"buffer-size", required_argument, NULL, 'b'},
 | |
|   {"events", no_argument, NULL, 'e'},
 | |
|   {"help", no_argument, NULL, 'h'},
 | |
|   {"flush-period", required_argument, NULL, 'S'},
 | |
|   {"hex", no_argument, NULL, 'H'},
 | |
|   {"mask", required_argument, NULL, 'm'},
 | |
|   {"new-window", no_argument, NULL, 'w'},
 | |
|   {"output", required_argument, NULL, 'o'},
 | |
|   {"no-delta", no_argument, NULL, 'd'},
 | |
|   {"pid", required_argument, NULL, 'p'},
 | |
|   {"quiet", no_argument, NULL, 'q'},
 | |
|   {"timestamp", no_argument, NULL, 't'},
 | |
|   {"toggle", no_argument, NULL, 'T'},
 | |
|   {"trace-children", no_argument, NULL, 'f'},
 | |
|   {"translate-error-numbers", no_argument, NULL, 'n'},
 | |
|   {"usecs", no_argument, NULL, 'u'},
 | |
|   {"version", no_argument, NULL, 'V'},
 | |
|   {NULL, 0, NULL, 0}
 | |
| };
 | |
| 
 | |
| static const char *const opts = "+b:dehHfm:no:p:qS:tTuVw";
 | |
| 
 | |
| static void
 | |
| print_version ()
 | |
| {
 | |
|   printf ("strace (cygwin) %d.%d.%d\n"
 | |
| 	  "System Trace\n"
 | |
| 	  "Copyright (C) 2000 - %s Red Hat, Inc.\n"
 | |
| 	  "This is free software; see the source for copying conditions.  There is NO\n"
 | |
| 	  "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
 | |
| 	  CYGWIN_VERSION_DLL_MAJOR / 1000,
 | |
| 	  CYGWIN_VERSION_DLL_MAJOR % 1000,
 | |
| 	  CYGWIN_VERSION_DLL_MINOR,
 | |
| 	  strrchr (__DATE__, ' ') + 1);
 | |
| }
 | |
| 
 | |
| int
 | |
| main2 (int argc, char **argv)
 | |
| {
 | |
|   unsigned mask = 0;
 | |
|   FILE *ofile = NULL;
 | |
|   pid_t pid = 0;
 | |
|   int opt;
 | |
|   int toggle = 0;
 | |
|   int sawquiet = -1;
 | |
|   DWORD ret = 0;
 | |
| 
 | |
|   if (load_cygwin ())
 | |
|     {
 | |
|       char **av = (char **) cygwin_internal (CW_ARGV);
 | |
|       if (av && (uintptr_t) av != (uintptr_t) -1)
 | |
| 	for (argc = 0, argv = av; *av; av++)
 | |
| 	  argc++;
 | |
|     }
 | |
| 
 | |
|   if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/')))
 | |
|     pgm = *argv;
 | |
|   else
 | |
|     pgm++;
 | |
| 
 | |
|   while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
 | |
|     switch (opt)
 | |
|       {
 | |
|       case 'b':
 | |
| 	bufsize = atoi (optarg);
 | |
| 	break;
 | |
|       case 'd':
 | |
| 	delta ^= 1;
 | |
| 	break;
 | |
|       case 'e':
 | |
| 	events ^= 1;
 | |
| 	break;
 | |
|       case 'f':
 | |
| 	forkdebug ^= 1;
 | |
| 	break;
 | |
|       case 'h':
 | |
| 	// Print help and exit
 | |
| 	usage (stdout);
 | |
| 	break;
 | |
|       case 'H':
 | |
| 	include_hex ^= 1;
 | |
| 	break;
 | |
|       case 'm':
 | |
| 	{
 | |
| 	  char *endptr;
 | |
| 	  mask = parse_mask (optarg, &endptr);
 | |
| 	  if (*endptr != '\0')
 | |
| 	    {
 | |
| 	      // Bad mask expression.
 | |
| 	      error (0, "syntax error in mask expression \"%s\" near \
 | |
| character #%d.\n", optarg, (int) (endptr - optarg), endptr);
 | |
| 	    }
 | |
| 	  break;
 | |
| 	}
 | |
|       case 'n':
 | |
| 	numerror ^= 1;
 | |
| 	break;
 | |
|       case 'o':
 | |
| 	if ((ofile = fopen (cygpath (optarg, NULL), "wb")) == NULL)
 | |
| 	  error (1, "can't open %s", optarg);
 | |
| #ifdef F_SETFD
 | |
| 	(void) fcntl (fileno (ofile), F_SETFD, 0);
 | |
| #endif
 | |
| 	break;
 | |
|       case 'p':
 | |
| 	pid = strtoul (optarg, NULL, 10);
 | |
| 	strace_active |= 2;
 | |
| 	break;
 | |
|       case 'q':
 | |
| 	if (sawquiet < 0)
 | |
| 	  sawquiet = 1;
 | |
| 	else
 | |
| 	  sawquiet ^= 1;
 | |
| 	break;
 | |
|       case 'S':
 | |
| 	flush_period = strtoul (optarg, NULL, 10);
 | |
| 	break;
 | |
|       case 't':
 | |
| 	hhmmss ^= 1;
 | |
| 	break;
 | |
|       case 'T':
 | |
| 	toggle ^= 1;
 | |
| 	break;
 | |
|       case 'u':
 | |
| 	// FIXME: currently unimplemented
 | |
| 	show_usecs ^= 1;
 | |
| 	delta ^= 1;
 | |
| 	break;
 | |
|       case 'V':
 | |
| 	// Print version info and exit
 | |
| 	print_version ();
 | |
| 	return 0;
 | |
|       case 'w':
 | |
| 	new_window ^= 1;
 | |
| 	break;
 | |
|       default:
 | |
| 	fprintf (stderr, "Try `%s --help' for more information.\n", pgm);
 | |
| 	exit (1);
 | |
|       }
 | |
| 
 | |
|   if (pid && argv[optind])
 | |
|     error (0, "cannot provide both a command line and a process id");
 | |
| 
 | |
|   if (!pid && !argv[optind])
 | |
|     error (0, "must provide either a command line or a process id");
 | |
| 
 | |
|   if (toggle && !pid)
 | |
|     error (0, "must provide a process id to toggle tracing");
 | |
| 
 | |
|   if (!pid)
 | |
|     quiet = sawquiet < 0 || !sawquiet;
 | |
|   else if (sawquiet < 0)
 | |
|     quiet = 0;
 | |
|   else
 | |
|     quiet = sawquiet;
 | |
| 
 | |
|   if (!mask)
 | |
|     mask = _STRACE_ALL;
 | |
| 
 | |
|   if (!ofile)
 | |
|     ofile = stdout;
 | |
| 
 | |
|   if (bufsize)
 | |
|     setvbuf (ofile, (char *) alloca (bufsize), _IOFBF, bufsize);
 | |
| 
 | |
|   if (toggle)
 | |
|     dotoggle (pid);
 | |
|   else
 | |
|     {
 | |
|       drive_map = (void *) cygwin_internal (CW_ALLOC_DRIVE_MAP);
 | |
|       ret = dostrace (mask, ofile, pid, argv + optind);
 | |
|       if (drive_map)
 | |
| 	cygwin_internal (CW_FREE_DRIVE_MAP, drive_map);
 | |
|     }
 | |
|   if (ofile && ofile != stdout)
 | |
|     fclose (ofile);
 | |
|   ExitProcess (ret);
 | |
| }
 | |
| 
 | |
| int
 | |
| main (int argc, char **argv)
 | |
| {
 | |
|   /* Make sure to have room for the _cygtls area *and* to initialize it.
 | |
|      This is required to make sure cygwin_internal calls into Cygwin work
 | |
|      reliably.  This problem has been noticed under AllocationPreference
 | |
|      registry setting to 0x100000 (TOP_DOWN). */
 | |
|   char buf[CYGTLS_PADSIZE];
 | |
| 
 | |
|   memset (buf, 0, sizeof (buf));
 | |
|   exit (main2 (argc, argv));
 | |
| }
 | |
| 
 | |
| #undef CloseHandle
 | |
| 
 | |
| static BOOL
 | |
| close_handle (HANDLE h, DWORD ok)
 | |
| {
 | |
|   child_list *c;
 | |
|   for (c = &children; (c = c->next) != NULL;)
 | |
|     if (c->hproc == h && c->id != ok)
 | |
|       error (0, "Closing child handle %p", h);
 | |
|   return CloseHandle (h);
 | |
| }
 |