1 /* GLIB - Library of useful routines for C programming
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * SPDX-License-Identifier: LGPL-2.1-or-later
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 * Modified by the GLib Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GLib Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GLib at ftp://ftp.gtk.org/pub/gtk/.
28 * MT safe ; except for g_on_error_stack_trace, but who wants thread safety
33 #include "glibconfig.h"
40 #ifdef HAVE_SYS_TIME_H
43 #include <sys/types.h>
51 #ifdef HAVE_SYS_SELECT_H
52 #include <sys/select.h>
53 #endif /* HAVE_SYS_SELECT_H */
59 # define STRICT /* Strict typing, please */
60 # define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */
67 #include "gbacktrace.h"
71 #include "gprintfint.h"
76 static void stack_trace (const char * const *args);
79 /* Default to using LLDB for backtraces on macOS. */
85 #define DEBUGGER "lldb"
87 #define DEBUGGER "gdb"
90 /* People want to hit this from their debugger... */
91 GLIB_AVAILABLE_IN_ALL volatile gboolean glib_on_error_halt;
92 volatile gboolean glib_on_error_halt = TRUE;
96 * @prg_name: the program name, needed by gdb for the "[S]tack trace"
97 * option. If @prg_name is %NULL, g_get_prgname() is called to get
98 * the program name (which will work correctly if gdk_init() or
99 * gtk_init() has been called)
101 * Prompts the user with
102 * `[E]xit, [H]alt, show [S]tack trace or [P]roceed`.
103 * This function is intended to be used for debugging use only.
104 * The following example shows how it can be used together with
105 * the g_log() functions.
107 * |[<!-- language="C" -->
111 * log_handler (const gchar *log_domain,
112 * GLogLevelFlags log_level,
113 * const gchar *message,
114 * gpointer user_data)
116 * g_log_default_handler (log_domain, log_level, message, user_data);
118 * g_on_error_query (MY_PROGRAM_NAME);
122 * main (int argc, char *argv[])
124 * g_log_set_handler (MY_LOG_DOMAIN,
125 * G_LOG_LEVEL_WARNING |
126 * G_LOG_LEVEL_ERROR |
127 * G_LOG_LEVEL_CRITICAL,
133 * If "[E]xit" is selected, the application terminates with a call
136 * If "[S]tack" trace is selected, g_on_error_stack_trace() is called.
137 * This invokes gdb, which attaches to the current process and shows
138 * a stack trace. The prompt is then shown again.
140 * If "[P]roceed" is selected, the function returns.
142 * This function may cause different actions on non-UNIX platforms.
144 * On Windows consider using the `G_DEBUGGER` environment
145 * variable (see [Running GLib Applications](glib-running.html)) and
146 * calling g_on_error_stack_trace() instead.
149 g_on_error_query (const gchar *prg_name)
152 static const gchar * const query1 = "[E]xit, [H]alt";
153 static const gchar * const query2 = ", show [S]tack trace";
154 static const gchar * const query3 = " or [P]roceed";
158 prg_name = g_get_prgname ();
164 "%s (pid:%u): %s%s%s: ",
172 "(process:%u): %s%s: ",
178 if (isatty(0) && isatty(1))
180 if (fgets (buf, 8, stdin) == NULL)
188 if ((buf[0] == 'E' || buf[0] == 'e')
191 else if ((buf[0] == 'P' || buf[0] == 'p')
195 && (buf[0] == 'S' || buf[0] == 's')
198 g_on_error_stack_trace (prg_name);
201 else if ((buf[0] == 'H' || buf[0] == 'h')
204 while (glib_on_error_halt)
206 glib_on_error_halt = TRUE;
213 prg_name = g_get_prgname ();
215 /* MessageBox is allowed on UWP apps only when building against
216 * the debug CRT, which will set -D_DEBUG */
217 #if defined(_DEBUG) || !defined(G_WINAPI_ONLY_APP)
219 WCHAR *caption = NULL;
221 if (prg_name && *prg_name)
223 caption = g_utf8_to_utf16 (prg_name, -1, NULL, NULL, NULL);
226 MessageBoxW (NULL, L"g_on_error_query called, program terminating",
233 printf ("g_on_error_query called, program '%s' terminating\n",
234 (prg_name && *prg_name) ? prg_name : "(null)");
241 * g_on_error_stack_trace:
242 * @prg_name: the program name, needed by gdb for the "[S]tack trace"
245 * Invokes gdb, which attaches to the current process and shows a
246 * stack trace. Called by g_on_error_query() when the "[S]tack trace"
247 * option is selected. You can get the current process's program name
248 * with g_get_prgname(), assuming that you have called gtk_init() or
251 * This function may cause different actions on non-UNIX platforms.
253 * When running on Windows, this function is *not* called by
254 * g_on_error_query(). If called directly, it will raise an
255 * exception, which will crash the program. If the `G_DEBUGGER` environment
256 * variable is set, a debugger will be invoked to attach and
257 * handle that exception (see [Running GLib Applications](glib-running.html)).
260 g_on_error_stack_trace (const gchar *prg_name)
262 #if defined(G_OS_UNIX)
265 const gchar *args[5] = { DEBUGGER, NULL, NULL, NULL, NULL };
271 _g_sprintf (buf, "%u", (guint) getpid ());
288 else if (pid == (pid_t) -1)
290 perror ("unable to fork " DEBUGGER);
294 /* Wait until the child really terminates. On Mac OS X waitpid ()
295 * will also return when the child is being stopped due to tracing.
299 pid_t retval = waitpid (pid, &status, 0);
300 if (WIFEXITED (retval) || WIFSIGNALED (retval))
304 if (IsDebuggerPresent ())
313 static gboolean stack_trace_done = FALSE;
316 stack_trace_sigchld (int signum)
318 stack_trace_done = TRUE;
323 static inline const char *
324 get_strerror (char *buffer, gsize n)
326 #if defined(STRERROR_R_CHAR_P)
327 return strerror_r (errno, buffer, n);
328 #elif defined(HAVE_STRERROR_R)
329 int ret = strerror_r (errno, buffer, n);
330 if (ret == 0 || ret == EINVAL)
334 const char *error_str = strerror (errno);
338 strncpy (buffer, error_str, n);
344 checked_write (int fd, gconstpointer buf, gsize n)
346 gssize written = write (fd, buf, n);
350 char msg[BUFSIZE] = {0};
351 char error_str[BUFSIZE / 2] = {0};
353 get_strerror (error_str, sizeof (error_str) - 1);
354 snprintf (msg, sizeof (msg) - 1, "Unable to write to fd %d: %s", fd, error_str);
365 int new_fd = dup (fd);
369 char msg[BUFSIZE] = {0};
370 char error_str[BUFSIZE / 2] = {0};
372 get_strerror (error_str, sizeof (error_str) - 1);
373 snprintf (msg, sizeof (msg) - 1, "Unable to duplicate fd %d: %s", fd, error_str);
382 stack_trace (const char * const *args)
394 char buffer[BUFSIZE];
397 stack_trace_done = FALSE;
398 signal (SIGCHLD, stack_trace_sigchld);
400 if ((pipe (in_fd) == -1) || (pipe (out_fd) == -1))
402 perror ("unable to open pipe");
409 /* Save stderr for printing failure below */
410 int old_err = dup (2);
413 int getfd = fcntl (old_err, F_GETFD);
415 (void) fcntl (old_err, F_SETFD, getfd | FD_CLOEXEC);
419 checked_dup (in_fd[0]); /* set the stdin to the in pipe */
421 checked_dup (out_fd[1]); /* set the stdout to the out pipe */
423 checked_dup (out_fd[1]); /* set the stderr to the out pipe */
425 execvp (args[0], (char **) args); /* exec gdb */
427 /* Print failure to original stderr */
431 /* We can ignore the return value here as we're failing anyways */
432 (void) !dup (old_err);
434 perror ("exec " DEBUGGER " failed");
437 else if (pid == (pid_t) -1)
439 perror ("unable to fork");
444 FD_SET (out_fd[0], &fdset);
447 checked_write (in_fd[1], "bt\n", 3);
448 checked_write (in_fd[1], "p x = 0\n", 8);
449 checked_write (in_fd[1], "process detach\n", 15);
450 checked_write (in_fd[1], "quit\n", 5);
452 /* Don't wrap so that lines are not truncated */
453 checked_write (in_fd[1], "set width unlimited\n", 20);
454 checked_write (in_fd[1], "backtrace\n", 10);
455 checked_write (in_fd[1], "p x = 0\n", 8);
456 checked_write (in_fd[1], "quit\n", 5);
471 sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv);
475 if ((sel > 0) && (FD_ISSET (out_fd[0], &readset)))
477 if (read (out_fd[0], &c, 1))
487 if (c == '*' || (c == ' ' && line_idx == 1))
500 if ((c == '\n') || (c == '\r'))
503 _g_fprintf (stdout, "%s", buffer);
516 else if (stack_trace_done)
527 #endif /* !G_OS_WIN32 */