gbacktrace.c 8.92 KB
Newer Older
Owen Taylor's avatar
Owen Taylor committed
1 2 3 4
/* GLIB - Library of useful routines for C programming
 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Owen Taylor's avatar
Owen Taylor committed
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
Owen Taylor's avatar
Owen Taylor committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Owen Taylor's avatar
Owen Taylor committed
15 16 17 18
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
19

20
/*
21
 * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
22 23
 * file for a list of people on the GLib Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
24
 * GLib at ftp://ftp.gtk.org/pub/gtk/.
25 26
 */

27 28
/*
 * MT safe ; except for g_on_error_stack_trace, but who wants thread safety
29 30 31
 * then
 */

32
#include "config.h"
33
#include "glibconfig.h"
34

Owen Taylor's avatar
Owen Taylor committed
35 36 37 38
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
39

40
#ifdef HAVE_SYS_TIME_H
Owen Taylor's avatar
Owen Taylor committed
41
#include <sys/time.h>
42 43
#endif
#ifdef HAVE_SYS_TIMES_H
Owen Taylor's avatar
Owen Taylor committed
44
#include <sys/times.h>
45
#endif
Owen Taylor's avatar
Owen Taylor committed
46
#include <sys/types.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
47
#ifdef HAVE_SYS_WAIT_H
48
#include <sys/wait.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
49
#endif
Owen Taylor's avatar
Owen Taylor committed
50 51

#include <time.h>
52
#ifdef HAVE_UNISTD_H
Owen Taylor's avatar
Owen Taylor committed
53
#include <unistd.h>
54
#endif
Owen Taylor's avatar
Owen Taylor committed
55 56 57 58 59 60 61

#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif /* HAVE_SYS_SELECT_H */

#include <string.h> /* for bzero on BSD systems */

62
#ifdef G_OS_WIN32
63
#  define STRICT                /* Strict typing, please */
64
#  define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */
65
#  include <windows.h>
66
#  undef STRICT
67
#endif
Owen Taylor's avatar
Owen Taylor committed
68

69 70
#include "gbacktrace.h"

71
#include "gtypes.h"
72
#include "gmain.h"
73
#include "gprintfint.h"
74
#include "gutils.h"
75 76


Owen Taylor's avatar
Owen Taylor committed
77 78 79 80 81 82 83 84 85 86 87
#ifndef NO_FD_SET
#  define SELECT_MASK fd_set
#else
#  if defined(_IBMR2)
#    define SELECT_MASK void
#  else
#    define SELECT_MASK int
#  endif
#endif


88
#ifndef G_OS_WIN32
89
static void stack_trace (char **args);
90
#endif
Owen Taylor's avatar
Owen Taylor committed
91

92 93
/* People want to hit this from their debugger... */
GLIB_AVAILABLE_IN_ALL volatile gboolean glib_on_error_halt;
94
volatile gboolean glib_on_error_halt = TRUE;
Owen Taylor's avatar
Owen Taylor committed
95

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
/**
 * g_on_error_query:
 * @prg_name: the program name, needed by <command>gdb</command>
 *     for the [S]tack trace option. If @prg_name is %NULL, g_get_prgname()
 *     is called to get the program name (which will work correctly if
 *     gdk_init() or gtk_init() has been called)
 *
 * Prompts the user with
 * <computeroutput>[E]xit, [H]alt, show [S]tack trace or [P]roceed</computeroutput>.
 * This function is intended to be used for debugging use only.
 * The following example shows how it can be used together with
 * the g_log() functions.
 *
 * |[
 * &num;include &lt;glib.h&gt;
 *
 * static void
 * log_handler (const gchar   *log_domain,
 *              GLogLevelFlags log_level,
 *              const gchar   *message,
 *              gpointer       user_data)
 * {
 *   g_log_default_handler (log_domain, log_level, message, user_data);
 *
 *   g_on_error_query (MY_PROGRAM_NAME);
 * }
 *
 * int
 * main (int argc, char *argv[])
 * {
 *   g_log_set_handler (MY_LOG_DOMAIN,
 *                      G_LOG_LEVEL_WARNING |
 *                      G_LOG_LEVEL_ERROR |
 *                      G_LOG_LEVEL_CRITICAL,
 *                      log_handler,
 *                      NULL);
 *   /&ast; ... &ast;/
 * ]|
 *
 * If [E]xit is selected, the application terminates with a call
 * to <literal>_exit(0)</literal>.
 *
 * If [S]tack trace is selected, g_on_error_stack_trace() is called.
 * This invokes <command>gdb</command>, which attaches to the current
 * process and shows a stack trace. The prompt is then shown again.
 *
 * If [P]roceed is selected, the function returns.
 *
 * This function may cause different actions on non-UNIX platforms.
 */
