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