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