Owen Taylor's avatar
Owen Taylor committed
146
void
147
g_on_error_query (const gchar *prg_name)
Owen Taylor's avatar
Owen Taylor committed
148
{
149
#ifndef G_OS_WIN32
150 151 152
  static const gchar * const query1 = "[E]xit, [H]alt";
  static const gchar * const query2 = ", show [S]tack trace";
  static const gchar * const query3 = " or [P]roceed";
153 154 155 156
  gchar buf[16];

  if (!prg_name)
    prg_name = g_get_prgname ();
157

158
 retry:
159

160
  if (prg_name)
161
    _g_fprintf (stdout,
162 163 164 165 166 167
                "%s (pid:%u): %s%s%s: ",
                prg_name,
                (guint) getpid (),
                query1,
                query2,
                query3);
168
  else
169
    _g_fprintf (stdout,
170 171 172 173
                "(process:%u): %s%s: ",
                (guint) getpid (),
                query1,
                query3);
Owen Taylor's avatar
Owen Taylor committed
174
  fflush (stdout);
175

176
  if (isatty(0) && isatty(1))
177
    fgets (buf, 8, stdin);
178 179
  else
    strcpy (buf, "E\n");
180 181 182 183 184

  if ((buf[0] == 'E' || buf[0] == 'e')
      && buf[1] == '\n')
    _exit (0);
  else if ((buf[0] == 'P' || buf[0] == 'p')
185
           && buf[1] == '\n')
Owen Taylor's avatar
Owen Taylor committed
186
    return;
187
  else if (prg_name
188 189
           && (buf[0] == 'S' || buf[0] == 's')
           && buf[1] == '\n')
190 191 192 193 194
    {
      g_on_error_stack_trace (prg_name);
      goto retry;
    }
  else if ((buf[0] == 'H' || buf[0] == 'h')
195
           && buf[1] == '\n')
196 197
    {
      while (glib_on_error_halt)
198
        ;
199 200 201
      glib_on_error_halt = TRUE;
      return;
    }
Owen Taylor's avatar
Owen Taylor committed
202
  else
203
    goto retry;
204 205 206
#else
  if (!prg_name)
    prg_name = g_get_prgname ();
207

208
  MessageBox (NULL, "g_on_error_query called, program terminating",
209 210
              (prg_name && *prg_name) ? prg_name : NULL,
              MB_OK|MB_ICONERROR);
211 212
  _exit(0);
#endif
Owen Taylor's avatar
Owen Taylor committed
213 214
}

215 216 217
/**
 * g_on_error_stack_trace:
 * @prg_name: the program name, needed by <command>gdb</command>
218
 *     for the [S]tack trace option.
219 220 221
 *
 * Invokes <command>gdb</command>, which attaches to the current
 * process and shows a stack trace. Called by g_on_error_query()
222 223 224
 * when the [S]tack trace option is selected. You can get the current
 * process's "program name" with g_get_prgname(), assuming that you
 * have called gtk_init() or gdk_init().
225 226 227
 *
 * This function may cause different actions on non-UNIX platforms.
 */
