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