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