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>
48 #include "glib-unixprivate.h"
52 #ifdef HAVE_SYS_SELECT_H
53 #include <sys/select.h>
54 #endif /* HAVE_SYS_SELECT_H */
60 # define STRICT /* Strict typing, please */
61 # define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */
68 #include "gbacktrace.h"
72 #include "gprintfint.h"
77 static void stack_trace (const char * const *args);
80 /* Default to using LLDB for backtraces on macOS. */
86 #define DEBUGGER "lldb"
88 #define DEBUGGER "gdb"
91 /* People want to hit this from their debugger... */
92 GLIB_AVAILABLE_IN_ALL volatile gboolean glib_on_error_halt;
93 volatile gboolean glib_on_error_halt = TRUE;
97 * @prg_name: the program name, needed by gdb for the "[S]tack trace"
98 * option. If @prg_name is %NULL, g_get_prgname() is called to get
99 * the program name (which will work correctly if gdk_init() or
100 * gtk_init() has been called)
102 * Prompts the user with
103 * `[E]xit, [H]alt, show [S]tack trace or [P]roceed`.
104 * This function is intended to be used for debugging use only.
105 * The following example shows how it can be used together with
106 * the g_log() functions.
108 * |[<!-- language="C" -->
112 * log_handler (const gchar *log_domain,
113 * GLogLevelFlags log_level,
114 * const gchar *message,
115 * gpointer user_data)
117 * g_log_default_handler (log_domain, log_level, message, user_data);
119 * g_on_error_query (MY_PROGRAM_NAME);
123 * main (int argc, char *argv[])
125 * g_log_set_handler (MY_LOG_DOMAIN,
126 * G_LOG_LEVEL_WARNING |
127 * G_LOG_LEVEL_ERROR |
128 * G_LOG_LEVEL_CRITICAL,
134 * If "[E]xit" is selected, the application terminates with a call
137 * If "[S]tack" trace is selected, g_on_error_stack_trace() is called.
138 * This invokes gdb, which attaches to the current process and shows
139 * a stack trace. The prompt is then shown again.
141 * If "[P]roceed" is selected, the function returns.
143 * This function may cause different actions on non-UNIX platforms.
145 * On Windows consider using the `G_DEBUGGER` environment
146 * variable (see [Running GLib Applications](glib-running.html)) and
147 * calling g_on_error_stack_trace() instead.
150 g_on_error_query (const gchar *prg_name)
153 static const gchar * const query1 = "[E]xit, [H]alt";
154 static const gchar * const query2 = ", show [S]tack trace";
155 static const gchar * const query3 = " or [P]roceed";
159 prg_name = g_get_prgname ();
165 "%s (pid:%u): %s%s%s: ",
173 "(process:%u): %s%s: ",
179 if (isatty(0) && isatty(1))
181 if (fgets (buf, 8, stdin) == NULL)
189 if ((buf[0] == 'E' || buf[0] == 'e')
192 else if ((buf[0] == 'P' || buf[0] == 'p')
196 && (buf[0] == 'S' || buf[0] == 's')
199 g_on_error_stack_trace (prg_name);
202 else if ((buf[0] == 'H' || buf[0] == 'h')
205 while (glib_on_error_halt)
207 glib_on_error_halt = TRUE;
214 prg_name = g_get_prgname ();
216 /* MessageBox is allowed on UWP apps only when building against
217 * the debug CRT, which will set -D_DEBUG */
218 #if defined(_DEBUG) || !defined(G_WINAPI_ONLY_APP)
220 WCHAR *caption = NULL;
222 if (prg_name && *prg_name)
224 caption = g_utf8_to_utf16 (prg_name, -1, NULL, NULL, NULL);
227 MessageBoxW (NULL, L"g_on_error_query called, program terminating",
234 printf ("g_on_error_query called, program '%s' terminating\n",
235 (prg_name && *prg_name) ? prg_name : "(null)");
242 * g_on_error_stack_trace:
243 * @prg_name: the program name, needed by gdb for the "[S]tack trace"
246 * Invokes gdb, which attaches to the current process and shows a
247 * stack trace. Called by g_on_error_query() when the "[S]tack trace"
248 * option is selected. You can get the current process's program name
249 * with g_get_prgname(), assuming that you have called gtk_init() or
252 * This function may cause different actions on non-UNIX platforms.
254 * When running on Windows, this function is *not* called by
255 * g_on_error_query(). If called directly, it will raise an
256 * exception, which will crash the program. If the `G_DEBUGGER` environment
257 * variable is set, a debugger will be invoked to attach and
258 * handle that exception (see [Running GLib Applications](glib-running.html)).
261 g_on_error_stack_trace (const gchar *prg_name)
263 #if defined(G_OS_UNIX)
266 const gchar *args[5] = { DEBUGGER, NULL, NULL, NULL, NULL };
272 _g_sprintf (buf, "%u", (guint) getpid ());
289 else if (pid == (pid_t) -1)
291 perror ("unable to fork " DEBUGGER);
295 /* Wait until the child really terminates. On Mac OS X waitpid ()
296 * will also return when the child is being stopped due to tracing.
300 pid_t retval = waitpid (pid, &status, 0);
301 if (WIFEXITED (retval) || WIFSIGNALED (retval))
305 if (IsDebuggerPresent ())
314 static gboolean stack_trace_done = FALSE;
317 stack_trace_sigchld (int signum)
319 stack_trace_done = TRUE;
324 static inline const char *
325 get_strerror (char *buffer, gsize n)
327 #if defined(STRERROR_R_CHAR_P)
328 return strerror_r (errno, buffer, n);
329 #elif defined(HAVE_STRERROR_R)
330 int ret = strerror_r (errno, buffer, n);
331 if (ret == 0 || ret == EINVAL)
335 const char *error_str = strerror (errno);
339 strncpy (buffer, error_str, n);
345 checked_write (int fd, gconstpointer buf, gsize n)
347 gssize written = write (fd, buf, n);
351 char msg[BUFSIZE] = {0};
352 char error_str[BUFSIZE / 2] = {0};
354 get_strerror (error_str, sizeof (error_str) - 1);
355 snprintf (msg, sizeof (msg) - 1, "Unable to write to fd %d: %s", fd, error_str);
366 int new_fd = dup (fd);
370 char msg[BUFSIZE] = {0};
371 char error_str[BUFSIZE / 2] = {0};
373 get_strerror (error_str, sizeof (error_str) - 1);
374 snprintf (msg, sizeof (msg) - 1, "Unable to duplicate fd %d: %s", fd, error_str);
383 stack_trace (const char * const *args)
395 char buffer[BUFSIZE];
398 stack_trace_done = FALSE;
399 signal (SIGCHLD, stack_trace_sigchld);
401 if (!g_unix_open_pipe_internal (in_fd, TRUE, FALSE) ||
402 !g_unix_open_pipe_internal (out_fd, TRUE, FALSE))
404 perror ("unable to open pipe");
411 /* Save stderr for printing failure below */
412 int old_err = dup (2);
415 int getfd = fcntl (old_err, F_GETFD);
417 (void) fcntl (old_err, F_SETFD, getfd | FD_CLOEXEC);
421 checked_dup (in_fd[0]); /* set the stdin to the in pipe */
423 checked_dup (out_fd[1]); /* set the stdout to the out pipe */
425 checked_dup (out_fd[1]); /* set the stderr to the out pipe */
427 execvp (args[0], (char **) args); /* exec gdb */
429 /* Print failure to original stderr */
433 /* We can ignore the return value here as we're failing anyways */
434 (void) !dup (old_err);
436 perror ("exec " DEBUGGER " failed");
439 else if (pid == (pid_t) -1)
441 perror ("unable to fork");
446 FD_SET (out_fd[0], &fdset);
449 checked_write (in_fd[1], "bt\n", 3);
450 checked_write (in_fd[1], "p x = 0\n", 8);
451 checked_write (in_fd[1], "process detach\n", 15);
452 checked_write (in_fd[1], "quit\n", 5);
454 /* Don't wrap so that lines are not truncated */
455 checked_write (in_fd[1], "set width unlimited\n", 20);
456 checked_write (in_fd[1], "backtrace\n", 10);
457 checked_write (in_fd[1], "p x = 0\n", 8);
458 checked_write (in_fd[1], "quit\n", 5);
473 sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv);
477 if ((sel > 0) && (FD_ISSET (out_fd[0], &readset)))
479 if (read (out_fd[0], &c, 1))
489 if (c == '*' || (c == ' ' && line_idx == 1))
502 if ((c == '\n') || (c == '\r'))
505 _g_fprintf (stdout, "%s", buffer);
518 else if (stack_trace_done)
529 #endif /* !G_OS_WIN32 */