Partly revert "gio: Add filename type annotations"
[glib.git] / glib / gbacktrace.c
blobd231b5a5ae24eaaeab95fb139176f010a37ebc84
1 /* GLIB - Library of useful routines for C programming
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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 of the License, or (at your option) any later version.
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.
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/>.
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/.
26 * MT safe ; except for g_on_error_stack_trace, but who wants thread safety
27 * then
30 #include "config.h"
31 #include "glibconfig.h"
33 #include <signal.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
38 #ifdef HAVE_SYS_TIME_H
39 #include <sys/time.h>
40 #endif
41 #include <sys/types.h>
43 #include <time.h>
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
53 #include <string.h>
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
64 #include "gbacktrace.h"
66 #include "gtypes.h"
67 #include "gmain.h"
68 #include "gprintfint.h"
69 #include "gutils.h"
72 #ifndef NO_FD_SET
73 # define SELECT_MASK fd_set
74 #else
75 # if defined(_IBMR2)
76 # define SELECT_MASK void
77 # else
78 # define SELECT_MASK int
79 # endif
80 #endif
83 #ifndef G_OS_WIN32
84 static void stack_trace (char **args);
85 #endif
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;
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)
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.
104 * |[<!-- language="C" -->
105 * #include <glib.h>
107 * static void
108 * log_handler (const gchar *log_domain,
109 * GLogLevelFlags log_level,
110 * const gchar *message,
111 * gpointer user_data)
113 * g_log_default_handler (log_domain, log_level, message, user_data);
115 * g_on_error_query (MY_PROGRAM_NAME);
118 * int
119 * main (int argc, char *argv[])
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 * ]|
130 * If "[E]xit" is selected, the application terminates with a call
131 * to _exit(0).
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.
137 * If "[P]roceed" is selected, the function returns.
139 * This function may cause different actions on non-UNIX platforms.
141 void
142 g_on_error_query (const gchar *prg_name)
144 #ifndef G_OS_WIN32
145 static const gchar * const query1 = "[E]xit, [H]alt";
146 static const gchar * const query2 = ", show [S]tack trace";
147 static const gchar * const query3 = " or [P]roceed";
148 gchar buf[16];
150 if (!prg_name)
151 prg_name = g_get_prgname ();
153 retry:
155 if (prg_name)
156 _g_fprintf (stdout,
157 "%s (pid:%u): %s%s%s: ",
158 prg_name,
159 (guint) getpid (),
160 query1,
161 query2,
162 query3);
163 else
164 _g_fprintf (stdout,
165 "(process:%u): %s%s: ",
166 (guint) getpid (),
167 query1,
168 query3);
169 fflush (stdout);
171 if (isatty(0) && isatty(1))
172 fgets (buf, 8, stdin);
173 else
174 strcpy (buf, "E\n");
176 if ((buf[0] == 'E' || buf[0] == 'e')
177 && buf[1] == '\n')
178 _exit (0);
179 else if ((buf[0] == 'P' || buf[0] == 'p')
180 && buf[1] == '\n')
181 return;
182 else if (prg_name
183 && (buf[0] == 'S' || buf[0] == 's')
184 && buf[1] == '\n')
186 g_on_error_stack_trace (prg_name);
187 goto retry;
189 else if ((buf[0] == 'H' || buf[0] == 'h')
190 && buf[1] == '\n')
192 while (glib_on_error_halt)
194 glib_on_error_halt = TRUE;
195 return;
197 else
198 goto retry;
199 #else
200 if (!prg_name)
201 prg_name = g_get_prgname ();
203 MessageBox (NULL, "g_on_error_query called, program terminating",
204 (prg_name && *prg_name) ? prg_name : NULL,
205 MB_OK|MB_ICONERROR);
206 _exit(0);
207 #endif
211 * g_on_error_stack_trace:
212 * @prg_name: the program name, needed by gdb for the "[S]tack trace"
213 * option
215 * Invokes gdb, which attaches to the current process and shows a
216 * stack trace. Called by g_on_error_query() when the "[S]tack trace"
217 * option is selected. You can get the current process's program name
218 * with g_get_prgname(), assuming that you have called gtk_init() or
219 * gdk_init().
221 * This function may cause different actions on non-UNIX platforms.
223 void
224 g_on_error_stack_trace (const gchar *prg_name)
226 #if defined(G_OS_UNIX)
227 pid_t pid;
228 gchar buf[16];
229 gchar *args[4] = { "gdb", NULL, NULL, NULL };
230 int status;
232 if (!prg_name)
233 return;
235 _g_sprintf (buf, "%u", (guint) getpid ());
237 args[1] = (gchar*) prg_name;
238 args[2] = buf;
240 pid = fork ();
241 if (pid == 0)
243 stack_trace (args);
244 _exit (0);
246 else if (pid == (pid_t) -1)
248 perror ("unable to fork gdb");
249 return;
252 waitpid (pid, &status, 0);
253 #else
254 if (IsDebuggerPresent ())
255 G_BREAKPOINT ();
256 else
257 g_abort ();
258 #endif
261 #ifndef G_OS_WIN32
263 static gboolean stack_trace_done = FALSE;
265 static void
266 stack_trace_sigchld (int signum)
268 stack_trace_done = TRUE;
271 static void
272 stack_trace (char **args)
274 pid_t pid;
275 int in_fd[2];
276 int out_fd[2];
277 SELECT_MASK fdset;
278 SELECT_MASK readset;
279 struct timeval tv;
280 int sel, idx, state;
281 char buffer[256];
282 char c;
284 stack_trace_done = FALSE;
285 signal (SIGCHLD, stack_trace_sigchld);
287 if ((pipe (in_fd) == -1) || (pipe (out_fd) == -1))
289 perror ("unable to open pipe");
290 _exit (0);
293 pid = fork ();
294 if (pid == 0)
296 /* Save stderr for printing failure below */
297 int old_err = dup (2);
298 fcntl (old_err, F_SETFD, fcntl (old_err, F_GETFD) | FD_CLOEXEC);
300 close (0); dup (in_fd[0]); /* set the stdin to the in pipe */
301 close (1); dup (out_fd[1]); /* set the stdout to the out pipe */
302 close (2); dup (out_fd[1]); /* set the stderr to the out pipe */
304 execvp (args[0], args); /* exec gdb */
306 /* Print failure to original stderr */
307 close (2); dup (old_err);
308 perror ("exec gdb failed");
309 _exit (0);
311 else if (pid == (pid_t) -1)
313 perror ("unable to fork");
314 _exit (0);
317 FD_ZERO (&fdset);
318 FD_SET (out_fd[0], &fdset);
320 write (in_fd[1], "backtrace\n", 10);
321 write (in_fd[1], "p x = 0\n", 8);
322 write (in_fd[1], "quit\n", 5);
324 idx = 0;
325 state = 0;
327 while (1)
329 readset = fdset;
330 tv.tv_sec = 1;
331 tv.tv_usec = 0;
333 sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv);
334 if (sel == -1)
335 break;
337 if ((sel > 0) && (FD_ISSET (out_fd[0], &readset)))
339 if (read (out_fd[0], &c, 1))
341 switch (state)
343 case 0:
344 if (c == '#')
346 state = 1;
347 idx = 0;
348 buffer[idx++] = c;
350 break;
351 case 1:
352 buffer[idx++] = c;
353 if ((c == '\n') || (c == '\r'))
355 buffer[idx] = 0;
356 _g_fprintf (stdout, "%s", buffer);
357 state = 0;
358 idx = 0;
360 break;
361 default:
362 break;
366 else if (stack_trace_done)
367 break;
370 close (in_fd[0]);
371 close (in_fd[1]);
372 close (out_fd[0]);
373 close (out_fd[1]);
374 _exit (0);
377 #endif /* !G_OS_WIN32 */