]> sourceware.org Git - newlib-cygwin.git/blame - winsup/utils/strace.cc
* dcrt0.cc (set_os_type): Record OS name string.
[newlib-cygwin.git] / winsup / utils / strace.cc
CommitLineData
1fd5e000
CF
1#include <stdio.h>
2#include <fcntl.h>
3#include <getopt.h>
4#include <stdarg.h>
5#include <string.h>
6#include <stdlib.h>
7#include <windows.h>
8#include <signal.h>
9#include "sys/strace.h"
10
11static const char *pgm;
12static int forkdebug = 0;
13static int texterror = 0;
14
15static BOOL close_handle (HANDLE h, DWORD ok);
16
17#define CloseHandle(h) close_handle(h, 0)
18
19struct child_list
20 {
21 DWORD id;
22 HANDLE hproc;
23 struct child_list *next;
24 };
25
26child_list children = {0};
27
28static void
29warn (int geterrno, const char *fmt, ...)
30{
31 va_list args;
32 char buf[4096];
33
34 va_start (args, fmt);
35 sprintf (buf, "%s: ", pgm);
36 vsprintf (strchr (buf, '\0'), fmt, args);
37 if (geterrno)
38 perror (buf);
39 else
40 {
41 fputs (buf, stderr);
42 fputs ("\n", stderr);
43 }
44}
45
46static void __attribute__ ((noreturn))
47error (int geterrno, const char *fmt, ...)
48{
49 va_list args;
50 char buf[4096];
51
52 va_start (args, fmt);
53 sprintf (buf, "%s: ", pgm);
54 vsprintf (strchr (buf, '\0'), fmt, args);
55 if (geterrno)
56 perror (buf);
57 else
58 {
59 fputs (buf, stderr);
60 fputs ("\n", stderr);
61 }
62 ExitProcess (1);
63}
64
65DWORD lastid = 0;
66HANDLE lasth;
67
68#define PROCFLAGS \
69 PROCESS_ALL_ACCESS /*(PROCESS_DUP_HANDLE | PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_VM_WRITE)*/
70static void
71add_child (DWORD id, HANDLE hproc)
72{
73 child_list *c = children.next;
74 children.next = new (child_list);
75 children.next->next = c;
76 lastid = children.next->id = id;
77 HANDLE me = GetCurrentProcess ();
78 if (!DuplicateHandle (me, hproc, me, &children.next->hproc, PROCFLAGS,
79 FALSE, DUPLICATE_CLOSE_SOURCE))
80 error (0, "couldn't duplicate %p,%p", me, hproc);
81 lasth = children.next->hproc;
82}
83
84static HANDLE
85get_child_handle (DWORD id)
86{
87 child_list *c;
88 for (c = &children; (c = c->next) != NULL; )
89 if (c->id == id)
90 return c->hproc;
91
92 error (0, "no process id %d found", id);
93}
94
95static void
96remove_child (DWORD id)
97{
98 child_list *c;
99 if (id == lastid)
100 lastid = 0;
101 for (c = &children; c->next != NULL; c = c->next)
102 if (c->next->id == id)
103 {
104 child_list *c1 = c->next;
105 close_handle (c1->hproc, id);
106 c->next = c1->next;
107 delete c1;
108 return;
109 }
110
111 error (0, "no process id %d found", id);
112}
113
114#define LINE_BUF_CHUNK 128
115
116class linebuf
117{
118 size_t alloc;
119public:
120 size_t ix;
121 char *buf;
122 linebuf ()
123 {
124 ix = 0;
125 alloc = 0;
126 buf = NULL;
127 }
128 ~linebuf () {if (buf) free (buf);}
129 void add (const char *what, int len);
130 void add (const char *what) {add (what, strlen (what));}
131 void prepend (const char *what, int len);
132};
133
134void
135linebuf::add (const char *what, int len)
136{
137 size_t newix;
138 if ((newix = ix + len) >= alloc)
139 {
140 alloc += LINE_BUF_CHUNK + len;
141 buf = (char *) realloc (buf, alloc + 1);
142 }
143 memcpy (buf + ix, what, len);
144 ix = newix;
145 buf[ix] = '\0';
146}
147
148void
149linebuf::prepend (const char *what, int len)
150{
151 int buflen;
152 size_t newix;
153 if ((newix = ix + len) >= alloc)
154 {
155 alloc += LINE_BUF_CHUNK + len;
156 buf = (char *) realloc (buf, alloc + 1);
157 buf[ix] = '\0';
158 }
159 if ((buflen = strlen (buf)))
160 memmove (buf + len, buf, buflen + 1);
161 else
162 buf[newix] = '\0';
163 memcpy (buf, what, len);
164 ix = newix;
165}
166
167static void
168make_command_line (linebuf& one_line, char **argv)
169{
170 for (; *argv; argv++)
171 {
172 char *p = NULL;
173 const char *a = *argv;
174
175 int len = strlen (a);
176 if (len != 0 && !(p = strpbrk (a, " \t\n\r\"")))
177 one_line.add (a, len);
178 else
179 {
180 one_line.add ("\"", 1);
181 for (; p; a = p, p = strchr (p, '"'))
182 {
183 one_line.add (a, ++p - a);
184 if (p[-1] == '"')
185 one_line.add ("\"", 1);
186 }
187 if (*a)
188 one_line.add (a);
189 one_line.add ("\"", 1);
190 }
191 one_line.add (" ", 1);
192 }
193
194 if (one_line.ix)
195 one_line.buf[one_line.ix - 1] = '\0';
196 else
197 one_line.add ("", 1);
198}
199
200static DWORD child_pid;
201
202static BOOL WINAPI
203ctrl_c (DWORD type)
204{
205 static int tic = 1;
206 if ((tic ^= 1) && !GenerateConsoleCtrlEvent (CTRL_C_EVENT, 0))
207 error (0, "couldn't send CTRL-C to child, win32 error %d\n",
208 GetLastError ());
209 return TRUE;
210}
211
212static void
213create_child (char **argv)
214{
215 linebuf one_line;
216
217 STARTUPINFO si;
218 PROCESS_INFORMATION pi;
219 BOOL ret;
220 DWORD flags;
221
222 if (!*argv)
223 error (0, "no program argument specified");
224
225 memset (&si, 0, sizeof (si));
226 si.cb = sizeof (si);
227
228 /* cygwin32_conv_to_win32_path (exec_file, real_path);*/
229
230 flags = forkdebug ? 0 : DEBUG_ONLY_THIS_PROCESS;
231 flags |= /*CREATE_NEW_PROCESS_GROUP | */CREATE_DEFAULT_ERROR_MODE | DEBUG_PROCESS;
232
233 make_command_line (one_line, argv);
234
235 SetConsoleCtrlHandler (NULL, 0);
236 ret = CreateProcess (0,
237 one_line.buf,/* command line */
238 NULL, /* Security */
239 NULL, /* thread */
240 TRUE, /* inherit handles */
241 flags, /* start flags */
242 NULL,
243 NULL, /* current directory */
244 &si,
245 &pi);
246 if (!ret)
247 error (0, "error creating process %s, (error %d)", *argv, GetLastError());
248
249 CloseHandle (pi.hThread);
250 CloseHandle (pi.hProcess);
251 child_pid = pi.dwProcessId;
252 SetConsoleCtrlHandler (ctrl_c, 1);
253}
254
255static int
256output_winerror (FILE *ofile, char *s)
257{
258 char *winerr = strstr (s, "Win32 error ");
259 if (!winerr)
260 return 0;
261
262 DWORD errnum = atoi (winerr + sizeof ("Win32 error ") - 1);
263 if (!errnum)
264 return 0;
265
266 /*
267 * NOTE: Currently there is no policy for how long the
268 * the buffers are, and looks like 256 is a smallest one
269 * (dlfcn.cc). Other than error 1395 (length 213) and
270 * error 1015 (length 249), the rest are all under 188
271 * characters, and so I'll use 189 as the buffer length.
272 * For those longer error messages, FormatMessage will
273 * return FALSE, and we'll get the old behaviour such as
274 * ``Win32 error 1395'' etc.
275 */
276 char buf[4096];
277 if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
278 | FORMAT_MESSAGE_IGNORE_INSERTS,
279 NULL,
280 errnum,
281 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
282 (LPTSTR) buf,
283 sizeof (buf),
284 NULL))
285 return 0;
286
287 /* Get rid the trailing CR/NL pair. */
288 char *p = strchr (buf, '\0');
289 p[-2] = '\n';
290 p[-1] = '\0';
291
292 *winerr = '\0';
293 fputs (s, ofile);
294 fputs (buf, ofile);
295 return 1;
296}
297
298static void __stdcall
299handle_output_debug_string (DWORD id, LPVOID p, unsigned mask, FILE *ofile)
300{
301 int len;
302 int special;
303 char alen[3 + 8 + 1];
304 DWORD nbytes;
305 HANDLE hchild = get_child_handle (id);
306 #define INTROLEN (sizeof (alen) - 1)
307
308 if (id == lastid && hchild != lasth)
309 warn (0, "%p != %p", hchild, lasth);
310
311 alen[INTROLEN] = '\0';
312 if (!ReadProcessMemory (hchild, p, alen, INTROLEN, &nbytes))
313#ifndef DEBUGGING
314 return;
315#else
316 error (0, "couldn't get message length from subprocess %d<%p>, windows error %d",
317 id, hchild, GetLastError ());
318#endif
319
320 if (strncmp (alen, "cYg", 3))
321 return;
322 len = (int) strtoul (alen + 3, NULL, 16);
323 if (!len)
324 return;
325
326 if (len > 0)
327 special = 0;
328 else
329 {
330 special = len;
331 if (special == _STRACE_INTERFACE_ACTIVATE_ADDR)
332 len = 17;
333 }
334
335 char *buf = (char *) alloca (len + 1);
336
337 if (!ReadProcessMemory (hchild, ((char *) p) + INTROLEN, buf, len, &nbytes))
338 error (0, "couldn't get message from subprocess, windows error %d",
339 GetLastError ());
340
341 buf[len] = '\0';
342 char *s = strtok (buf, " ");
343
344 unsigned n = strtoul (s, NULL, 16);
345
346 s = strchr (s, '\0') + 1;
347
348 if (special == _STRACE_INTERFACE_ACTIVATE_ADDR)
349 {
350 DWORD new_flag = 1;
351 if (!WriteProcessMemory (hchild, (LPVOID) n, &new_flag,
352 sizeof (new_flag), &nbytes))
353 error (0, "couldn't write strace flag to subprocess, windows error %d",
354 GetLastError ());
355 return;
356 }
357
358 if (mask & n)
359 /* got it */;
360 else if (!(mask & _STRACE_ALL) || (n & _STRACE_NOTALL))
361 return; /* This should not be included in "all" output */
362
363 if (!texterror || !output_winerror (ofile, s))
364 fputs (s, ofile);
365 fflush (ofile);
366}
367
368static void
369proc_child (unsigned mask, FILE *ofile)
370{
371 DEBUG_EVENT ev;
372 int processes = 0;
373 SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
374 while (1)
375 {
376 BOOL debug_event = WaitForDebugEvent (&ev, 1000);
377 if (!debug_event)
378 continue;
379
380 switch (ev.dwDebugEventCode)
381 {
382 case CREATE_PROCESS_DEBUG_EVENT:
383 if (ev.u.CreateProcessInfo.hFile)
384 CloseHandle (ev.u.CreateProcessInfo.hFile);
385 if (ev.u.CreateProcessInfo.hThread)
386 CloseHandle (ev.u.CreateProcessInfo.hThread);
387 add_child (ev.dwProcessId, ev.u.CreateProcessInfo.hProcess);
388 processes++;
389 break;
390
391 case CREATE_THREAD_DEBUG_EVENT:
392 if (ev.u.CreateThread.hThread)
393 CloseHandle (ev.u.CreateThread.hThread);
394 break;
395
396 case LOAD_DLL_DEBUG_EVENT:
397 if (ev.u.LoadDll.hFile)
398 CloseHandle (ev.u.LoadDll.hFile);
399 break;
400
401 case OUTPUT_DEBUG_STRING_EVENT:
402 handle_output_debug_string (ev.dwProcessId,
403 ev.u.DebugString.lpDebugStringData,
404 mask, ofile);
405 break;
406
407 case EXIT_PROCESS_DEBUG_EVENT:
408 remove_child (ev.dwProcessId);
409 break;
410 }
411 if (!ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId,
412 DBG_CONTINUE))
413 error (0, "couldn't continue debug event, windows error %d",
414 GetLastError ());
415 if (ev.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT && --processes == 0)
416 break;
417 }
418}
419
420static void
421dostrace (unsigned mask, FILE *ofile, char **argv)
422{
423 create_child (argv);
424 proc_child (mask, ofile);
425
426 return;
427}
428
429int
430main(int argc, char **argv)
431{
432 unsigned mask = 0;
433 FILE *ofile = NULL;
434 int opt;
435
436 if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/')))
437 pgm = *argv;
438 else
439 pgm++;
440
441 while ((opt = getopt (argc, argv, "m:o:ft")) != EOF)
442 switch (opt)
443 {
444 case 'f':
445 forkdebug ^= 1;
446 break;
447 case 'm':
448 mask = strtoul (optarg, NULL, 16);
449 break;
450 case 'o':
451 if ((ofile = fopen (optarg, "w")) == NULL)
452 error (1, "can't open %s", optarg);
453#ifdef F_SETFD
454 (void) fcntl (fileno (ofile), F_SETFD, 0);
455#endif
456 break;
457 case 't':
458 texterror ^= 1;
459 break;
460 }
461
462 if (!mask)
463 mask = 1;
464
465 if (!ofile)
466 ofile = stdout;
467
468 dostrace (mask, ofile, argv + optind);
469}
470
471#undef CloseHandle
472
473static BOOL
474close_handle (HANDLE h, DWORD ok)
475{
476 child_list *c;
477 for (c = &children; (c = c->next) != NULL; )
478 if (c->hproc == h && c->id != ok)
479 error (0, "Closing child handle %p", h);
480 return CloseHandle (h);
481}
This page took 0.06445 seconds and 5 git commands to generate.