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