Owen Taylor's avatar
Owen Taylor committed
228
void
229
g_on_error_stack_trace (const gchar *prg_name)
Owen Taylor's avatar
Owen Taylor committed
230
{
231
#if defined(G_OS_UNIX) || defined(G_OS_BEOS)
Owen Taylor's avatar
Owen Taylor committed
232
  pid_t pid;
233 234
  gchar buf[16];
  gchar *args[4] = { "gdb", NULL, NULL, NULL };
235
  int status;
236 237 238

  if (!prg_name)
    return;
Owen Taylor's avatar
Owen Taylor committed
239

240
  _g_sprintf (buf, "%u", (guint) getpid ());
Owen Taylor's avatar
Owen Taylor committed
241

242
  args[1] = (gchar*) prg_name;
Owen Taylor's avatar
Owen Taylor committed
243 244
  args[2] = buf;

245 246
  pid = fork ();
  if (pid == 0)
Owen Taylor's avatar
Owen Taylor committed
247
    {
248 249
      stack_trace (args);
      _exit (0);
Owen Taylor's avatar
Owen Taylor committed
250
    }
251 252 253 254 255
  else if (pid == (pid_t) -1)
    {
      perror ("unable to fork gdb");
      return;
    }
256 257

  waitpid (pid, &status, 0);
258
#else
259 260 261 262
  if (IsDebuggerPresent ())
    G_BREAKPOINT ();
  else
    abort ();
263
#endif
264 265
}

266 267
#ifndef G_OS_WIN32

268 269 270 271 272 273
static gboolean stack_trace_done = FALSE;

static void
stack_trace_sigchld (int signum)
{
  stack_trace_done = TRUE;
Owen Taylor's avatar
Owen Taylor committed
274 275 276 277 278 279 280 281 282 283 284
}

static void
stack_trace (char **args)
{
  pid_t pid;
  int in_fd[2];
  int out_fd[2];
  SELECT_MASK fdset;
  SELECT_MASK readset;
  struct timeval tv;
285
  int sel, idx, state;
Owen Taylor's avatar
Owen Taylor committed
286 287 288
  char buffer[256];
  char c;

289
  stack_trace_done = FALSE;
Owen Taylor's avatar
Owen Taylor committed
290 291 292 293
  signal (SIGCHLD, stack_trace_sigchld);

  if ((pipe (in_fd) == -1) || (pipe (out_fd) == -1))
    {
294
      perror ("unable to open pipe");
Owen Taylor's avatar
Owen Taylor committed
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
      _exit (0);
    }

  pid = fork ();
  if (pid == 0)
    {
      close (0); dup (in_fd[0]);   /* set the stdin to the in pipe */
      close (1); dup (out_fd[1]);  /* set the stdout to the out pipe */
      close (2); dup (out_fd[1]);  /* set the stderr to the out pipe */

      execvp (args[0], args);      /* exec gdb */
      perror ("exec failed");
      _exit (0);
    }
  else if (pid == (pid_t) -1)
    {
311
      perror ("unable to fork");
Owen Taylor's avatar
Owen Taylor committed
312 313 314 315 316 317 318 319 320 321
      _exit (0);
    }

  FD_ZERO (&fdset);
  FD_SET (out_fd[0], &fdset);

  write (in_fd[1], "backtrace\n", 10);
  write (in_fd[1], "p x = 0\n", 8);
  write (in_fd[1], "quit\n", 5);

322
  idx = 0;
Owen Taylor's avatar
Owen Taylor committed
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
  state = 0;

  while (1)
    {
      readset = fdset;
      tv.tv_sec = 1;
      tv.tv_usec = 0;

      sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv);
      if (sel == -1)
        break;

      if ((sel > 0) && (FD_ISSET (out_fd[0], &readset)))
        {
          if (read (out_fd[0], &c, 1))
            {
              switch (state)
                {
                case 0:
                  if (c == '#')
                    {
                      state = 1;
345 346
                      idx = 0;
                      buffer[idx++] = c;
Owen Taylor's avatar
Owen Taylor committed
347 348 349
                    }
                  break;
                case 1:
350
                  buffer[idx++] = c;
Owen Taylor's avatar
Owen Taylor committed
351 352
                  if ((c == '\n') || (c == '\r'))
                    {
353
                      buffer[idx] = 0;
354
                      _g_fprintf (stdout, "%s", buffer);
Owen Taylor's avatar
Owen Taylor committed
355
                      state = 0;
356
                      idx = 0;
Owen Taylor's avatar
Owen Taylor committed
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
                    }
                  break;
                default:
                  break;
                }
            }
        }
      else if (stack_trace_done)
        break;
    }

  close (in_fd[0]);
  close (in_fd[1]);
  close (out_fd[0]);
  close (out_fd[1]);
  _exit (0);
}
374 375

#endif /* !G_OS_WIN32 */