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