]>
Commit | Line | Data |
---|---|---|
ce637855 CV |
1 | /* strace.cc |
2 | ||
3 | Copyright 2000, 2001 Red Hat Inc. | |
4 | ||
5 | Written by Chris Faylor <cgf@redhat.com> | |
6 | ||
7 | This file is part of Cygwin. | |
8 | ||
9 | This software is a copyrighted work licensed under the terms of the | |
10 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
11 | details. */ | |
12 | ||
1fd5e000 CF |
13 | #include <stdio.h> |
14 | #include <fcntl.h> | |
15 | #include <getopt.h> | |
16 | #include <stdarg.h> | |
17 | #include <string.h> | |
18 | #include <stdlib.h> | |
86fbc3d9 | 19 | #include <time.h> |
1fd5e000 CF |
20 | #include <windows.h> |
21 | #include <signal.h> | |
22 | #include "sys/strace.h" | |
23 | ||
c6c9f06b CF |
24 | /* GCC runtime library's C++ EH code unfortunately pulls in stdio, and we |
25 | get undefine references to __impure_ptr, and hence the following | |
26 | hack. It should be reasonably safe however as long as this file | |
27 | is built using -mno-cygwin as is intended. */ | |
28 | int _impure_ptr; | |
29 | ||
b6e259b1 DD |
30 | /* we *know* we're being built with GCC */ |
31 | #define alloca __builtin_alloca | |
32 | ||
1fd5e000 CF |
33 | static const char *pgm; |
34 | static int forkdebug = 0; | |
78fed23c CF |
35 | static int numerror = 1; |
36 | static int usecs = 1; | |
37 | static int delta = 1; | |
38 | static int hhmmss = 0; | |
3c952fed | 39 | static int bufsize = 0; |
86fbc3d9 ED |
40 | static int new_window = 0; |
41 | static long flush_period = 0; | |
1fd5e000 CF |
42 | |
43 | static BOOL close_handle (HANDLE h, DWORD ok); | |
44 | ||
45 | #define CloseHandle(h) close_handle(h, 0) | |
46 | ||
47 | struct child_list | |
eedc36cb CF |
48 | { |
49 | DWORD id; | |
50 | HANDLE hproc; | |
51 | int saw_stars; | |
52 | char nfields; | |
53 | long long start_time; | |
54 | DWORD last_usecs; | |
55 | struct child_list *next; | |
56 | child_list ():id (0), hproc (NULL), saw_stars (0), nfields (0), | |
57 | start_time (0), last_usecs (0), next (NULL) | |
1fd5e000 | 58 | { |
eedc36cb CF |
59 | } |
60 | }; | |
1fd5e000 | 61 | |
22fe0c41 | 62 | child_list children; |
1fd5e000 CF |
63 | |
64 | static void | |
65 | warn (int geterrno, const char *fmt, ...) | |
66 | { | |
67 | va_list args; | |
68 | char buf[4096]; | |
69 | ||
70 | va_start (args, fmt); | |
71 | sprintf (buf, "%s: ", pgm); | |
72 | vsprintf (strchr (buf, '\0'), fmt, args); | |
73 | if (geterrno) | |
74 | perror (buf); | |
75 | else | |
76 | { | |
77 | fputs (buf, stderr); | |
78 | fputs ("\n", stderr); | |
79 | } | |
80 | } | |
81 | ||
82 | static void __attribute__ ((noreturn)) | |
83 | error (int geterrno, const char *fmt, ...) | |
84 | { | |
85 | va_list args; | |
86 | char buf[4096]; | |
87 | ||
88 | va_start (args, fmt); | |
89 | sprintf (buf, "%s: ", pgm); | |
90 | vsprintf (strchr (buf, '\0'), fmt, args); | |
91 | if (geterrno) | |
92 | perror (buf); | |
93 | else | |
94 | { | |
95 | fputs (buf, stderr); | |
96 | fputs ("\n", stderr); | |
97 | } | |
98 | ExitProcess (1); | |
99 | } | |
100 | ||
101 | DWORD lastid = 0; | |
102 | HANDLE lasth; | |
103 | ||
104 | #define PROCFLAGS \ | |
eedc36cb | 105 | PROCESS_ALL_ACCESS /*(PROCESS_DUP_HANDLE | PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_VM_WRITE) */ |
1fd5e000 CF |
106 | static void |
107 | add_child (DWORD id, HANDLE hproc) | |
108 | { | |
109 | child_list *c = children.next; | |
110 | children.next = new (child_list); | |
111 | children.next->next = c; | |
112 | lastid = children.next->id = id; | |
113 | HANDLE me = GetCurrentProcess (); | |
aaf0dee2 | 114 | lasth = children.next->hproc = hproc; |
1fd5e000 CF |
115 | } |
116 | ||
22fe0c41 CF |
117 | static child_list * |
118 | get_child (DWORD id) | |
1fd5e000 CF |
119 | { |
120 | child_list *c; | |
eedc36cb | 121 | for (c = &children; (c = c->next) != NULL;) |
1fd5e000 | 122 | if (c->id == id) |
22fe0c41 | 123 | return c; |
1fd5e000 CF |
124 | |
125 | error (0, "no process id %d found", id); | |
126 | } | |
127 | ||
128 | static void | |
129 | remove_child (DWORD id) | |
130 | { | |
131 | child_list *c; | |
132 | if (id == lastid) | |
133 | lastid = 0; | |
134 | for (c = &children; c->next != NULL; c = c->next) | |
135 | if (c->next->id == id) | |
136 | { | |
137 | child_list *c1 = c->next; | |
1fd5e000 CF |
138 | c->next = c1->next; |
139 | delete c1; | |
140 | return; | |
141 | } | |
142 | ||
143 | error (0, "no process id %d found", id); | |
144 | } | |
145 | ||
146 | #define LINE_BUF_CHUNK 128 | |
147 | ||
148 | class linebuf | |
149 | { | |
150 | size_t alloc; | |
151 | public: | |
eedc36cb | 152 | size_t ix; |
1fd5e000 CF |
153 | char *buf; |
154 | linebuf () | |
155 | { | |
156 | ix = 0; | |
157 | alloc = 0; | |
158 | buf = NULL; | |
159 | } | |
eedc36cb CF |
160 | ~linebuf () |
161 | { | |
162 | if (buf) | |
163 | free (buf); | |
164 | } | |
1fd5e000 | 165 | void add (const char *what, int len); |
eedc36cb CF |
166 | void add (const char *what) |
167 | { | |
168 | add (what, strlen (what)); | |
169 | } | |
1fd5e000 CF |
170 | void prepend (const char *what, int len); |
171 | }; | |
172 | ||
173 | void | |
174 | linebuf::add (const char *what, int len) | |
175 | { | |
176 | size_t newix; | |
177 | if ((newix = ix + len) >= alloc) | |
178 | { | |
179 | alloc += LINE_BUF_CHUNK + len; | |
180 | buf = (char *) realloc (buf, alloc + 1); | |
181 | } | |
182 | memcpy (buf + ix, what, len); | |
183 | ix = newix; | |
184 | buf[ix] = '\0'; | |
185 | } | |
186 | ||
187 | void | |
188 | linebuf::prepend (const char *what, int len) | |
189 | { | |
190 | int buflen; | |
191 | size_t newix; | |
192 | if ((newix = ix + len) >= alloc) | |
193 | { | |
194 | alloc += LINE_BUF_CHUNK + len; | |
195 | buf = (char *) realloc (buf, alloc + 1); | |
196 | buf[ix] = '\0'; | |
197 | } | |
198 | if ((buflen = strlen (buf))) | |
eedc36cb | 199 | memmove (buf + len, buf, buflen + 1); |
1fd5e000 | 200 | else |
eedc36cb | 201 | buf[newix] = '\0'; |
1fd5e000 CF |
202 | memcpy (buf, what, len); |
203 | ix = newix; | |
204 | } | |
205 | ||
206 | static void | |
eedc36cb | 207 | make_command_line (linebuf & one_line, char **argv) |
1fd5e000 CF |
208 | { |
209 | for (; *argv; argv++) | |
210 | { | |
211 | char *p = NULL; | |
212 | const char *a = *argv; | |
213 | ||
214 | int len = strlen (a); | |
215 | if (len != 0 && !(p = strpbrk (a, " \t\n\r\""))) | |
216 | one_line.add (a, len); | |
217 | else | |
218 | { | |
219 | one_line.add ("\"", 1); | |
220 | for (; p; a = p, p = strchr (p, '"')) | |
221 | { | |
222 | one_line.add (a, ++p - a); | |
223 | if (p[-1] == '"') | |
224 | one_line.add ("\"", 1); | |
225 | } | |
226 | if (*a) | |
227 | one_line.add (a); | |
228 | one_line.add ("\"", 1); | |
229 | } | |
230 | one_line.add (" ", 1); | |
231 | } | |
232 | ||
233 | if (one_line.ix) | |
234 | one_line.buf[one_line.ix - 1] = '\0'; | |
235 | else | |
236 | one_line.add ("", 1); | |
237 | } | |
238 | ||
239 | static DWORD child_pid; | |
240 | ||
241 | static BOOL WINAPI | |
78fed23c | 242 | ctrl_c (DWORD) |
1fd5e000 CF |
243 | { |
244 | static int tic = 1; | |
245 | if ((tic ^= 1) && !GenerateConsoleCtrlEvent (CTRL_C_EVENT, 0)) | |
246 | error (0, "couldn't send CTRL-C to child, win32 error %d\n", | |
247 | GetLastError ()); | |
248 | return TRUE; | |
249 | } | |
250 | ||
251 | static void | |
252 | create_child (char **argv) | |
253 | { | |
254 | linebuf one_line; | |
255 | ||
256 | STARTUPINFO si; | |
257 | PROCESS_INFORMATION pi; | |
258 | BOOL ret; | |
259 | DWORD flags; | |
260 | ||
261 | if (!*argv) | |
262 | error (0, "no program argument specified"); | |
263 | ||
264 | memset (&si, 0, sizeof (si)); | |
265 | si.cb = sizeof (si); | |
266 | ||
eedc36cb | 267 | /* cygwin32_conv_to_win32_path (exec_file, real_path); */ |
1fd5e000 CF |
268 | |
269 | flags = forkdebug ? 0 : DEBUG_ONLY_THIS_PROCESS; | |
86fbc3d9 ED |
270 | flags |= CREATE_DEFAULT_ERROR_MODE | DEBUG_PROCESS; |
271 | flags |= (new_window ? CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP : 0); | |
1fd5e000 CF |
272 | |
273 | make_command_line (one_line, argv); | |
274 | ||
275 | SetConsoleCtrlHandler (NULL, 0); | |
eedc36cb | 276 | ret = CreateProcess (0, one_line.buf, /* command line */ |
1fd5e000 CF |
277 | NULL, /* Security */ |
278 | NULL, /* thread */ | |
279 | TRUE, /* inherit handles */ | |
280 | flags, /* start flags */ | |
eedc36cb CF |
281 | NULL, NULL, /* current directory */ |
282 | &si, &pi); | |
1fd5e000 | 283 | if (!ret) |
eedc36cb CF |
284 | error (0, "error creating process %s, (error %d)", *argv, |
285 | GetLastError ()); | |
1fd5e000 CF |
286 | |
287 | CloseHandle (pi.hThread); | |
288 | CloseHandle (pi.hProcess); | |
289 | child_pid = pi.dwProcessId; | |
290 | SetConsoleCtrlHandler (ctrl_c, 1); | |
291 | } | |
292 | ||
293 | static int | |
294 | output_winerror (FILE *ofile, char *s) | |
295 | { | |
296 | char *winerr = strstr (s, "Win32 error "); | |
297 | if (!winerr) | |
298 | return 0; | |
299 | ||
300 | DWORD errnum = atoi (winerr + sizeof ("Win32 error ") - 1); | |
301 | if (!errnum) | |
302 | return 0; | |
303 | ||
304 | /* | |
305 | * NOTE: Currently there is no policy for how long the | |
306 | * the buffers are, and looks like 256 is a smallest one | |
307 | * (dlfcn.cc). Other than error 1395 (length 213) and | |
308 | * error 1015 (length 249), the rest are all under 188 | |
309 | * characters, and so I'll use 189 as the buffer length. | |
310 | * For those longer error messages, FormatMessage will | |
311 | * return FALSE, and we'll get the old behaviour such as | |
312 | * ``Win32 error 1395'' etc. | |
313 | */ | |
314 | char buf[4096]; | |
315 | if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | |
316 | | FORMAT_MESSAGE_IGNORE_INSERTS, | |
317 | NULL, | |
318 | errnum, | |
319 | MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), | |
eedc36cb | 320 | (LPTSTR) buf, sizeof (buf), NULL)) |
1fd5e000 CF |
321 | return 0; |
322 | ||
323 | /* Get rid the trailing CR/NL pair. */ | |
324 | char *p = strchr (buf, '\0'); | |
325 | p[-2] = '\n'; | |
326 | p[-1] = '\0'; | |
327 | ||
328 | *winerr = '\0'; | |
329 | fputs (s, ofile); | |
330 | fputs (buf, ofile); | |
331 | return 1; | |
332 | } | |
333 | ||
78fed23c CF |
334 | static SYSTEMTIME * |
335 | syst (long long t) | |
336 | { | |
337 | FILETIME n; | |
338 | static SYSTEMTIME st; | |
339 | long long now = t + ((long long) usecs * 10); | |
340 | n.dwHighDateTime = now >> 32; | |
341 | n.dwLowDateTime = now & 0xffffffff; | |
342 | FileTimeToSystemTime (&n, &st); | |
343 | return &st; | |
344 | } | |
345 | ||
1fd5e000 CF |
346 | static void __stdcall |
347 | handle_output_debug_string (DWORD id, LPVOID p, unsigned mask, FILE *ofile) | |
348 | { | |
349 | int len; | |
350 | int special; | |
351 | char alen[3 + 8 + 1]; | |
352 | DWORD nbytes; | |
22fe0c41 CF |
353 | child_list *child = get_child (id); |
354 | HANDLE hchild = child->hproc; | |
eedc36cb | 355 | #define INTROLEN (sizeof (alen) - 1) |
1fd5e000 CF |
356 | |
357 | if (id == lastid && hchild != lasth) | |
358 | warn (0, "%p != %p", hchild, lasth); | |
359 | ||
360 | alen[INTROLEN] = '\0'; | |
361 | if (!ReadProcessMemory (hchild, p, alen, INTROLEN, &nbytes)) | |
362 | #ifndef DEBUGGING | |
363 | return; | |
364 | #else | |
eedc36cb CF |
365 | error (0, |
366 | "couldn't get message length from subprocess %d<%p>, windows error %d", | |
1fd5e000 CF |
367 | id, hchild, GetLastError ()); |
368 | #endif | |
369 | ||
370 | if (strncmp (alen, "cYg", 3)) | |
371 | return; | |
372 | len = (int) strtoul (alen + 3, NULL, 16); | |
373 | if (!len) | |
374 | return; | |
375 | ||
376 | if (len > 0) | |
377 | special = 0; | |
378 | else | |
379 | { | |
380 | special = len; | |
381 | if (special == _STRACE_INTERFACE_ACTIVATE_ADDR) | |
382 | len = 17; | |
383 | } | |
78fed23c CF |
384 | |
385 | char *buf; | |
386 | buf = (char *) alloca (len + 65) + 10; | |
1fd5e000 CF |
387 | |
388 | if (!ReadProcessMemory (hchild, ((char *) p) + INTROLEN, buf, len, &nbytes)) | |
389 | error (0, "couldn't get message from subprocess, windows error %d", | |
390 | GetLastError ()); | |
391 | ||
392 | buf[len] = '\0'; | |
393 | char *s = strtok (buf, " "); | |
394 | ||
395 | unsigned n = strtoul (s, NULL, 16); | |
396 | ||
397 | s = strchr (s, '\0') + 1; | |
398 | ||
399 | if (special == _STRACE_INTERFACE_ACTIVATE_ADDR) | |
400 | { | |
401 | DWORD new_flag = 1; | |
402 | if (!WriteProcessMemory (hchild, (LPVOID) n, &new_flag, | |
eedc36cb CF |
403 | sizeof (new_flag), &nbytes)) |
404 | error (0, | |
405 | "couldn't write strace flag to subprocess, windows error %d", | |
1fd5e000 CF |
406 | GetLastError ()); |
407 | return; | |
408 | } | |
409 | ||
78fed23c CF |
410 | char *origs = s; |
411 | ||
1fd5e000 | 412 | if (mask & n) |
eedc36cb | 413 | /* got it */ ; |
1fd5e000 | 414 | else if (!(mask & _STRACE_ALL) || (n & _STRACE_NOTALL)) |
eedc36cb | 415 | return; /* This should not be included in "all" output */ |
1fd5e000 | 416 | |
78fed23c CF |
417 | DWORD dusecs, usecs; |
418 | char *ptusec, *ptrest; | |
419 | ||
420 | dusecs = strtoul (s, &ptusec, 10); | |
421 | char *q = ptusec; | |
422 | while (*q == ' ') | |
423 | q++; | |
424 | if (*q != '[') | |
425 | { | |
426 | usecs = strtoul (q, &ptrest, 10); | |
427 | while (*ptrest == ' ') | |
428 | ptrest++; | |
429 | } | |
430 | else | |
431 | { | |
432 | ptrest = q; | |
433 | ptusec = s; | |
434 | usecs = dusecs; | |
435 | } | |
436 | ||
22fe0c41 | 437 | if (child->saw_stars == 0) |
78fed23c CF |
438 | { |
439 | FILETIME st; | |
440 | char *news; | |
441 | ||
442 | GetSystemTimeAsFileTime (&st); | |
443 | FileTimeToLocalFileTime (&st, &st); | |
22fe0c41 CF |
444 | child->start_time = st.dwHighDateTime; |
445 | child->start_time <<= 32; | |
446 | child->start_time |= st.dwLowDateTime; | |
78fed23c | 447 | if (*(news = ptrest) != '[') |
22fe0c41 | 448 | child->saw_stars = 2; |
78fed23c CF |
449 | else |
450 | { | |
22fe0c41 | 451 | child->saw_stars++; |
78fed23c | 452 | while ((news = strchr (news, ' ')) != NULL && *++news != '*') |
22fe0c41 | 453 | child->nfields++; |
78fed23c | 454 | if (news == NULL) |
22fe0c41 | 455 | child->saw_stars++; |
78fed23c CF |
456 | else |
457 | { | |
458 | s = news; | |
22fe0c41 | 459 | child->nfields++; |
78fed23c CF |
460 | } |
461 | } | |
462 | } | |
22fe0c41 | 463 | else if (child->saw_stars < 2) |
78fed23c CF |
464 | { |
465 | int i; | |
466 | char *news; | |
467 | if (*(news = ptrest) != '[') | |
22fe0c41 | 468 | child->saw_stars = 2; |
78fed23c CF |
469 | else |
470 | { | |
22fe0c41 | 471 | for (i = 0; i < child->nfields; i++) |
78fed23c | 472 | if ((news = strchr (news, ' ')) == NULL) |
eedc36cb | 473 | break; // Should never happen |
78fed23c CF |
474 | else |
475 | news++; | |
476 | ||
477 | if (news == NULL) | |
22fe0c41 | 478 | child->saw_stars = 2; |
78fed23c CF |
479 | else |
480 | { | |
481 | s = news; | |
482 | if (*s == '*') | |
483 | { | |
22fe0c41 | 484 | SYSTEMTIME *st = syst (child->start_time); |
eedc36cb CF |
485 | fprintf (ofile, |
486 | "Date/Time: %d-%02d-%02d %02d:%02d:%02d\n", | |
487 | st->wYear, st->wMonth, st->wDay, st->wHour, | |
488 | st->wMinute, st->wSecond); | |
22fe0c41 | 489 | child->saw_stars++; |
78fed23c CF |
490 | } |
491 | } | |
492 | } | |
493 | } | |
494 | ||
22fe0c41 | 495 | long long d = usecs - child->last_usecs; |
78fed23c CF |
496 | char intbuf[40]; |
497 | ||
22fe0c41 | 498 | if (child->saw_stars < 2 || s != origs) |
eedc36cb | 499 | /* Nothing */ ; |
78fed23c CF |
500 | else if (hhmmss) |
501 | { | |
502 | s = ptrest - 9; | |
22fe0c41 | 503 | SYSTEMTIME *st = syst (child->start_time + (long long) usecs * 10); |
78fed23c CF |
504 | sprintf (s, "%02d:%02d:%02d", st->wHour, st->wMinute, st->wSecond); |
505 | *strchr (s, '\0') = ' '; | |
506 | } | |
507 | else if (!delta) | |
508 | s = ptusec; | |
509 | else | |
510 | { | |
511 | s = ptusec; | |
512 | sprintf (intbuf, "%5d ", (int) d); | |
513 | int len = strlen (intbuf); | |
514 | ||
515 | memcpy ((s -= len), intbuf, len); | |
516 | } | |
517 | ||
22fe0c41 | 518 | child->last_usecs = usecs; |
78fed23c | 519 | if (numerror || !output_winerror (ofile, s)) |
1fd5e000 | 520 | fputs (s, ofile); |
3c952fed CF |
521 | if (!bufsize) |
522 | fflush (ofile); | |
1fd5e000 CF |
523 | } |
524 | ||
525 | static void | |
526 | proc_child (unsigned mask, FILE *ofile) | |
527 | { | |
528 | DEBUG_EVENT ev; | |
529 | int processes = 0; | |
86fbc3d9 ED |
530 | time_t cur_time, last_time; |
531 | ||
1fd5e000 | 532 | SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST); |
86fbc3d9 | 533 | last_time = time (NULL); |
1fd5e000 CF |
534 | while (1) |
535 | { | |
536 | BOOL debug_event = WaitForDebugEvent (&ev, 1000); | |
5e0d760f | 537 | DWORD status = DBG_CONTINUE; |
86fbc3d9 ED |
538 | |
539 | if (bufsize && flush_period > 0 && | |
540 | (cur_time = time (NULL)) >= last_time + flush_period) | |
541 | { | |
542 | last_time = cur_time; | |
543 | fflush (ofile); | |
544 | } | |
545 | ||
1fd5e000 CF |
546 | if (!debug_event) |
547 | continue; | |
548 | ||
549 | switch (ev.dwDebugEventCode) | |
550 | { | |
551 | case CREATE_PROCESS_DEBUG_EVENT: | |
552 | if (ev.u.CreateProcessInfo.hFile) | |
553 | CloseHandle (ev.u.CreateProcessInfo.hFile); | |
1fd5e000 CF |
554 | add_child (ev.dwProcessId, ev.u.CreateProcessInfo.hProcess); |
555 | processes++; | |
556 | break; | |
557 | ||
558 | case CREATE_THREAD_DEBUG_EVENT: | |
1fd5e000 CF |
559 | break; |
560 | ||
561 | case LOAD_DLL_DEBUG_EVENT: | |
562 | if (ev.u.LoadDll.hFile) | |
563 | CloseHandle (ev.u.LoadDll.hFile); | |
564 | break; | |
565 | ||
566 | case OUTPUT_DEBUG_STRING_EVENT: | |
567 | handle_output_debug_string (ev.dwProcessId, | |
568 | ev.u.DebugString.lpDebugStringData, | |
569 | mask, ofile); | |
570 | break; | |
571 | ||
572 | case EXIT_PROCESS_DEBUG_EVENT: | |
573 | remove_child (ev.dwProcessId); | |
574 | break; | |
5e0d760f | 575 | case EXCEPTION_DEBUG_EVENT: |
eedc36cb CF |
576 | if (ev.u.Exception.ExceptionRecord.ExceptionCode != |
577 | STATUS_BREAKPOINT) | |
5460fae7 CF |
578 | { |
579 | status = DBG_EXCEPTION_NOT_HANDLED; | |
580 | #if 0 | |
581 | fprintf (stderr, "exception %p at %p\n", | |
582 | ev.u.Exception.ExceptionRecord.ExceptionCode, | |
583 | ev.u.Exception.ExceptionRecord.ExceptionAddress); | |
584 | #endif | |
585 | } | |
5e0d760f | 586 | break; |
1fd5e000 | 587 | } |
5e0d760f | 588 | if (!ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId, status)) |
1fd5e000 CF |
589 | error (0, "couldn't continue debug event, windows error %d", |
590 | GetLastError ()); | |
591 | if (ev.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT && --processes == 0) | |
592 | break; | |
593 | } | |
594 | } | |
595 | ||
596 | static void | |
597 | dostrace (unsigned mask, FILE *ofile, char **argv) | |
598 | { | |
599 | create_child (argv); | |
600 | proc_child (mask, ofile); | |
601 | ||
602 | return; | |
603 | } | |
604 | ||
605 | int | |
eedc36cb | 606 | main (int argc, char **argv) |
1fd5e000 CF |
607 | { |
608 | unsigned mask = 0; | |
609 | FILE *ofile = NULL; | |
610 | int opt; | |
611 | ||
612 | if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/'))) | |
613 | pgm = *argv; | |
614 | else | |
615 | pgm++; | |
616 | ||
86fbc3d9 | 617 | while ((opt = getopt (argc, argv, "b:m:o:fndutwS:")) != EOF) |
1fd5e000 CF |
618 | switch (opt) |
619 | { | |
620 | case 'f': | |
621 | forkdebug ^= 1; | |
622 | break; | |
3c952fed CF |
623 | case 'b': |
624 | bufsize = atoi (optarg); | |
625 | break; | |
1fd5e000 CF |
626 | case 'm': |
627 | mask = strtoul (optarg, NULL, 16); | |
628 | break; | |
629 | case 'o': | |
630 | if ((ofile = fopen (optarg, "w")) == NULL) | |
631 | error (1, "can't open %s", optarg); | |
632 | #ifdef F_SETFD | |
633 | (void) fcntl (fileno (ofile), F_SETFD, 0); | |
634 | #endif | |
635 | break; | |
78fed23c CF |
636 | case 'n': |
637 | numerror ^= 1; | |
638 | break; | |
1fd5e000 | 639 | case 't': |
78fed23c CF |
640 | hhmmss ^= 1; |
641 | break; | |
642 | case 'd': | |
643 | delta ^= 1; | |
1fd5e000 | 644 | break; |
78fed23c CF |
645 | case 'u': |
646 | usecs ^= 1; | |
86fbc3d9 ED |
647 | break; |
648 | case 'w': | |
649 | new_window ^= 1; | |
650 | break; | |
651 | case 'S': | |
652 | flush_period = strtol (optarg, NULL, 10); | |
653 | break; | |
1fd5e000 CF |
654 | } |
655 | ||
656 | if (!mask) | |
657 | mask = 1; | |
658 | ||
3c952fed CF |
659 | if (bufsize) |
660 | setvbuf (ofile, (char *) alloca (bufsize), _IOFBF, bufsize); | |
661 | ||
1fd5e000 CF |
662 | if (!ofile) |
663 | ofile = stdout; | |
664 | ||
665 | dostrace (mask, ofile, argv + optind); | |
666 | } | |
667 | ||
668 | #undef CloseHandle | |
669 | ||
670 | static BOOL | |
671 | close_handle (HANDLE h, DWORD ok) | |
672 | { | |
673 | child_list *c; | |
eedc36cb | 674 | for (c = &children; (c = c->next) != NULL;) |
1fd5e000 CF |
675 | if (c->hproc == h && c->id != ok) |
676 | error (0, "Closing child handle %p", h); | |
677 | return CloseHandle (h); | |
678 | } |