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