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