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