]> sourceware.org Git - newlib-cygwin.git/blame - winsup/utils/strace.cc
Cygwin: add header defining GCC exception codes
[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 637#ifdef __x86_64__
8bfb1afd 638 sprintf (s, "%012llx", n);
61522196
CV
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
ae3370bb
CV
675static char *
676cygwin_pid (DWORD winpid)
677{
678 static char buf[48];
679 static DWORD max_cygpid = 0;
680 DWORD cygpid;
681
682 if (!max_cygpid)
683 max_cygpid = (DWORD) cygwin_internal (CW_MAX_CYGWIN_PID);
684
685 cygpid = (DWORD) cygwin_internal (CW_WINPID_TO_CYGWIN_PID, winpid);
686
687 if (cygpid >= max_cygpid)
688 snprintf (buf, sizeof buf, "%lu", winpid);
689 else
690 snprintf (buf, sizeof buf, "%lu (pid: %lu)", winpid, cygpid);
691 return buf;
692}
693
6265ac10 694static DWORD
d73f9745 695proc_child (unsigned mask, FILE *ofile, pid_t pid)
1fd5e000 696{
6265ac10 697 DWORD res = 0;
1fd5e000 698 DEBUG_EVENT ev;
86fbc3d9
ED
699 time_t cur_time, last_time;
700
1fd5e000 701 SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
86fbc3d9 702 last_time = time (NULL);
1fd5e000
CF
703 while (1)
704 {
705 BOOL debug_event = WaitForDebugEvent (&ev, 1000);
5e0d760f 706 DWORD status = DBG_CONTINUE;
86fbc3d9
ED
707
708 if (bufsize && flush_period > 0 &&
709 (cur_time = time (NULL)) >= last_time + flush_period)
710 {
711 last_time = cur_time;
712 fflush (ofile);
713 }
714
1fd5e000
CF
715 if (!debug_event)
716 continue;
717
d73f9745
CF
718 if (pid)
719 {
720 (void) cygwin_internal (CW_STRACE_TOGGLE, pid);
721 pid = 0;
722 }
723
1fd5e000
CF
724 switch (ev.dwDebugEventCode)
725 {
726 case CREATE_PROCESS_DEBUG_EVENT:
5c4129d9 727 if (events)
ae3370bb
CV
728 fprintf (ofile, "--- Process %s created\n",
729 cygwin_pid (ev.dwProcessId));
1fd5e000
CF
730 if (ev.u.CreateProcessInfo.hFile)
731 CloseHandle (ev.u.CreateProcessInfo.hFile);
1fd5e000 732 add_child (ev.dwProcessId, ev.u.CreateProcessInfo.hProcess);
1fd5e000
CF
733 break;
734
735 case CREATE_THREAD_DEBUG_EVENT:
5c4129d9 736 if (events)
ae3370bb
CV
737 fprintf (ofile, "--- Process %s thread %lu created\n",
738 cygwin_pid (ev.dwProcessId), ev.dwThreadId);
1fd5e000
CF
739 break;
740
741 case LOAD_DLL_DEBUG_EVENT:
5c4129d9
JT
742 if (events)
743 {
744 // lpImageName is not always populated, so find the filename for
745 // hFile instead
746 WCHAR dllname[MAX_PATH+1];
747 if (!GetFileNameFromHandle(ev.u.LoadDll.hFile, dllname))
748 wcscpy(dllname, L"(unknown)");
749
ae3370bb
CV
750 fprintf (ofile, "--- Process %s loaded %ls at %p\n",
751 cygwin_pid (ev.dwProcessId), dllname,
752 ev.u.LoadDll.lpBaseOfDll);
5c4129d9
JT
753 }
754
1fd5e000
CF
755 if (ev.u.LoadDll.hFile)
756 CloseHandle (ev.u.LoadDll.hFile);
757 break;
758
5c4129d9
JT
759 case UNLOAD_DLL_DEBUG_EVENT:
760 if (events)
ae3370bb
CV
761 fprintf (ofile, "--- Process %s unloaded DLL at %p\n",
762 cygwin_pid (ev.dwProcessId), ev.u.UnloadDll.lpBaseOfDll);
5c4129d9
JT
763 break;
764
1fd5e000
CF
765 case OUTPUT_DEBUG_STRING_EVENT:
766 handle_output_debug_string (ev.dwProcessId,
767 ev.u.DebugString.lpDebugStringData,
768 mask, ofile);
769 break;
770
771 case EXIT_PROCESS_DEBUG_EVENT:
5c4129d9 772 if (events)
ae3370bb
CV
773 fprintf (ofile, "--- Process %s exited with status 0x%lx\n",
774 cygwin_pid (ev.dwProcessId), ev.u.ExitProcess.dwExitCode);
44e1d662 775 res = ev.u.ExitProcess.dwExitCode;
daed3bec 776 remove_child (ev.dwProcessId);
1fd5e000 777 break;
5c4129d9
JT
778
779 case EXIT_THREAD_DEBUG_EVENT:
780 if (events)
ae3370bb
CV
781 fprintf (ofile, "--- Process %s thread %lu exited with "
782 "status 0x%lx\n",
783 cygwin_pid (ev.dwProcessId), ev.dwThreadId,
784 ev.u.ExitThread.dwExitCode);
5c4129d9
JT
785 break;
786
5e0d760f 787 case EXCEPTION_DEBUG_EVENT:
a157df31 788 switch (ev.u.Exception.ExceptionRecord.ExceptionCode)
5460fae7 789 {
a157df31
CV
790 case STATUS_BREAKPOINT:
791 case 0x406d1388: /* SetThreadName exception. */
792 break;
793 default:
5460fae7 794 status = DBG_EXCEPTION_NOT_HANDLED;
5d970405 795 if (ev.u.Exception.dwFirstChance)
ae3370bb
CV
796 fprintf (ofile, "--- Process %s, exception %08lx at %p\n",
797 cygwin_pid (ev.dwProcessId),
61522196 798 ev.u.Exception.ExceptionRecord.ExceptionCode,
5d970405 799 ev.u.Exception.ExceptionRecord.ExceptionAddress);
a157df31 800 break;
5460fae7 801 }
5e0d760f 802 break;
1fd5e000 803 }
5e0d760f 804 if (!ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId, status))
1fd5e000
CF
805 error (0, "couldn't continue debug event, windows error %d",
806 GetLastError ());
5d970405 807 if (!processes)
1fd5e000
CF
808 break;
809 }
6265ac10
CF
810
811 return res;
1fd5e000
CF
812}
813
80082f1e
CF
814static void
815dotoggle (pid_t pid)
816{
80082f1e
CF
817 child_pid = (DWORD) cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid);
818 if (!child_pid)
819 {
820 warn (0, "no such cygwin pid - %d", pid);
821 child_pid = pid;
822 }
823 if (cygwin_internal (CW_STRACE_TOGGLE, child_pid))
824 error (0, "failed to toggle tracing for process %d<%d>", pid, child_pid);
825
826 return;
827}
828
6265ac10 829static DWORD
de6f8362 830dostrace (unsigned mask, FILE *ofile, pid_t pid, char **argv)
1fd5e000 831{
de6f8362
CF
832 if (!pid)
833 create_child (argv);
834 else
835 attach_process (pid);
1fd5e000 836
6265ac10 837 return proc_child (mask, ofile, pid);
1fd5e000
CF
838}
839
33f0f67d
CV
840typedef struct tag_mask_mnemonic
841{
842 unsigned long val;
843 const char *text;
844}
845mask_mnemonic;
846
847static const mask_mnemonic mnemonic_table[] = {
848 {_STRACE_ALL, "all"},
849 {_STRACE_FLUSH, "flush"},
850 {_STRACE_INHERIT, "inherit"},
851 {_STRACE_UHOH, "uhoh"},
852 {_STRACE_SYSCALL, "syscall"},
853 {_STRACE_STARTUP, "startup"},
854 {_STRACE_DEBUG, "debug"},
855 {_STRACE_PARANOID, "paranoid"},
856 {_STRACE_TERMIOS, "termios"},
857 {_STRACE_SELECT, "select"},
858 {_STRACE_WM, "wm"},
859 {_STRACE_SIGP, "sigp"},
860 {_STRACE_MINIMAL, "minimal"},
861 {_STRACE_EXITDUMP, "exitdump"},
862 {_STRACE_SYSTEM, "system"},
863 {_STRACE_NOMUTEX, "nomutex"},
864 {_STRACE_MALLOC, "malloc"},
865 {_STRACE_THREAD, "thread"},
ff3e6bcb 866 {_STRACE_PTHREAD, "pthread"},
dbf41aee 867 {_STRACE_SPECIAL, "special"},
33f0f67d
CV
868 {0, NULL}
869};
870
871static unsigned long
872mnemonic2ul (const char *nptr, char **endptr)
873{
874 // Look up mnemonic in table, return value.
875 // *endptr = ptr to char that breaks match.
876 const mask_mnemonic *mnp = mnemonic_table;
877
878 while (mnp->text != NULL)
879 {
880 if (strcmp (mnp->text, nptr) == 0)
881 {
882 // Found a match.
883 if (endptr != NULL)
884 {
885 *endptr = ((char *) nptr) + strlen (mnp->text);
886 }
887 return mnp->val;
888 }
889 mnp++;
890 }
891
892 // Didn't find it.
893 if (endptr != NULL)
894 {
895 *endptr = (char *) nptr;
896 }
897 return 0;
898}
899
900static unsigned long
901parse_mask (const char *ms, char **endptr)
902{
903 const char *p = ms;
904 char *newp;
905 unsigned long retval = 0, thisval;
906 const size_t bufsize = 16;
907 char buffer[bufsize];
908 size_t len;
909
910 while (*p != '\0')
911 {
912 // First extract the term, terminate it, and lowercase it.
913 strncpy (buffer, p, bufsize);
914 buffer[bufsize - 1] = '\0';
915 len = strcspn (buffer, "+,\0");
916 buffer[len] = '\0';
917 strlwr (buffer);
918
919 // Check if this is a mnemonic. We have to do this first or strtoul()
920 // will false-trigger on anything starting with "a" through "f".
921 thisval = mnemonic2ul (buffer, &newp);
922 if (buffer == newp)
923 {
924 // This term isn't mnemonic, check if it's hex.
925 thisval = strtoul (buffer, &newp, 16);
926 if (newp != buffer + len)
927 {
928 // Not hex either, syntax error.
929 *endptr = (char *) p;
930 return 0;
931 }
932 }
933
934 p += len;
935 retval += thisval;
936
937 // Handle operators
938 if (*p == '\0')
939 break;
940 if ((*p == '+') || (*p == ','))
941 {
942 // For now these both equate to addition/ORing. Until we get
943 // fancy and add things like "all-<something>", all we need do is
944 // continue the looping.
945 p++;
946 continue;
947 }
948 else
949 {
950 // Syntax error
951 *endptr = (char *) p;
952 return 0;
953 }
954 }
955
956 *endptr = (char *) p;
957 return retval;
958}
959
e7fca6f8 960static void __attribute__ ((__noreturn__))
ba692ae9 961usage (FILE *where = stderr)
33f0f67d 962{
ba692ae9
CF
963 fprintf (where, "\
964Usage: %s [OPTIONS] <command-line>\n\
80082f1e 965Usage: %s [OPTIONS] -p <pid>\n\
92b499ac 966\n\
aa275fe0
JDF
967Trace system calls and signals\n\
968\n\
48f939e2
CF
969 -b, --buffer-size=SIZE set size of output file buffer\n\
970 -d, --no-delta don't display the delta-t microsecond timestamp\n\
5c4129d9 971 -e, --events log all Windows DEBUG_EVENTS (toggle - default true)\n\
f69af9b3 972 -f, --trace-children trace child processes (toggle - default true)\n\
ba692ae9 973 -h, --help output usage information and exit\n\
48f939e2 974 -m, --mask=MASK set message filter mask\n\
48f939e2 975 -n, --crack-error-numbers output descriptive text instead of error\n\
1b23b30b 976 numbers for Windows errors\n\
80082f1e
CF
977 -o, --output=FILENAME set output file to FILENAME\n\
978 -p, --pid=n attach to executing program with cygwin pid n\n\
99b08560 979 -q, --quiet suppress messages about attaching, detaching, etc.\n\
48f939e2 980 -S, --flush-period=PERIOD flush buffered strace output every PERIOD secs\n\
ba692ae9 981 -t, --timestamp use an absolute hh:mm:ss timestamp insted of \n\
1b23b30b 982 the default microsecond timestamp. Implies -d\n\
80082f1e 983 -T, --toggle toggle tracing in a process already being\n\
1b23b30b 984 traced. Requires -p <pid>\n\
801d6cc7 985 -u, --usecs toggle printing of microseconds timestamp\n\
92b499ac 986 -V, --version output version information and exit\n\
48f939e2 987 -w, --new-window spawn program under test in a new window\n\
80082f1e 988\n", pgm, pgm);
ba692ae9
CF
989 if ( where == stdout)
990 fprintf (stdout, "\
33f0f67d
CV
991 MASK can be any combination of the following mnemonics and/or hex values\n\
992 (0x is optional). Combine masks with '+' or ',' like so:\n\
993\n\
1b23b30b 994 --mask=wm+system,malloc+0x00800\n\
33f0f67d
CV
995\n\
996 Mnemonic Hex Corresponding Def Description\n\
997 =========================================================================\n\
dbf41aee
CF
998 all 0x000001 (_STRACE_ALL) All strace messages.\n\
999 flush 0x000002 (_STRACE_FLUSH) Flush output buffer after each message.\n\
1000 inherit 0x000004 (_STRACE_INHERIT) Children inherit mask from parent.\n\
1001 uhoh 0x000008 (_STRACE_UHOH) Unusual or weird phenomenon.\n\
1002 syscall 0x000010 (_STRACE_SYSCALL) System calls.\n\
1003 startup 0x000020 (_STRACE_STARTUP) argc/envp printout at startup.\n\
1004 debug 0x000040 (_STRACE_DEBUG) Info to help debugging. \n\
1005 paranoid 0x000080 (_STRACE_PARANOID) Paranoid info.\n\
1006 termios 0x000100 (_STRACE_TERMIOS) Info for debugging termios stuff.\n\
1007 select 0x000200 (_STRACE_SELECT) Info on ugly select internals.\n\
1008 wm 0x000400 (_STRACE_WM) Trace Windows msgs (enable _strace_wm).\n\
1009 sigp 0x000800 (_STRACE_SIGP) Trace signal and process handling.\n\
1010 minimal 0x001000 (_STRACE_MINIMAL) Very minimal strace output.\n\
7f32efbf 1011 pthread 0x002000 (_STRACE_PTHREAD) Pthread calls.\n\
dbf41aee
CF
1012 exitdump 0x004000 (_STRACE_EXITDUMP) Dump strace cache on exit.\n\
1013 system 0x008000 (_STRACE_SYSTEM) Serious error; goes to console and log.\n\
1014 nomutex 0x010000 (_STRACE_NOMUTEX) Don't use mutex for synchronization.\n\
1015 malloc 0x020000 (_STRACE_MALLOC) Trace malloc calls.\n\
1016 thread 0x040000 (_STRACE_THREAD) Thread-locking calls.\n\
1017 special 0x100000 (_STRACE_SPECIAL) Special debugging printfs for\n\
1b23b30b 1018 non-checked-in code\n\
33f0f67d 1019");
ba692ae9 1020 if (where == stderr)
92b499ac 1021 fprintf (stderr, "Try `%s --help' for more information.\n", pgm);
ba692ae9 1022 exit (where == stderr ? 1 : 0 );
33f0f67d
CV
1023}
1024
1025struct option longopts[] = {
33f0f67d 1026 {"buffer-size", required_argument, NULL, 'b'},
5c4129d9 1027 {"events", no_argument, NULL, 'e'},
de6f8362
CF
1028 {"help", no_argument, NULL, 'h'},
1029 {"flush-period", required_argument, NULL, 'S'},
459a9561 1030 {"hex", no_argument, NULL, 'H'},
33f0f67d 1031 {"mask", required_argument, NULL, 'm'},
de6f8362 1032 {"new-window", no_argument, NULL, 'w'},
33f0f67d 1033 {"output", required_argument, NULL, 'o'},
33f0f67d 1034 {"no-delta", no_argument, NULL, 'd'},
de6f8362 1035 {"pid", required_argument, NULL, 'p'},
5d970405 1036 {"quiet", no_argument, NULL, 'q'},
33f0f67d 1037 {"timestamp", no_argument, NULL, 't'},
80082f1e 1038 {"toggle", no_argument, NULL, 'T'},
de6f8362
CF
1039 {"trace-children", no_argument, NULL, 'f'},
1040 {"translate-error-numbers", no_argument, NULL, 'n'},
1041 {"usecs", no_argument, NULL, 'u'},
92b499ac 1042 {"version", no_argument, NULL, 'V'},
33f0f67d
CV
1043 {NULL, 0, NULL, 0}
1044};
1045
5c4129d9 1046static const char *const opts = "+b:dehHfm:no:p:qS:tTuVw";
33f0f67d 1047
ba692ae9
CF
1048static void
1049print_version ()
1050{
92b499ac 1051 printf ("strace (cygwin) %d.%d.%d\n"
1b23b30b 1052 "System Trace\n"
6e623e93 1053 "Copyright (C) 2000 - %s Cygwin Authors\n"
1b23b30b 1054 "This is free software; see the source for copying conditions. There is NO\n"
92b499ac 1055 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
1b23b30b
CF
1056 CYGWIN_VERSION_DLL_MAJOR / 1000,
1057 CYGWIN_VERSION_DLL_MAJOR % 1000,
1058 CYGWIN_VERSION_DLL_MINOR,
1059 strrchr (__DATE__, ' ') + 1);
ba692ae9
CF
1060}
1061
1fd5e000 1062int
6ab56bdd 1063main2 (int argc, char **argv)
1fd5e000
CF
1064{
1065 unsigned mask = 0;
1066 FILE *ofile = NULL;
80082f1e 1067 pid_t pid = 0;
1fd5e000 1068 int opt;
b841df79 1069 int toggle = 0;
5d970405 1070 int sawquiet = -1;
fe244117 1071 DWORD ret = 0;
1fd5e000 1072
26c07f70
CF
1073 if (load_cygwin ())
1074 {
1075 char **av = (char **) cygwin_internal (CW_ARGV);
61522196 1076 if (av && (uintptr_t) av != (uintptr_t) -1)
26c07f70
CF
1077 for (argc = 0, argv = av; *av; av++)
1078 argc++;
1079 }
1080
f5ecacfc
CV
1081 _setmode (1, _O_BINARY);
1082 _setmode (2, _O_BINARY);
1083
1fd5e000
CF
1084 if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/')))
1085 pgm = *argv;
1086 else
1087 pgm++;
1088
33f0f67d 1089 while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
1fd5e000
CF
1090 switch (opt)
1091 {
de6f8362
CF
1092 case 'b':
1093 bufsize = atoi (optarg);
33f0f67d 1094 break;
de6f8362
CF
1095 case 'd':
1096 delta ^= 1;
33f0f67d 1097 break;
5c4129d9
JT
1098 case 'e':
1099 events ^= 1;
1100 break;
1fd5e000
CF
1101 case 'f':
1102 forkdebug ^= 1;
1103 break;
de6f8362 1104 case 'h':
ba692ae9 1105 usage (stdout);
459a9561
CF
1106 case 'H':
1107 include_hex ^= 1;
1108 break;
1fd5e000 1109 case 'm':
33f0f67d
CV
1110 {
1111 char *endptr;
1112 mask = parse_mask (optarg, &endptr);
1113 if (*endptr != '\0')
1114 {
1115 // Bad mask expression.
1116 error (0, "syntax error in mask expression \"%s\" near \
1117character #%d.\n", optarg, (int) (endptr - optarg), endptr);
1118 }
80082f1e 1119 break;
33f0f67d 1120 }
de6f8362
CF
1121 case 'n':
1122 numerror ^= 1;
1123 break;
1fd5e000 1124 case 'o':
8b800efe 1125 if ((ofile = fopen (cygpath (optarg, NULL), "wb")) == NULL)
1fd5e000
CF
1126 error (1, "can't open %s", optarg);
1127#ifdef F_SETFD
1128 (void) fcntl (fileno (ofile), F_SETFD, 0);
1129#endif
1130 break;
de6f8362 1131 case 'p':
7c518815 1132 pid = strtoul (optarg, NULL, 10);
5d970405
CF
1133 strace_active |= 2;
1134 break;
1135 case 'q':
1136 if (sawquiet < 0)
1137 sawquiet = 1;
1138 else
1139 sawquiet ^= 1;
de6f8362
CF
1140 break;
1141 case 'S':
7c518815 1142 flush_period = strtoul (optarg, NULL, 10);
78fed23c 1143 break;
1fd5e000 1144 case 't':
78fed23c
CF
1145 hhmmss ^= 1;
1146 break;
80082f1e
CF
1147 case 'T':
1148 toggle ^= 1;
1149 break;
78fed23c 1150 case 'u':
de6f8362 1151 // FIXME: currently unimplemented
801d6cc7
CF
1152 show_usecs ^= 1;
1153 delta ^= 1;
86fbc3d9 1154 break;
92b499ac 1155 case 'V':
de6f8362 1156 // Print version info and exit
ba692ae9
CF
1157 print_version ();
1158 return 0;
86fbc3d9
ED
1159 case 'w':
1160 new_window ^= 1;
1161 break;
92b499ac
CV
1162 default:
1163 fprintf (stderr, "Try `%s --help' for more information.\n", pgm);
80082f1e 1164 exit (1);
1fd5e000
CF
1165 }
1166
80082f1e
CF
1167 if (pid && argv[optind])
1168 error (0, "cannot provide both a command line and a process id");
1169
1170 if (!pid && !argv[optind])
1171 error (0, "must provide either a command line or a process id");
1172
1173 if (toggle && !pid)
1174 error (0, "must provide a process id to toggle tracing");
ba692ae9 1175
5d970405
CF
1176 if (!pid)
1177 quiet = sawquiet < 0 || !sawquiet;
1178 else if (sawquiet < 0)
1179 quiet = 0;
1180 else
1181 quiet = sawquiet;
1182
1fd5e000 1183 if (!mask)
7c03f799 1184 mask = _STRACE_ALL;
1fd5e000
CF
1185
1186 if (!ofile)
1187 ofile = stdout;
1188
fe244117
CV
1189 if (bufsize)
1190 setvbuf (ofile, (char *) alloca (bufsize), _IOFBF, bufsize);
1191
80082f1e
CF
1192 if (toggle)
1193 dotoggle (pid);
1194 else
5c4129d9
JT
1195 {
1196 drive_map = (void *) cygwin_internal (CW_ALLOC_DRIVE_MAP);
fe244117 1197 ret = dostrace (mask, ofile, pid, argv + optind);
5c4129d9
JT
1198 if (drive_map)
1199 cygwin_internal (CW_FREE_DRIVE_MAP, drive_map);
5c4129d9 1200 }
fe244117
CV
1201 if (ofile && ofile != stdout)
1202 fclose (ofile);
1203 ExitProcess (ret);
1fd5e000
CF
1204}
1205
6ab56bdd
CV
1206int
1207main (int argc, char **argv)
1208{
1209 /* Make sure to have room for the _cygtls area *and* to initialize it.
1210 This is required to make sure cygwin_internal calls into Cygwin work
1211 reliably. This problem has been noticed under AllocationPreference
1212 registry setting to 0x100000 (TOP_DOWN). */
1213 char buf[CYGTLS_PADSIZE];
1214
b43e28ae 1215 RtlSecureZeroMemory (buf, sizeof (buf));
6ab56bdd
CV
1216 exit (main2 (argc, argv));
1217}
1218
1fd5e000
CF
1219#undef CloseHandle
1220
1221static BOOL
1222close_handle (HANDLE h, DWORD ok)
1223{
1224 child_list *c;
eedc36cb 1225 for (c = &children; (c = c->next) != NULL;)
1fd5e000
CF
1226 if (c->hproc == h && c->id != ok)
1227 error (0, "Closing child handle %p", h);
1228 return CloseHandle (h);
1229}
This page took 0.483196 seconds and 5 git commands to generate.