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