1 /* GLIB - Library of useful routines for C programming
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*
19 * Modified by the GLib Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GLib Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GLib at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25 /*
26 * MT safe ; except for g_on_error_stack_trace, but who wants thread safety
27 * then
28 */
29
30 #include "config.h"
31 #include "glibconfig.h"
32
33 #include <signal.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37
38 #ifdef HAVE_SYS_TIME_H
39 #include <sys/time.h>
40 #endif
41 #include <sys/types.h>
42
43 #include <time.h>
44
45 #ifdef G_OS_UNIX
46 #include <unistd.h>
47 #include <sys/wait.h>
48 #ifdef HAVE_SYS_SELECT_H
49 #include <sys/select.h>
50 #endif /* HAVE_SYS_SELECT_H */
51 #endif
52
53 #include <string.h>
54
55 #ifdef G_OS_WIN32
56 # define STRICT /* Strict typing, please */
57 # define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */
58 # include <windows.h>
59 # undef STRICT
60 #else
61 # include <fcntl.h>
62 #endif
63
64 #include "gbacktrace.h"
65
66 #include "gtypes.h"
67 #include "gmain.h"
68 #include "gprintfint.h"
69 #include "gunicode.h"
70 #include "gutils.h"
71
72 #ifndef G_OS_WIN32
73 static void stack_trace (const char * const *args);
74 #endif
75
76 /* Default to using LLDB for backtraces on macOS. */
77 #ifdef __APPLE__
78 #define USE_LLDB
79 #endif
80
81 #ifdef USE_LLDB
82 #define DEBUGGER "lldb"
83 #else
84 #define DEBUGGER "gdb"
85 #endif
86
87 /* People want to hit this from their debugger... */
88 GLIB_AVAILABLE_IN_ALL volatile gboolean glib_on_error_halt;
89 volatile gboolean glib_on_error_halt = TRUE;
90
91 /**
92 * g_on_error_query:
93 * @prg_name: the program name, needed by gdb for the "[S]tack trace"
94 * option. If @prg_name is %NULL, g_get_prgname() is called to get
95 * the program name (which will work correctly if gdk_init() or
96 * gtk_init() has been called)
97 *
98 * Prompts the user with
99 * `[E]xit, [H]alt, show [S]tack trace or [P]roceed`.
100 * This function is intended to be used for debugging use only.
101 * The following example shows how it can be used together with
102 * the g_log() functions.
103 *
104 * |[<!-- language="C" -->
105 * #include <glib.h>
106 *
107 * static void
108 * log_handler (const gchar *log_domain,
109 * GLogLevelFlags log_level,
110 * const gchar *message,
111 * gpointer user_data)
112 * {
113 * g_log_default_handler (log_domain, log_level, message, user_data);
114 *
115 * g_on_error_query (MY_PROGRAM_NAME);
116 * }
117 *
118 * int
119 * main (int argc, char *argv[])
120 * {
121 * g_log_set_handler (MY_LOG_DOMAIN,
122 * G_LOG_LEVEL_WARNING |
123 * G_LOG_LEVEL_ERROR |
124 * G_LOG_LEVEL_CRITICAL,
125 * log_handler,
126 * NULL);
127 * ...
128 * ]|
129 *
130 * If "[E]xit" is selected, the application terminates with a call
131 * to _exit(0).
132 *
133 * If "[S]tack" trace is selected, g_on_error_stack_trace() is called.
134 * This invokes gdb, which attaches to the current process and shows
135 * a stack trace. The prompt is then shown again.
136 *
137 * If "[P]roceed" is selected, the function returns.
138 *
139 * This function may cause different actions on non-UNIX platforms.
140 *
141 * On Windows consider using the `G_DEBUGGER` environment
142 * variable (see [Running GLib Applications](glib-running.html)) and
143 * calling g_on_error_stack_trace() instead.
144 */
145 void
g_on_error_query(const gchar * prg_name)146 g_on_error_query (const gchar *prg_name)
147 {
148 #ifndef G_OS_WIN32
149 static const gchar * const query1 = "[E]xit, [H]alt";
150 static const gchar * const query2 = ", show [S]tack trace";
151 static const gchar * const query3 = " or [P]roceed";
152 gchar buf[16];
153
154 if (!prg_name)
155 prg_name = g_get_prgname ();
156
157 retry:
158
159 if (prg_name)
160 _g_fprintf (stdout,
161 "%s (pid:%u): %s%s%s: ",
162 prg_name,
163 (guint) getpid (),
164 query1,
165 query2,
166 query3);
167 else
168 _g_fprintf (stdout,
169 "(process:%u): %s%s: ",
170 (guint) getpid (),
171 query1,
172 query3);
173 fflush (stdout);
174
175 if (isatty(0) && isatty(1))
176 fgets (buf, 8, stdin);
177 else
178 strcpy (buf, "E\n");
179
180 if ((buf[0] == 'E' || buf[0] == 'e')
181 && buf[1] == '\n')
182 _exit (0);
183 else if ((buf[0] == 'P' || buf[0] == 'p')
184 && buf[1] == '\n')
185 return;
186 else if (prg_name
187 && (buf[0] == 'S' || buf[0] == 's')
188 && buf[1] == '\n')
189 {
190 g_on_error_stack_trace (prg_name);
191 goto retry;
192 }
193 else if ((buf[0] == 'H' || buf[0] == 'h')
194 && buf[1] == '\n')
195 {
196 while (glib_on_error_halt)
197 ;
198 glib_on_error_halt = TRUE;
199 return;
200 }
201 else
202 goto retry;
203 #else
204 if (!prg_name)
205 prg_name = g_get_prgname ();
206
207 /* MessageBox is allowed on UWP apps only when building against
208 * the debug CRT, which will set -D_DEBUG */
209 #if defined(_DEBUG) || !defined(G_WINAPI_ONLY_APP)
210 {
211 WCHAR *caption = NULL;
212
213 if (prg_name && *prg_name)
214 {
215 caption = g_utf8_to_utf16 (prg_name, -1, NULL, NULL, NULL);
216 }
217
218 MessageBoxW (NULL, L"g_on_error_query called, program terminating",
219 caption,
220 MB_OK|MB_ICONERROR);
221
222 g_free (caption);
223 }
224 #else
225 printf ("g_on_error_query called, program '%s' terminating\n",
226 (prg_name && *prg_name) ? prg_name : "(null)");
227 #endif
228 _exit(0);
229 #endif
230 }
231
232 /**
233 * g_on_error_stack_trace:
234 * @prg_name: the program name, needed by gdb for the "[S]tack trace"
235 * option
236 *
237 * Invokes gdb, which attaches to the current process and shows a
238 * stack trace. Called by g_on_error_query() when the "[S]tack trace"
239 * option is selected. You can get the current process's program name
240 * with g_get_prgname(), assuming that you have called gtk_init() or
241 * gdk_init().
242 *
243 * This function may cause different actions on non-UNIX platforms.
244 *
245 * When running on Windows, this function is *not* called by
246 * g_on_error_query(). If called directly, it will raise an
247 * exception, which will crash the program. If the `G_DEBUGGER` environment
248 * variable is set, a debugger will be invoked to attach and
249 * handle that exception (see [Running GLib Applications](glib-running.html)).
250 */
251 void
g_on_error_stack_trace(const gchar * prg_name)252 g_on_error_stack_trace (const gchar *prg_name)
253 {
254 #if defined(G_OS_UNIX)
255 pid_t pid;
256 gchar buf[16];
257 const gchar *args[5] = { DEBUGGER, NULL, NULL, NULL, NULL };
258 int status;
259
260 if (!prg_name)
261 return;
262
263 _g_sprintf (buf, "%u", (guint) getpid ());
264
265 #ifdef USE_LLDB
266 args[1] = prg_name;
267 args[2] = "-p";
268 args[3] = buf;
269 #else
270 args[1] = prg_name;
271 args[2] = buf;
272 #endif
273
274 pid = fork ();
275 if (pid == 0)
276 {
277 stack_trace (args);
278 _exit (0);
279 }
280 else if (pid == (pid_t) -1)
281 {
282 perror ("unable to fork " DEBUGGER);
283 return;
284 }
285
286 /* Wait until the child really terminates. On Mac OS X waitpid ()
287 * will also return when the child is being stopped due to tracing.
288 */
289 while (1)
290 {
291 pid_t retval = waitpid (pid, &status, 0);
292 if (WIFEXITED (retval) || WIFSIGNALED (retval))
293 break;
294 }
295 #else
296 if (IsDebuggerPresent ())
297 G_BREAKPOINT ();
298 else
299 g_abort ();
300 #endif
301 }
302
303 #ifndef G_OS_WIN32
304
305 static gboolean stack_trace_done = FALSE;
306
307 static void
stack_trace_sigchld(int signum)308 stack_trace_sigchld (int signum)
309 {
310 stack_trace_done = TRUE;
311 }
312
313 #define BUFSIZE 1024
314
315 static void
stack_trace(const char * const * args)316 stack_trace (const char * const *args)
317 {
318 pid_t pid;
319 int in_fd[2];
320 int out_fd[2];
321 fd_set fdset;
322 fd_set readset;
323 struct timeval tv;
324 int sel, idx, state, line_idx;
325 char buffer[BUFSIZE];
326 char c;
327
328 stack_trace_done = FALSE;
329 signal (SIGCHLD, stack_trace_sigchld);
330
331 if ((pipe (in_fd) == -1) || (pipe (out_fd) == -1))
332 {
333 perror ("unable to open pipe");
334 _exit (0);
335 }
336
337 pid = fork ();
338 if (pid == 0)
339 {
340 /* Save stderr for printing failure below */
341 int old_err = dup (2);
342 if (old_err != -1)
343 fcntl (old_err, F_SETFD, fcntl (old_err, F_GETFD) | FD_CLOEXEC);
344
345 close (0); dup (in_fd[0]); /* set the stdin to the in pipe */
346 close (1); dup (out_fd[1]); /* set the stdout to the out pipe */
347 close (2); dup (out_fd[1]); /* set the stderr to the out pipe */
348
349 execvp (args[0], (char **) args); /* exec gdb */
350
351 /* Print failure to original stderr */
352 if (old_err != -1)
353 {
354 close (2);
355 dup (old_err);
356 }
357 perror ("exec " DEBUGGER " failed");
358 _exit (0);
359 }
360 else if (pid == (pid_t) -1)
361 {
362 perror ("unable to fork");
363 _exit (0);
364 }
365
366 FD_ZERO (&fdset);
367 FD_SET (out_fd[0], &fdset);
368
369 #ifdef USE_LLDB
370 write (in_fd[1], "bt\n", 3);
371 write (in_fd[1], "p x = 0\n", 8);
372 write (in_fd[1], "process detach\n", 15);
373 write (in_fd[1], "quit\n", 5);
374 #else
375 write (in_fd[1], "backtrace\n", 10);
376 write (in_fd[1], "p x = 0\n", 8);
377 write (in_fd[1], "quit\n", 5);
378 #endif
379
380 idx = 0;
381 line_idx = 0;
382 state = 0;
383
384 while (1)
385 {
386 readset = fdset;
387 tv.tv_sec = 1;
388 tv.tv_usec = 0;
389
390 sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv);
391 if (sel == -1)
392 break;
393
394 if ((sel > 0) && (FD_ISSET (out_fd[0], &readset)))
395 {
396 if (read (out_fd[0], &c, 1))
397 {
398 line_idx += 1;
399 switch (state)
400 {
401 case 0:
402 #ifdef USE_LLDB
403 if (c == '*' || (c == ' ' && line_idx == 1))
404 #else
405 if (c == '#')
406 #endif
407 {
408 state = 1;
409 idx = 0;
410 buffer[idx++] = c;
411 }
412 break;
413 case 1:
414 if (idx < BUFSIZE)
415 buffer[idx++] = c;
416 if ((c == '\n') || (c == '\r'))
417 {
418 buffer[idx] = 0;
419 _g_fprintf (stdout, "%s", buffer);
420 state = 0;
421 idx = 0;
422 line_idx = 0;
423 }
424 break;
425 default:
426 break;
427 }
428 }
429 }
430 else if (stack_trace_done)
431 break;
432 }
433
434 close (in_fd[0]);
435 close (in_fd[1]);
436 close (out_fd[0]);
437 close (out_fd[1]);
438 _exit (0);
439 }
440
441 #endif /* !G_OS_WIN32 */
442