]> sourceware.org Git - newlib-cygwin.git/blame - winsup/utils/strace.cc
include/elf/
[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,
3afd8b74 4 2009, 2010 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
de6f8362 14#define cygwin_internal cygwin_internal_dontuse
1fd5e000
CF
15#include <stdio.h>
16#include <fcntl.h>
17#include <getopt.h>
18#include <stdarg.h>
19#include <string.h>
20#include <stdlib.h>
86fbc3d9 21#include <time.h>
1fd5e000
CF
22#include <windows.h>
23#include <signal.h>
de6f8362
CF
24#include <errno.h>
25#include "cygwin/include/sys/strace.h"
26#include "cygwin/include/sys/cygwin.h"
229f2bb4 27#include "path.h"
de6f8362 28#undef cygwin_internal
71d8f118 29#include "loadlib.h"
02a7f96e 30#include "ddk/ntapi.h"
1fd5e000 31
b6e259b1
DD
32/* we *know* we're being built with GCC */
33#define alloca __builtin_alloca
34
33f0f67d 35// Version string.
ba692ae9 36static const char version[] = "$Revision$";
33f0f67d 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
CF
267extern "C" {
268unsigned long (*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);
de6f8362
CF
289 if (!(cygwin_internal = (DWORD (*) (int, ...)) GetProcAddress (h, "cygwin_internal")))
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)
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 (status)
321 warn (0, "Could not clear DEBUG_ONLY_THIS_PROCESS (%x), will not trace child processes", status);
322
323 CloseHandle(h);
324 }
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];
440 DWORD 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
a593a4c5 485 unsigned long n = strtoul (s, NULL, 16);
1fd5e000
CF
486
487 s = strchr (s, '\0') + 1;
488
5d970405
CF
489 if (special == _STRACE_CHILD_PID)
490 {
5d970405
CF
491 return;
492 }
493
1fd5e000
CF
494 if (special == _STRACE_INTERFACE_ACTIVATE_ADDR)
495 {
5d970405
CF
496 if (!WriteProcessMemory (hchild, (LPVOID) n, &strace_active,
497 sizeof (strace_active), &nbytes))
498 error (0, "couldn't write strace flag to subprocess at %p, "
499 "windows error %d", n, GetLastError ());
1fd5e000
CF
500 return;
501 }
502
78fed23c
CF
503 char *origs = s;
504
1fd5e000 505 if (mask & n)
eedc36cb 506 /* got it */ ;
1fd5e000 507 else if (!(mask & _STRACE_ALL) || (n & _STRACE_NOTALL))
eedc36cb 508 return; /* This should not be included in "all" output */
1fd5e000 509
78fed23c
CF
510 DWORD dusecs, usecs;
511 char *ptusec, *ptrest;
512
513 dusecs = strtoul (s, &ptusec, 10);
514 char *q = ptusec;
515 while (*q == ' ')
516 q++;
517 if (*q != '[')
518 {
519 usecs = strtoul (q, &ptrest, 10);
520 while (*ptrest == ' ')
521 ptrest++;
522 }
523 else
524 {
525 ptrest = q;
801d6cc7 526 ptusec = show_usecs ? s : ptrest;
78fed23c
CF
527 usecs = dusecs;
528 }
529
22fe0c41 530 if (child->saw_stars == 0)
78fed23c
CF
531 {
532 FILETIME st;
533 char *news;
534
535 GetSystemTimeAsFileTime (&st);
536 FileTimeToLocalFileTime (&st, &st);
22fe0c41
CF
537 child->start_time = st.dwHighDateTime;
538 child->start_time <<= 32;
539 child->start_time |= st.dwLowDateTime;
78fed23c 540 if (*(news = ptrest) != '[')
22fe0c41 541 child->saw_stars = 2;
78fed23c
CF
542 else
543 {
22fe0c41 544 child->saw_stars++;
78fed23c 545 while ((news = strchr (news, ' ')) != NULL && *++news != '*')
22fe0c41 546 child->nfields++;
78fed23c 547 if (news == NULL)
22fe0c41 548 child->saw_stars++;
78fed23c
CF
549 else
550 {
551 s = news;
22fe0c41 552 child->nfields++;
78fed23c
CF
553 }
554 }
555 }
22fe0c41 556 else if (child->saw_stars < 2)
78fed23c
CF
557 {
558 int i;
559 char *news;
560 if (*(news = ptrest) != '[')
22fe0c41 561 child->saw_stars = 2;
78fed23c
CF
562 else
563 {
22fe0c41 564 for (i = 0; i < child->nfields; i++)
78fed23c 565 if ((news = strchr (news, ' ')) == NULL)
eedc36cb 566 break; // Should never happen
78fed23c
CF
567 else
568 news++;
569
570 if (news == NULL)
22fe0c41 571 child->saw_stars = 2;
78fed23c
CF
572 else
573 {
574 s = news;
575 if (*s == '*')
576 {
22fe0c41 577 SYSTEMTIME *st = syst (child->start_time);
eedc36cb
CF
578 fprintf (ofile,
579 "Date/Time: %d-%02d-%02d %02d:%02d:%02d\n",
580 st->wYear, st->wMonth, st->wDay, st->wHour,
581 st->wMinute, st->wSecond);
22fe0c41 582 child->saw_stars++;
78fed23c
CF
583 }
584 }
585 }
586 }
587
22fe0c41 588 long long d = usecs - child->last_usecs;
78fed23c
CF
589 char intbuf[40];
590
22fe0c41 591 if (child->saw_stars < 2 || s != origs)
eedc36cb 592 /* Nothing */ ;
78fed23c
CF
593 else if (hhmmss)
594 {
595 s = ptrest - 9;
22fe0c41 596 SYSTEMTIME *st = syst (child->start_time + (long long) usecs * 10);
78fed23c
CF
597 sprintf (s, "%02d:%02d:%02d", st->wHour, st->wMinute, st->wSecond);
598 *strchr (s, '\0') = ' ';
599 }
600 else if (!delta)
601 s = ptusec;
602 else
603 {
604 s = ptusec;
605 sprintf (intbuf, "%5d ", (int) d);
606 int len = strlen (intbuf);
607
608 memcpy ((s -= len), intbuf, len);
609 }
610
459a9561
CF
611 if (include_hex)
612 {
613 s -= 8;
a593a4c5 614 sprintf (s, "%p", (void *) n);
459a9561
CF
615 strchr (s, '\0')[0] = ' ';
616 }
22fe0c41 617 child->last_usecs = usecs;
78fed23c 618 if (numerror || !output_winerror (ofile, s))
1fd5e000 619 fputs (s, ofile);
3c952fed
CF
620 if (!bufsize)
621 fflush (ofile);
1fd5e000
CF
622}
623
6265ac10 624static DWORD
d73f9745 625proc_child (unsigned mask, FILE *ofile, pid_t pid)
1fd5e000 626{
6265ac10 627 DWORD res = 0;
1fd5e000 628 DEBUG_EVENT ev;
86fbc3d9
ED
629 time_t cur_time, last_time;
630
1fd5e000 631 SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
86fbc3d9 632 last_time = time (NULL);
1fd5e000
CF
633 while (1)
634 {
635 BOOL debug_event = WaitForDebugEvent (&ev, 1000);
5e0d760f 636 DWORD status = DBG_CONTINUE;
86fbc3d9
ED
637
638 if (bufsize && flush_period > 0 &&
639 (cur_time = time (NULL)) >= last_time + flush_period)
640 {
641 last_time = cur_time;
642 fflush (ofile);
643 }
644
1fd5e000
CF
645 if (!debug_event)
646 continue;
647
d73f9745
CF
648 if (pid)
649 {
650 (void) cygwin_internal (CW_STRACE_TOGGLE, pid);
651 pid = 0;
652 }
653
1fd5e000
CF
654 switch (ev.dwDebugEventCode)
655 {
656 case CREATE_PROCESS_DEBUG_EVENT:
657 if (ev.u.CreateProcessInfo.hFile)
658 CloseHandle (ev.u.CreateProcessInfo.hFile);
1fd5e000 659 add_child (ev.dwProcessId, ev.u.CreateProcessInfo.hProcess);
1fd5e000
CF
660 break;
661
662 case CREATE_THREAD_DEBUG_EVENT:
1fd5e000
CF
663 break;
664
665 case LOAD_DLL_DEBUG_EVENT:
666 if (ev.u.LoadDll.hFile)
667 CloseHandle (ev.u.LoadDll.hFile);
668 break;
669
670 case OUTPUT_DEBUG_STRING_EVENT:
671 handle_output_debug_string (ev.dwProcessId,
672 ev.u.DebugString.lpDebugStringData,
673 mask, ofile);
674 break;
675
676 case EXIT_PROCESS_DEBUG_EVENT:
6265ac10 677 res = ev.u.ExitProcess.dwExitCode >> 8;
daed3bec 678 remove_child (ev.dwProcessId);
1fd5e000 679 break;
5e0d760f 680 case EXCEPTION_DEBUG_EVENT:
5d970405 681 if (ev.u.Exception.ExceptionRecord.ExceptionCode != STATUS_BREAKPOINT)
5460fae7
CF
682 {
683 status = DBG_EXCEPTION_NOT_HANDLED;
5d970405 684 if (ev.u.Exception.dwFirstChance)
a35d9f1a 685 fprintf (ofile, "--- Process %lu, exception %p at %p\n", ev.dwProcessId,
2971f076 686 (void *) ev.u.Exception.ExceptionRecord.ExceptionCode,
5d970405 687 ev.u.Exception.ExceptionRecord.ExceptionAddress);
5460fae7 688 }
5e0d760f 689 break;
1fd5e000 690 }
5e0d760f 691 if (!ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId, status))
1fd5e000
CF
692 error (0, "couldn't continue debug event, windows error %d",
693 GetLastError ());
5d970405 694 if (!processes)
1fd5e000
CF
695 break;
696 }
6265ac10
CF
697
698 return res;
1fd5e000
CF
699}
700
80082f1e
CF
701static void
702dotoggle (pid_t pid)
703{
80082f1e
CF
704 child_pid = (DWORD) cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid);
705 if (!child_pid)
706 {
707 warn (0, "no such cygwin pid - %d", pid);
708 child_pid = pid;
709 }
710 if (cygwin_internal (CW_STRACE_TOGGLE, child_pid))
711 error (0, "failed to toggle tracing for process %d<%d>", pid, child_pid);
712
713 return;
714}
715
6265ac10 716static DWORD
de6f8362 717dostrace (unsigned mask, FILE *ofile, pid_t pid, char **argv)
1fd5e000 718{
de6f8362
CF
719 if (!pid)
720 create_child (argv);
721 else
722 attach_process (pid);
1fd5e000 723
6265ac10 724 return proc_child (mask, ofile, pid);
1fd5e000
CF
725}
726
33f0f67d
CV
727typedef struct tag_mask_mnemonic
728{
729 unsigned long val;
730 const char *text;
731}
732mask_mnemonic;
733
734static const mask_mnemonic mnemonic_table[] = {
735 {_STRACE_ALL, "all"},
736 {_STRACE_FLUSH, "flush"},
737 {_STRACE_INHERIT, "inherit"},
738 {_STRACE_UHOH, "uhoh"},
739 {_STRACE_SYSCALL, "syscall"},
740 {_STRACE_STARTUP, "startup"},
741 {_STRACE_DEBUG, "debug"},
742 {_STRACE_PARANOID, "paranoid"},
743 {_STRACE_TERMIOS, "termios"},
744 {_STRACE_SELECT, "select"},
745 {_STRACE_WM, "wm"},
746 {_STRACE_SIGP, "sigp"},
747 {_STRACE_MINIMAL, "minimal"},
748 {_STRACE_EXITDUMP, "exitdump"},
749 {_STRACE_SYSTEM, "system"},
750 {_STRACE_NOMUTEX, "nomutex"},
751 {_STRACE_MALLOC, "malloc"},
752 {_STRACE_THREAD, "thread"},
ff3e6bcb 753 {_STRACE_PTHREAD, "pthread"},
dbf41aee 754 {_STRACE_SPECIAL, "special"},
33f0f67d
CV
755 {0, NULL}
756};
757
758static unsigned long
759mnemonic2ul (const char *nptr, char **endptr)
760{
761 // Look up mnemonic in table, return value.
762 // *endptr = ptr to char that breaks match.
763 const mask_mnemonic *mnp = mnemonic_table;
764
765 while (mnp->text != NULL)
766 {
767 if (strcmp (mnp->text, nptr) == 0)
768 {
769 // Found a match.
770 if (endptr != NULL)
771 {
772 *endptr = ((char *) nptr) + strlen (mnp->text);
773 }
774 return mnp->val;
775 }
776 mnp++;
777 }
778
779 // Didn't find it.
780 if (endptr != NULL)
781 {
782 *endptr = (char *) nptr;
783 }
784 return 0;
785}
786
787static unsigned long
788parse_mask (const char *ms, char **endptr)
789{
790 const char *p = ms;
791 char *newp;
792 unsigned long retval = 0, thisval;
793 const size_t bufsize = 16;
794 char buffer[bufsize];
795 size_t len;
796
797 while (*p != '\0')
798 {
799 // First extract the term, terminate it, and lowercase it.
800 strncpy (buffer, p, bufsize);
801 buffer[bufsize - 1] = '\0';
802 len = strcspn (buffer, "+,\0");
803 buffer[len] = '\0';
804 strlwr (buffer);
805
806 // Check if this is a mnemonic. We have to do this first or strtoul()
807 // will false-trigger on anything starting with "a" through "f".
808 thisval = mnemonic2ul (buffer, &newp);
809 if (buffer == newp)
810 {
811 // This term isn't mnemonic, check if it's hex.
812 thisval = strtoul (buffer, &newp, 16);
813 if (newp != buffer + len)
814 {
815 // Not hex either, syntax error.
816 *endptr = (char *) p;
817 return 0;
818 }
819 }
820
821 p += len;
822 retval += thisval;
823
824 // Handle operators
825 if (*p == '\0')
826 break;
827 if ((*p == '+') || (*p == ','))
828 {
829 // For now these both equate to addition/ORing. Until we get
830 // fancy and add things like "all-<something>", all we need do is
831 // continue the looping.
832 p++;
833 continue;
834 }
835 else
836 {
837 // Syntax error
838 *endptr = (char *) p;
839 return 0;
840 }
841 }
842
843 *endptr = (char *) p;
844 return retval;
845}
846
847static void
ba692ae9 848usage (FILE *where = stderr)
33f0f67d 849{
ba692ae9
CF
850 fprintf (where, "\
851Usage: %s [OPTIONS] <command-line>\n\
80082f1e 852Usage: %s [OPTIONS] -p <pid>\n\
aa275fe0
JDF
853Trace system calls and signals\n\
854\n\
48f939e2
CF
855 -b, --buffer-size=SIZE set size of output file buffer\n\
856 -d, --no-delta don't display the delta-t microsecond timestamp\n\
f69af9b3 857 -f, --trace-children trace child processes (toggle - default true)\n\
ba692ae9 858 -h, --help output usage information and exit\n\
48f939e2 859 -m, --mask=MASK set message filter mask\n\
48f939e2
CF
860 -n, --crack-error-numbers output descriptive text instead of error\n\
861 numbers for Windows errors\n\
80082f1e
CF
862 -o, --output=FILENAME set output file to FILENAME\n\
863 -p, --pid=n attach to executing program with cygwin pid n\n\
99b08560 864 -q, --quiet suppress messages about attaching, detaching, etc.\n\
48f939e2 865 -S, --flush-period=PERIOD flush buffered strace output every PERIOD secs\n\
ba692ae9
CF
866 -t, --timestamp use an absolute hh:mm:ss timestamp insted of \n\
867 the default microsecond timestamp. Implies -d\n\
80082f1e
CF
868 -T, --toggle toggle tracing in a process already being\n\
869 traced. Requires -p <pid>\n\
801d6cc7 870 -u, --usecs toggle printing of microseconds timestamp\n\
ba692ae9 871 -v, --version output version information and exit\n\
48f939e2 872 -w, --new-window spawn program under test in a new window\n\
80082f1e 873\n", pgm, pgm);
ba692ae9
CF
874 if ( where == stdout)
875 fprintf (stdout, "\
33f0f67d
CV
876 MASK can be any combination of the following mnemonics and/or hex values\n\
877 (0x is optional). Combine masks with '+' or ',' like so:\n\
878\n\
879 --mask=wm+system,malloc+0x00800\n\
880\n\
881 Mnemonic Hex Corresponding Def Description\n\
882 =========================================================================\n\
dbf41aee
CF
883 all 0x000001 (_STRACE_ALL) All strace messages.\n\
884 flush 0x000002 (_STRACE_FLUSH) Flush output buffer after each message.\n\
885 inherit 0x000004 (_STRACE_INHERIT) Children inherit mask from parent.\n\
886 uhoh 0x000008 (_STRACE_UHOH) Unusual or weird phenomenon.\n\
887 syscall 0x000010 (_STRACE_SYSCALL) System calls.\n\
888 startup 0x000020 (_STRACE_STARTUP) argc/envp printout at startup.\n\
889 debug 0x000040 (_STRACE_DEBUG) Info to help debugging. \n\
890 paranoid 0x000080 (_STRACE_PARANOID) Paranoid info.\n\
891 termios 0x000100 (_STRACE_TERMIOS) Info for debugging termios stuff.\n\
892 select 0x000200 (_STRACE_SELECT) Info on ugly select internals.\n\
893 wm 0x000400 (_STRACE_WM) Trace Windows msgs (enable _strace_wm).\n\
894 sigp 0x000800 (_STRACE_SIGP) Trace signal and process handling.\n\
895 minimal 0x001000 (_STRACE_MINIMAL) Very minimal strace output.\n\
896 pthread 0x002000 (_STRACE_PTHREAD) Pthread calls.\n\
897 exitdump 0x004000 (_STRACE_EXITDUMP) Dump strace cache on exit.\n\
898 system 0x008000 (_STRACE_SYSTEM) Serious error; goes to console and log.\n\
899 nomutex 0x010000 (_STRACE_NOMUTEX) Don't use mutex for synchronization.\n\
900 malloc 0x020000 (_STRACE_MALLOC) Trace malloc calls.\n\
901 thread 0x040000 (_STRACE_THREAD) Thread-locking calls.\n\
902 special 0x100000 (_STRACE_SPECIAL) Special debugging printfs for\n\
903 non-checked-in code\n\
33f0f67d 904");
ba692ae9
CF
905 if (where == stderr)
906 fprintf (stderr, "Try '%s --help' for more information.\n", pgm);
907 exit (where == stderr ? 1 : 0 );
33f0f67d
CV
908}
909
910struct option longopts[] = {
33f0f67d 911 {"buffer-size", required_argument, NULL, 'b'},
de6f8362
CF
912 {"help", no_argument, NULL, 'h'},
913 {"flush-period", required_argument, NULL, 'S'},
459a9561 914 {"hex", no_argument, NULL, 'H'},
33f0f67d 915 {"mask", required_argument, NULL, 'm'},
de6f8362 916 {"new-window", no_argument, NULL, 'w'},
33f0f67d 917 {"output", required_argument, NULL, 'o'},
33f0f67d 918 {"no-delta", no_argument, NULL, 'd'},
de6f8362 919 {"pid", required_argument, NULL, 'p'},
5d970405 920 {"quiet", no_argument, NULL, 'q'},
33f0f67d 921 {"timestamp", no_argument, NULL, 't'},
80082f1e 922 {"toggle", no_argument, NULL, 'T'},
de6f8362
CF
923 {"trace-children", no_argument, NULL, 'f'},
924 {"translate-error-numbers", no_argument, NULL, 'n'},
925 {"usecs", no_argument, NULL, 'u'},
926 {"version", no_argument, NULL, 'v'},
33f0f67d
CV
927 {NULL, 0, NULL, 0}
928};
929
5d970405 930static const char *const opts = "+b:dhHfm:no:p:qS:tTuvw";
33f0f67d 931
ba692ae9
CF
932static void
933print_version ()
934{
935 const char *v = strchr (version, ':');
936 int len;
937 if (!v)
938 {
939 v = "?";
940 len = 1;
941 }
942 else
943 {
944 v += 2;
945 len = strchr (v, ' ') - v;
946 }
947 printf ("\
948%s (cygwin) %.*s\n\
949System Trace\n\
90253b82 950Copyright 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.\n\
98467dae
JDF
951Compiled on %s\n\
952", pgm, len, v, __DATE__);
ba692ae9
CF
953}
954
1fd5e000 955int
eedc36cb 956main (int argc, char **argv)
1fd5e000
CF
957{
958 unsigned mask = 0;
959 FILE *ofile = NULL;
80082f1e 960 pid_t pid = 0;
1fd5e000 961 int opt;
b841df79 962 int toggle = 0;
5d970405 963 int sawquiet = -1;
1fd5e000 964
26c07f70
CF
965 if (load_cygwin ())
966 {
967 char **av = (char **) cygwin_internal (CW_ARGV);
cba3b11f 968 if (av && (DWORD) av != (DWORD) -1)
26c07f70
CF
969 for (argc = 0, argv = av; *av; av++)
970 argc++;
971 }
972
1fd5e000
CF
973 if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/')))
974 pgm = *argv;
975 else
976 pgm++;
977
33f0f67d 978 while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
1fd5e000
CF
979 switch (opt)
980 {
de6f8362
CF
981 case 'b':
982 bufsize = atoi (optarg);
33f0f67d 983 break;
de6f8362
CF
984 case 'd':
985 delta ^= 1;
33f0f67d 986 break;
1fd5e000
CF
987 case 'f':
988 forkdebug ^= 1;
989 break;
de6f8362
CF
990 case 'h':
991 // Print help and exit
ba692ae9 992 usage (stdout);
3c952fed 993 break;
459a9561
CF
994 case 'H':
995 include_hex ^= 1;
996 break;
1fd5e000 997 case 'm':
33f0f67d
CV
998 {
999 char *endptr;
1000 mask = parse_mask (optarg, &endptr);
1001 if (*endptr != '\0')
1002 {
1003 // Bad mask expression.
1004 error (0, "syntax error in mask expression \"%s\" near \
1005character #%d.\n", optarg, (int) (endptr - optarg), endptr);
1006 }
80082f1e 1007 break;
33f0f67d 1008 }
de6f8362
CF
1009 case 'n':
1010 numerror ^= 1;
1011 break;
1fd5e000 1012 case 'o':
8b800efe 1013 if ((ofile = fopen (cygpath (optarg, NULL), "wb")) == NULL)
1fd5e000
CF
1014 error (1, "can't open %s", optarg);
1015#ifdef F_SETFD
1016 (void) fcntl (fileno (ofile), F_SETFD, 0);
1017#endif
1018 break;
de6f8362 1019 case 'p':
7c518815 1020 pid = strtoul (optarg, NULL, 10);
5d970405
CF
1021 strace_active |= 2;
1022 break;
1023 case 'q':
1024 if (sawquiet < 0)
1025 sawquiet = 1;
1026 else
1027 sawquiet ^= 1;
de6f8362
CF
1028 break;
1029 case 'S':
7c518815 1030 flush_period = strtoul (optarg, NULL, 10);
78fed23c 1031 break;
1fd5e000 1032 case 't':
78fed23c
CF
1033 hhmmss ^= 1;
1034 break;
80082f1e
CF
1035 case 'T':
1036 toggle ^= 1;
1037 break;
78fed23c 1038 case 'u':
de6f8362 1039 // FIXME: currently unimplemented
801d6cc7
CF
1040 show_usecs ^= 1;
1041 delta ^= 1;
86fbc3d9 1042 break;
de6f8362
CF
1043 case 'v':
1044 // Print version info and exit
ba692ae9
CF
1045 print_version ();
1046 return 0;
86fbc3d9
ED
1047 case 'w':
1048 new_window ^= 1;
1049 break;
80082f1e
CF
1050 case '?':
1051 fprintf (stderr, "Try '%s --help' for more information.\n", pgm);
1052 exit (1);
1fd5e000
CF
1053 }
1054
80082f1e
CF
1055 if (pid && argv[optind])
1056 error (0, "cannot provide both a command line and a process id");
1057
1058 if (!pid && !argv[optind])
1059 error (0, "must provide either a command line or a process id");
1060
1061 if (toggle && !pid)
1062 error (0, "must provide a process id to toggle tracing");
ba692ae9 1063
5d970405
CF
1064 if (!pid)
1065 quiet = sawquiet < 0 || !sawquiet;
1066 else if (sawquiet < 0)
1067 quiet = 0;
1068 else
1069 quiet = sawquiet;
1070
1fd5e000 1071 if (!mask)
7c03f799 1072 mask = _STRACE_ALL;
1fd5e000 1073
3c952fed
CF
1074 if (bufsize)
1075 setvbuf (ofile, (char *) alloca (bufsize), _IOFBF, bufsize);
1076
1fd5e000
CF
1077 if (!ofile)
1078 ofile = stdout;
1079
6265ac10 1080 DWORD res = 0;
80082f1e
CF
1081 if (toggle)
1082 dotoggle (pid);
1083 else
6265ac10
CF
1084 res = dostrace (mask, ofile, pid, argv + optind);
1085 return res;
1fd5e000
CF
1086}
1087
1088#undef CloseHandle
1089
1090static BOOL
1091close_handle (HANDLE h, DWORD ok)
1092{
1093 child_list *c;
eedc36cb 1094 for (c = &children; (c = c->next) != NULL;)
1fd5e000
CF
1095 if (c->hproc == h && c->id != ok)
1096 error (0, "Closing child handle %p", h);
1097 return CloseHandle (h);
1098}
This page took 0.355211 seconds and 5 git commands to generate.