]> sourceware.org Git - newlib-cygwin.git/blame - winsup/utils/strace.cc
* Makefile.in: Add -c option which is now removed from COMPILE_C*.
[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)
134 fprintf (stderr, "Windows process %d attached\n", id);
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
CF
150 if (!quiet)
151 fprintf (stderr, "Windows process %d detached\n", id);
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);
eedc36cb 329 ret = CreateProcess (0, one_line.buf, /* command line */
1fd5e000
CF
330 NULL, /* Security */
331 NULL, /* thread */
332 TRUE, /* inherit handles */
333 flags, /* start flags */
eedc36cb
CF
334 NULL, NULL, /* current directory */
335 &si, &pi);
1fd5e000 336 if (!ret)
eedc36cb
CF
337 error (0, "error creating process %s, (error %d)", *argv,
338 GetLastError ());
1fd5e000
CF
339
340 CloseHandle (pi.hThread);
341 CloseHandle (pi.hProcess);
342 child_pid = pi.dwProcessId;
343 SetConsoleCtrlHandler (ctrl_c, 1);
344}
345
346static int
347output_winerror (FILE *ofile, char *s)
348{
349 char *winerr = strstr (s, "Win32 error ");
350 if (!winerr)
351 return 0;
352
353 DWORD errnum = atoi (winerr + sizeof ("Win32 error ") - 1);
354 if (!errnum)
355 return 0;
356
357 /*
358 * NOTE: Currently there is no policy for how long the
359 * the buffers are, and looks like 256 is a smallest one
360 * (dlfcn.cc). Other than error 1395 (length 213) and
361 * error 1015 (length 249), the rest are all under 188
362 * characters, and so I'll use 189 as the buffer length.
363 * For those longer error messages, FormatMessage will
364 * return FALSE, and we'll get the old behaviour such as
365 * ``Win32 error 1395'' etc.
366 */
367 char buf[4096];
368 if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
369 | FORMAT_MESSAGE_IGNORE_INSERTS,
370 NULL,
371 errnum,
372 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
eedc36cb 373 (LPTSTR) buf, sizeof (buf), NULL))
1fd5e000
CF
374 return 0;
375
376 /* Get rid the trailing CR/NL pair. */
377 char *p = strchr (buf, '\0');
378 p[-2] = '\n';
379 p[-1] = '\0';
380
381 *winerr = '\0';
382 fputs (s, ofile);
383 fputs (buf, ofile);
384 return 1;
385}
386
78fed23c
CF
387static SYSTEMTIME *
388syst (long long t)
389{
390 FILETIME n;
391 static SYSTEMTIME st;
801d6cc7 392 long long now = t /*+ ((long long) usecs * 10)*/;
78fed23c
CF
393 n.dwHighDateTime = now >> 32;
394 n.dwLowDateTime = now & 0xffffffff;
395 FileTimeToSystemTime (&n, &st);
396 return &st;
397}
398
1fd5e000
CF
399static void __stdcall
400handle_output_debug_string (DWORD id, LPVOID p, unsigned mask, FILE *ofile)
401{
402 int len;
403 int special;
404 char alen[3 + 8 + 1];
405 DWORD nbytes;
22fe0c41 406 child_list *child = get_child (id);
5d970405
CF
407 if (!child)
408 error (0, "no process id %d found", id);
22fe0c41 409 HANDLE hchild = child->hproc;
eedc36cb 410#define INTROLEN (sizeof (alen) - 1)
1fd5e000
CF
411
412 if (id == lastid && hchild != lasth)
413 warn (0, "%p != %p", hchild, lasth);
414
415 alen[INTROLEN] = '\0';
416 if (!ReadProcessMemory (hchild, p, alen, INTROLEN, &nbytes))
417#ifndef DEBUGGING
418 return;
419#else
eedc36cb
CF
420 error (0,
421 "couldn't get message length from subprocess %d<%p>, windows error %d",
1fd5e000
CF
422 id, hchild, GetLastError ());
423#endif
424
425 if (strncmp (alen, "cYg", 3))
426 return;
427 len = (int) strtoul (alen + 3, NULL, 16);
428 if (!len)
429 return;
430
431 if (len > 0)
432 special = 0;
433 else
434 {
435 special = len;
5d970405 436 if (special == _STRACE_INTERFACE_ACTIVATE_ADDR || special == _STRACE_CHILD_PID)
1fd5e000
CF
437 len = 17;
438 }
78fed23c
CF
439
440 char *buf;
459a9561 441 buf = (char *) alloca (len + 85) + 20;
1fd5e000
CF
442
443 if (!ReadProcessMemory (hchild, ((char *) p) + INTROLEN, buf, len, &nbytes))
444 error (0, "couldn't get message from subprocess, windows error %d",
445 GetLastError ());
446
447 buf[len] = '\0';
448 char *s = strtok (buf, " ");
449
a593a4c5 450 unsigned long n = strtoul (s, NULL, 16);
1fd5e000
CF
451
452 s = strchr (s, '\0') + 1;
453
5d970405
CF
454 if (special == _STRACE_CHILD_PID)
455 {
456 if (!DebugActiveProcess (n))
457 error (0, "couldn't attach to subprocess %d for debugging, "
458 "windows error %d", n, GetLastError ());
459 return;
460 }
461
1fd5e000
CF
462 if (special == _STRACE_INTERFACE_ACTIVATE_ADDR)
463 {
5d970405
CF
464 if (!WriteProcessMemory (hchild, (LPVOID) n, &strace_active,
465 sizeof (strace_active), &nbytes))
466 error (0, "couldn't write strace flag to subprocess at %p, "
467 "windows error %d", n, GetLastError ());
1fd5e000
CF
468 return;
469 }
470
78fed23c
CF
471 char *origs = s;
472
1fd5e000 473 if (mask & n)
eedc36cb 474 /* got it */ ;
1fd5e000 475 else if (!(mask & _STRACE_ALL) || (n & _STRACE_NOTALL))
eedc36cb 476 return; /* This should not be included in "all" output */
1fd5e000 477
78fed23c
CF
478 DWORD dusecs, usecs;
479 char *ptusec, *ptrest;
480
481 dusecs = strtoul (s, &ptusec, 10);
482 char *q = ptusec;
483 while (*q == ' ')
484 q++;
485 if (*q != '[')
486 {
487 usecs = strtoul (q, &ptrest, 10);
488 while (*ptrest == ' ')
489 ptrest++;
490 }
491 else
492 {
493 ptrest = q;
801d6cc7 494 ptusec = show_usecs ? s : ptrest;
78fed23c
CF
495 usecs = dusecs;
496 }
497
22fe0c41 498 if (child->saw_stars == 0)
78fed23c
CF
499 {
500 FILETIME st;
501 char *news;
502
503 GetSystemTimeAsFileTime (&st);
504 FileTimeToLocalFileTime (&st, &st);
22fe0c41
CF
505 child->start_time = st.dwHighDateTime;
506 child->start_time <<= 32;
507 child->start_time |= st.dwLowDateTime;
78fed23c 508 if (*(news = ptrest) != '[')
22fe0c41 509 child->saw_stars = 2;
78fed23c
CF
510 else
511 {
22fe0c41 512 child->saw_stars++;
78fed23c 513 while ((news = strchr (news, ' ')) != NULL && *++news != '*')
22fe0c41 514 child->nfields++;
78fed23c 515 if (news == NULL)
22fe0c41 516 child->saw_stars++;
78fed23c
CF
517 else
518 {
519 s = news;
22fe0c41 520 child->nfields++;
78fed23c
CF
521 }
522 }
523 }
22fe0c41 524 else if (child->saw_stars < 2)
78fed23c
CF
525 {
526 int i;
527 char *news;
528 if (*(news = ptrest) != '[')
22fe0c41 529 child->saw_stars = 2;
78fed23c
CF
530 else
531 {
22fe0c41 532 for (i = 0; i < child->nfields; i++)
78fed23c 533 if ((news = strchr (news, ' ')) == NULL)
eedc36cb 534 break; // Should never happen
78fed23c
CF
535 else
536 news++;
537
538 if (news == NULL)
22fe0c41 539 child->saw_stars = 2;
78fed23c
CF
540 else
541 {
542 s = news;
543 if (*s == '*')
544 {
22fe0c41 545 SYSTEMTIME *st = syst (child->start_time);
eedc36cb
CF
546 fprintf (ofile,
547 "Date/Time: %d-%02d-%02d %02d:%02d:%02d\n",
548 st->wYear, st->wMonth, st->wDay, st->wHour,
549 st->wMinute, st->wSecond);
22fe0c41 550 child->saw_stars++;
78fed23c
CF
551 }
552 }
553 }
554 }
555
22fe0c41 556 long long d = usecs - child->last_usecs;
78fed23c
CF
557 char intbuf[40];
558
22fe0c41 559 if (child->saw_stars < 2 || s != origs)
eedc36cb 560 /* Nothing */ ;
78fed23c
CF
561 else if (hhmmss)
562 {
563 s = ptrest - 9;
22fe0c41 564 SYSTEMTIME *st = syst (child->start_time + (long long) usecs * 10);
78fed23c
CF
565 sprintf (s, "%02d:%02d:%02d", st->wHour, st->wMinute, st->wSecond);
566 *strchr (s, '\0') = ' ';
567 }
568 else if (!delta)
569 s = ptusec;
570 else
571 {
572 s = ptusec;
573 sprintf (intbuf, "%5d ", (int) d);
574 int len = strlen (intbuf);
575
576 memcpy ((s -= len), intbuf, len);
577 }
578
459a9561
CF
579 if (include_hex)
580 {
581 s -= 8;
a593a4c5 582 sprintf (s, "%p", (void *) n);
459a9561
CF
583 strchr (s, '\0')[0] = ' ';
584 }
22fe0c41 585 child->last_usecs = usecs;
78fed23c 586 if (numerror || !output_winerror (ofile, s))
1fd5e000 587 fputs (s, ofile);
3c952fed
CF
588 if (!bufsize)
589 fflush (ofile);
1fd5e000
CF
590}
591
6265ac10 592static DWORD
d73f9745 593proc_child (unsigned mask, FILE *ofile, pid_t pid)
1fd5e000 594{
6265ac10 595 DWORD res = 0;
1fd5e000 596 DEBUG_EVENT ev;
86fbc3d9
ED
597 time_t cur_time, last_time;
598
1fd5e000 599 SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
86fbc3d9 600 last_time = time (NULL);
1fd5e000
CF
601 while (1)
602 {
603 BOOL debug_event = WaitForDebugEvent (&ev, 1000);
5e0d760f 604 DWORD status = DBG_CONTINUE;
86fbc3d9
ED
605
606 if (bufsize && flush_period > 0 &&
607 (cur_time = time (NULL)) >= last_time + flush_period)
608 {
609 last_time = cur_time;
610 fflush (ofile);
611 }
612
1fd5e000
CF
613 if (!debug_event)
614 continue;
615
d73f9745
CF
616 if (pid)
617 {
618 (void) cygwin_internal (CW_STRACE_TOGGLE, pid);
619 pid = 0;
620 }
621
1fd5e000
CF
622 switch (ev.dwDebugEventCode)
623 {
624 case CREATE_PROCESS_DEBUG_EVENT:
625 if (ev.u.CreateProcessInfo.hFile)
626 CloseHandle (ev.u.CreateProcessInfo.hFile);
1fd5e000 627 add_child (ev.dwProcessId, ev.u.CreateProcessInfo.hProcess);
1fd5e000
CF
628 break;
629
630 case CREATE_THREAD_DEBUG_EVENT:
1fd5e000
CF
631 break;
632
633 case LOAD_DLL_DEBUG_EVENT:
634 if (ev.u.LoadDll.hFile)
635 CloseHandle (ev.u.LoadDll.hFile);
636 break;
637
638 case OUTPUT_DEBUG_STRING_EVENT:
639 handle_output_debug_string (ev.dwProcessId,
640 ev.u.DebugString.lpDebugStringData,
641 mask, ofile);
642 break;
643
644 case EXIT_PROCESS_DEBUG_EVENT:
6265ac10 645 res = ev.u.ExitProcess.dwExitCode >> 8;
daed3bec 646 remove_child (ev.dwProcessId);
1fd5e000 647 break;
5e0d760f 648 case EXCEPTION_DEBUG_EVENT:
5d970405 649 if (ev.u.Exception.ExceptionRecord.ExceptionCode != STATUS_BREAKPOINT)
5460fae7
CF
650 {
651 status = DBG_EXCEPTION_NOT_HANDLED;
5d970405
CF
652 if (ev.u.Exception.dwFirstChance)
653 fprintf (ofile, "--- Process %u, exception %p at %p\n", ev.dwProcessId,
654 ev.u.Exception.ExceptionRecord.ExceptionCode,
655 ev.u.Exception.ExceptionRecord.ExceptionAddress);
5460fae7 656 }
5e0d760f 657 break;
1fd5e000 658 }
5e0d760f 659 if (!ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId, status))
1fd5e000
CF
660 error (0, "couldn't continue debug event, windows error %d",
661 GetLastError ());
5d970405 662 if (!processes)
1fd5e000
CF
663 break;
664 }
6265ac10
CF
665
666 return res;
1fd5e000
CF
667}
668
80082f1e
CF
669static void
670dotoggle (pid_t pid)
671{
80082f1e
CF
672 child_pid = (DWORD) cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid);
673 if (!child_pid)
674 {
675 warn (0, "no such cygwin pid - %d", pid);
676 child_pid = pid;
677 }
678 if (cygwin_internal (CW_STRACE_TOGGLE, child_pid))
679 error (0, "failed to toggle tracing for process %d<%d>", pid, child_pid);
680
681 return;
682}
683
6265ac10 684static DWORD
de6f8362 685dostrace (unsigned mask, FILE *ofile, pid_t pid, char **argv)
1fd5e000 686{
de6f8362
CF
687 if (!pid)
688 create_child (argv);
689 else
690 attach_process (pid);
1fd5e000 691
6265ac10 692 return proc_child (mask, ofile, pid);
1fd5e000
CF
693}
694
33f0f67d
CV
695typedef struct tag_mask_mnemonic
696{
697 unsigned long val;
698 const char *text;
699}
700mask_mnemonic;
701
702static const mask_mnemonic mnemonic_table[] = {
703 {_STRACE_ALL, "all"},
704 {_STRACE_FLUSH, "flush"},
705 {_STRACE_INHERIT, "inherit"},
706 {_STRACE_UHOH, "uhoh"},
707 {_STRACE_SYSCALL, "syscall"},
708 {_STRACE_STARTUP, "startup"},
709 {_STRACE_DEBUG, "debug"},
710 {_STRACE_PARANOID, "paranoid"},
711 {_STRACE_TERMIOS, "termios"},
712 {_STRACE_SELECT, "select"},
713 {_STRACE_WM, "wm"},
714 {_STRACE_SIGP, "sigp"},
715 {_STRACE_MINIMAL, "minimal"},
716 {_STRACE_EXITDUMP, "exitdump"},
717 {_STRACE_SYSTEM, "system"},
718 {_STRACE_NOMUTEX, "nomutex"},
719 {_STRACE_MALLOC, "malloc"},
720 {_STRACE_THREAD, "thread"},
721 {0, NULL}
722};
723
724static unsigned long
725mnemonic2ul (const char *nptr, char **endptr)
726{
727 // Look up mnemonic in table, return value.
728 // *endptr = ptr to char that breaks match.
729 const mask_mnemonic *mnp = mnemonic_table;
730
731 while (mnp->text != NULL)
732 {
733 if (strcmp (mnp->text, nptr) == 0)
734 {
735 // Found a match.
736 if (endptr != NULL)
737 {
738 *endptr = ((char *) nptr) + strlen (mnp->text);
739 }
740 return mnp->val;
741 }
742 mnp++;
743 }
744
745 // Didn't find it.
746 if (endptr != NULL)
747 {
748 *endptr = (char *) nptr;
749 }
750 return 0;
751}
752
753static unsigned long
754parse_mask (const char *ms, char **endptr)
755{
756 const char *p = ms;
757 char *newp;
758 unsigned long retval = 0, thisval;
759 const size_t bufsize = 16;
760 char buffer[bufsize];
761 size_t len;
762
763 while (*p != '\0')
764 {
765 // First extract the term, terminate it, and lowercase it.
766 strncpy (buffer, p, bufsize);
767 buffer[bufsize - 1] = '\0';
768 len = strcspn (buffer, "+,\0");
769 buffer[len] = '\0';
770 strlwr (buffer);
771
772 // Check if this is a mnemonic. We have to do this first or strtoul()
773 // will false-trigger on anything starting with "a" through "f".
774 thisval = mnemonic2ul (buffer, &newp);
775 if (buffer == newp)
776 {
777 // This term isn't mnemonic, check if it's hex.
778 thisval = strtoul (buffer, &newp, 16);
779 if (newp != buffer + len)
780 {
781 // Not hex either, syntax error.
782 *endptr = (char *) p;
783 return 0;
784 }
785 }
786
787 p += len;
788 retval += thisval;
789
790 // Handle operators
791 if (*p == '\0')
792 break;
793 if ((*p == '+') || (*p == ','))
794 {
795 // For now these both equate to addition/ORing. Until we get
796 // fancy and add things like "all-<something>", all we need do is
797 // continue the looping.
798 p++;
799 continue;
800 }
801 else
802 {
803 // Syntax error
804 *endptr = (char *) p;
805 return 0;
806 }
807 }
808
809 *endptr = (char *) p;
810 return retval;
811}
812
813static void
ba692ae9 814usage (FILE *where = stderr)
33f0f67d 815{
ba692ae9
CF
816 fprintf (where, "\
817Usage: %s [OPTIONS] <command-line>\n\
80082f1e 818Usage: %s [OPTIONS] -p <pid>\n\
aa275fe0
JDF
819Trace system calls and signals\n\
820\n\
48f939e2
CF
821 -b, --buffer-size=SIZE set size of output file buffer\n\
822 -d, --no-delta don't display the delta-t microsecond timestamp\n\
f69af9b3 823 -f, --trace-children trace child processes (toggle - default true)\n\
ba692ae9 824 -h, --help output usage information and exit\n\
48f939e2 825 -m, --mask=MASK set message filter mask\n\
48f939e2
CF
826 -n, --crack-error-numbers output descriptive text instead of error\n\
827 numbers for Windows errors\n\
80082f1e
CF
828 -o, --output=FILENAME set output file to FILENAME\n\
829 -p, --pid=n attach to executing program with cygwin pid n\n\
99b08560 830 -q, --quiet suppress messages about attaching, detaching, etc.\n\
48f939e2 831 -S, --flush-period=PERIOD flush buffered strace output every PERIOD secs\n\
ba692ae9
CF
832 -t, --timestamp use an absolute hh:mm:ss timestamp insted of \n\
833 the default microsecond timestamp. Implies -d\n\
80082f1e
CF
834 -T, --toggle toggle tracing in a process already being\n\
835 traced. Requires -p <pid>\n\
801d6cc7 836 -u, --usecs toggle printing of microseconds timestamp\n\
ba692ae9 837 -v, --version output version information and exit\n\
48f939e2 838 -w, --new-window spawn program under test in a new window\n\
80082f1e 839\n", pgm, pgm);
ba692ae9
CF
840 if ( where == stdout)
841 fprintf (stdout, "\
33f0f67d
CV
842 MASK can be any combination of the following mnemonics and/or hex values\n\
843 (0x is optional). Combine masks with '+' or ',' like so:\n\
844\n\
845 --mask=wm+system,malloc+0x00800\n\
846\n\
847 Mnemonic Hex Corresponding Def Description\n\
848 =========================================================================\n\
849 all 0x00001 (_STRACE_ALL) All strace messages.\n\
850 flush 0x00002 (_STRACE_FLUSH) Flush output buffer after each message.\n\
851 inherit 0x00004 (_STRACE_INHERIT) Children inherit mask from parent.\n\
852 uhoh 0x00008 (_STRACE_UHOH) Unusual or weird phenomenon.\n\
853 syscall 0x00010 (_STRACE_SYSCALL) System calls.\n\
854 startup 0x00020 (_STRACE_STARTUP) argc/envp printout at startup.\n\
855 debug 0x00040 (_STRACE_DEBUG) Info to help debugging. \n\
856 paranoid 0x00080 (_STRACE_PARANOID) Paranoid info.\n\
857 termios 0x00100 (_STRACE_TERMIOS) Info for debugging termios stuff.\n\
858 select 0x00200 (_STRACE_SELECT) Info on ugly select internals.\n\
859 wm 0x00400 (_STRACE_WM) Trace Windows msgs (enable _strace_wm).\n\
860 sigp 0x00800 (_STRACE_SIGP) Trace signal and process handling.\n\
861 minimal 0x01000 (_STRACE_MINIMAL) Very minimal strace output.\n\
862 exitdump 0x04000 (_STRACE_EXITDUMP) Dump strace cache on exit.\n\
ba692ae9 863 system 0x08000 (_STRACE_SYSTEM) Serious error; goes to console and log.\n\
33f0f67d
CV
864 nomutex 0x10000 (_STRACE_NOMUTEX) Don't use mutex for synchronization.\n\
865 malloc 0x20000 (_STRACE_MALLOC) Trace malloc calls.\n\
866 thread 0x40000 (_STRACE_THREAD) Thread-locking calls.\n\
33f0f67d 867");
ba692ae9
CF
868 if (where == stderr)
869 fprintf (stderr, "Try '%s --help' for more information.\n", pgm);
870 exit (where == stderr ? 1 : 0 );
33f0f67d
CV
871}
872
873struct option longopts[] = {
33f0f67d 874 {"buffer-size", required_argument, NULL, 'b'},
de6f8362
CF
875 {"help", no_argument, NULL, 'h'},
876 {"flush-period", required_argument, NULL, 'S'},
459a9561 877 {"hex", no_argument, NULL, 'H'},
33f0f67d 878 {"mask", required_argument, NULL, 'm'},
de6f8362 879 {"new-window", no_argument, NULL, 'w'},
33f0f67d 880 {"output", required_argument, NULL, 'o'},
33f0f67d 881 {"no-delta", no_argument, NULL, 'd'},
de6f8362 882 {"pid", required_argument, NULL, 'p'},
5d970405 883 {"quiet", no_argument, NULL, 'q'},
33f0f67d 884 {"timestamp", no_argument, NULL, 't'},
80082f1e 885 {"toggle", no_argument, NULL, 'T'},
de6f8362
CF
886 {"trace-children", no_argument, NULL, 'f'},
887 {"translate-error-numbers", no_argument, NULL, 'n'},
888 {"usecs", no_argument, NULL, 'u'},
889 {"version", no_argument, NULL, 'v'},
33f0f67d
CV
890 {NULL, 0, NULL, 0}
891};
892
5d970405 893static const char *const opts = "+b:dhHfm:no:p:qS:tTuvw";
33f0f67d 894
ba692ae9
CF
895static void
896print_version ()
897{
898 const char *v = strchr (version, ':');
899 int len;
900 if (!v)
901 {
902 v = "?";
903 len = 1;
904 }
905 else
906 {
907 v += 2;
908 len = strchr (v, ' ') - v;
909 }
910 printf ("\
911%s (cygwin) %.*s\n\
912System Trace\n\
90253b82 913Copyright 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.\n\
98467dae
JDF
914Compiled on %s\n\
915", pgm, len, v, __DATE__);
ba692ae9
CF
916}
917
1fd5e000 918int
eedc36cb 919main (int argc, char **argv)
1fd5e000
CF
920{
921 unsigned mask = 0;
922 FILE *ofile = NULL;
80082f1e 923 pid_t pid = 0;
1fd5e000 924 int opt;
b841df79 925 int toggle = 0;
5d970405 926 int sawquiet = -1;
1fd5e000 927
26c07f70
CF
928 if (load_cygwin ())
929 {
930 char **av = (char **) cygwin_internal (CW_ARGV);
cba3b11f 931 if (av && (DWORD) av != (DWORD) -1)
26c07f70
CF
932 for (argc = 0, argv = av; *av; av++)
933 argc++;
934 }
935
1fd5e000
CF
936 if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/')))
937 pgm = *argv;
938 else
939 pgm++;
940
33f0f67d 941 while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
1fd5e000
CF
942 switch (opt)
943 {
de6f8362
CF
944 case 'b':
945 bufsize = atoi (optarg);
33f0f67d 946 break;
de6f8362
CF
947 case 'd':
948 delta ^= 1;
33f0f67d 949 break;
1fd5e000
CF
950 case 'f':
951 forkdebug ^= 1;
952 break;
de6f8362
CF
953 case 'h':
954 // Print help and exit
ba692ae9 955 usage (stdout);
3c952fed 956 break;
459a9561
CF
957 case 'H':
958 include_hex ^= 1;
959 break;
1fd5e000 960 case 'm':
33f0f67d
CV
961 {
962 char *endptr;
963 mask = parse_mask (optarg, &endptr);
964 if (*endptr != '\0')
965 {
966 // Bad mask expression.
967 error (0, "syntax error in mask expression \"%s\" near \
968character #%d.\n", optarg, (int) (endptr - optarg), endptr);
969 }
80082f1e 970 break;
33f0f67d 971 }
de6f8362
CF
972 case 'n':
973 numerror ^= 1;
974 break;
1fd5e000 975 case 'o':
229f2bb4 976 if ((ofile = fopen (cygpath (optarg, NULL), "w")) == NULL)
1fd5e000
CF
977 error (1, "can't open %s", optarg);
978#ifdef F_SETFD
979 (void) fcntl (fileno (ofile), F_SETFD, 0);
980#endif
981 break;
de6f8362 982 case 'p':
7c518815 983 pid = strtoul (optarg, NULL, 10);
5d970405
CF
984 strace_active |= 2;
985 break;
986 case 'q':
987 if (sawquiet < 0)
988 sawquiet = 1;
989 else
990 sawquiet ^= 1;
de6f8362
CF
991 break;
992 case 'S':
7c518815 993 flush_period = strtoul (optarg, NULL, 10);
78fed23c 994 break;
1fd5e000 995 case 't':
78fed23c
CF
996 hhmmss ^= 1;
997 break;
80082f1e
CF
998 case 'T':
999 toggle ^= 1;
1000 break;
78fed23c 1001 case 'u':
de6f8362 1002 // FIXME: currently unimplemented
801d6cc7
CF
1003 show_usecs ^= 1;
1004 delta ^= 1;
86fbc3d9 1005 break;
de6f8362
CF
1006 case 'v':
1007 // Print version info and exit
ba692ae9
CF
1008 print_version ();
1009 return 0;
86fbc3d9
ED
1010 case 'w':
1011 new_window ^= 1;
1012 break;
80082f1e
CF
1013 case '?':
1014 fprintf (stderr, "Try '%s --help' for more information.\n", pgm);
1015 exit (1);
1fd5e000
CF
1016 }
1017
80082f1e
CF
1018 if (pid && argv[optind])
1019 error (0, "cannot provide both a command line and a process id");
1020
1021 if (!pid && !argv[optind])
1022 error (0, "must provide either a command line or a process id");
1023
1024 if (toggle && !pid)
1025 error (0, "must provide a process id to toggle tracing");
ba692ae9 1026
5d970405
CF
1027 if (!pid)
1028 quiet = sawquiet < 0 || !sawquiet;
1029 else if (sawquiet < 0)
1030 quiet = 0;
1031 else
1032 quiet = sawquiet;
1033
1fd5e000 1034 if (!mask)
7c03f799 1035 mask = _STRACE_ALL;
1fd5e000 1036
3c952fed
CF
1037 if (bufsize)
1038 setvbuf (ofile, (char *) alloca (bufsize), _IOFBF, bufsize);
1039
1fd5e000
CF
1040 if (!ofile)
1041 ofile = stdout;
1042
6265ac10 1043 DWORD res = 0;
80082f1e
CF
1044 if (toggle)
1045 dotoggle (pid);
1046 else
6265ac10
CF
1047 res = dostrace (mask, ofile, pid, argv + optind);
1048 return res;
1fd5e000
CF
1049}
1050
1051#undef CloseHandle
1052
1053static BOOL
1054close_handle (HANDLE h, DWORD ok)
1055{
1056 child_list *c;
eedc36cb 1057 for (c = &children; (c = c->next) != NULL;)
1fd5e000
CF
1058 if (c->hproc == h && c->id != ok)
1059 error (0, "Closing child handle %p", h);
1060 return CloseHandle (h);
1061}
This page took 0.342593 seconds and 5 git commands to generate.