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