]> sourceware.org Git - newlib-cygwin.git/blame - winsup/utils/strace.cc
* fhandler_raw.cc (fhandler_dev_raw::clear): Don't reset unit.
[newlib-cygwin.git] / winsup / utils / strace.cc
CommitLineData
ce637855
CV
1/* strace.cc
2
3 Copyright 2000, 2001 Red Hat Inc.
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
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. */
28int _impure_ptr;
29
b6e259b1
DD
30/* we *know* we're being built with GCC */
31#define alloca __builtin_alloca
32
1fd5e000
CF
33static const char *pgm;
34static int forkdebug = 0;
78fed23c
CF
35static int numerror = 1;
36static int usecs = 1;
37static int delta = 1;
38static int hhmmss = 0;
3c952fed 39static int bufsize = 0;
86fbc3d9
ED
40static int new_window = 0;
41static long flush_period = 0;
1fd5e000
CF
42
43static BOOL close_handle (HANDLE h, DWORD ok);
44
45#define CloseHandle(h) close_handle(h, 0)
46
47struct 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 62child_list children;
1fd5e000
CF
63
64static void
65warn (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
82static void __attribute__ ((noreturn))
83error (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
101DWORD lastid = 0;
102HANDLE lasth;
103
104#define PROCFLAGS \
eedc36cb 105 PROCESS_ALL_ACCESS /*(PROCESS_DUP_HANDLE | PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_VM_WRITE) */
1fd5e000
CF
106static void
107add_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
117static child_list *
118get_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
128static void
129remove_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
148class linebuf
149{
150 size_t alloc;
151public:
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
173void
174linebuf::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
187void
188linebuf::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
206static void
eedc36cb 207make_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
239static DWORD child_pid;
240
241static BOOL WINAPI
78fed23c 242ctrl_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
251static void
252create_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
293static int
294output_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
334static SYSTEMTIME *
335syst (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
346static void __stdcall
347handle_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
525static void
526proc_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
596static void
597dostrace (unsigned mask, FILE *ofile, char **argv)
598{
599 create_child (argv);
600 proc_child (mask, ofile);
601
602 return;
603}
604
605int
eedc36cb 606main (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
670static BOOL
671close_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}
This page took 0.119546 seconds and 5 git commands to generate.