(root)/
glib-2.79.0/
glib/
gbacktrace.c
       1  /* GLIB - Library of useful routines for C programming
       2   * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
       3   *
       4   * SPDX-License-Identifier: LGPL-2.1-or-later
       5   *
       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.
      10   *
      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.
      15   *
      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/>.
      18   */
      19  
      20  /*
      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/.
      25   */
      26  
      27  /*
      28   * MT safe ; except for g_on_error_stack_trace, but who wants thread safety
      29   * then
      30   */
      31  
      32  #include "config.h"
      33  #include "glibconfig.h"
      34  
      35  #include <signal.h>
      36  #include <stdarg.h>
      37  #include <stdio.h>
      38  #include <stdlib.h>
      39  
      40  #ifdef HAVE_SYS_TIME_H
      41  #include <sys/time.h>
      42  #endif
      43  #include <sys/types.h>
      44  
      45  #include <time.h>
      46  
      47  #ifdef G_OS_UNIX
      48  #include "glib-unixprivate.h"
      49  #include <errno.h>
      50  #include <unistd.h>
      51  #include <sys/wait.h>
      52  #ifdef HAVE_SYS_SELECT_H
      53  #include <sys/select.h>
      54  #endif /* HAVE_SYS_SELECT_H */
      55  #endif
      56  
      57  #include <string.h>
      58  
      59  #ifdef G_OS_WIN32
      60  #  define STRICT                /* Strict typing, please */
      61  #  define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */
      62  #  include <windows.h>
      63  #  undef STRICT
      64  #else
      65  #  include <fcntl.h>
      66  #endif
      67  
      68  #include "gbacktrace.h"
      69  
      70  #include "gtypes.h"
      71  #include "gmain.h"
      72  #include "gprintfint.h"
      73  #include "gunicode.h"
      74  #include "gutils.h"
      75  
      76  #ifndef G_OS_WIN32
      77  static void stack_trace (const char * const *args);
      78  #endif
      79  
      80  /* Default to using LLDB for backtraces on macOS. */
      81  #ifdef __APPLE__
      82  #define USE_LLDB
      83  #endif
      84  
      85  #ifdef USE_LLDB
      86  #define DEBUGGER "lldb"
      87  #else
      88  #define DEBUGGER "gdb"
      89  #endif
      90  
      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;
      94  
      95  /**
      96   * g_on_error_query:
      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)
     101   *
     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.
     107   *
     108   * |[<!-- language="C" -->
     109   * #include <glib.h>
     110   *
     111   * static void
     112   * log_handler (const gchar   *log_domain,
     113   *              GLogLevelFlags log_level,
     114   *              const gchar   *message,
     115   *              gpointer       user_data)
     116   * {
     117   *   g_log_default_handler (log_domain, log_level, message, user_data);
     118   *
     119   *   g_on_error_query (MY_PROGRAM_NAME);
     120   * }
     121   *
     122   * int
     123   * main (int argc, char *argv[])
     124   * {
     125   *   g_log_set_handler (MY_LOG_DOMAIN,
     126   *                      G_LOG_LEVEL_WARNING |
     127   *                      G_LOG_LEVEL_ERROR |
     128   *                      G_LOG_LEVEL_CRITICAL,
     129   *                      log_handler,
     130   *                      NULL);
     131   *   ...
     132   * ]|
     133   *
     134   * If "[E]xit" is selected, the application terminates with a call
     135   * to _exit(0).
     136   *
     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.
     140   *
     141   * If "[P]roceed" is selected, the function returns.
     142   *
     143   * This function may cause different actions on non-UNIX platforms.
     144   *
     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.
     148   */
     149  void
     150  g_on_error_query (const gchar *prg_name)
     151  {
     152  #ifndef G_OS_WIN32
     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";
     156    gchar buf[16];
     157  
     158    if (!prg_name)
     159      prg_name = g_get_prgname ();
     160  
     161   retry:
     162  
     163    if (prg_name)
     164      _g_fprintf (stdout,
     165                  "%s (pid:%u): %s%s%s: ",
     166                  prg_name,
     167                  (guint) getpid (),
     168                  query1,
     169                  query2,
     170                  query3);
     171    else
     172      _g_fprintf (stdout,
     173                  "(process:%u): %s%s: ",
     174                  (guint) getpid (),
     175                  query1,
     176                  query3);
     177    fflush (stdout);
     178  
     179    if (isatty(0) && isatty(1))
     180      {
     181        if (fgets (buf, 8, stdin) == NULL)
     182          _exit (0);
     183      }
     184    else
     185      {
     186        strcpy (buf, "E\n");
     187      }
     188  
     189    if ((buf[0] == 'E' || buf[0] == 'e')
     190        && buf[1] == '\n')
     191      _exit (0);
     192    else if ((buf[0] == 'P' || buf[0] == 'p')
     193             && buf[1] == '\n')
     194      return;
     195    else if (prg_name
     196             && (buf[0] == 'S' || buf[0] == 's')
     197             && buf[1] == '\n')
     198      {
     199        g_on_error_stack_trace (prg_name);
     200        goto retry;
     201      }
     202    else if ((buf[0] == 'H' || buf[0] == 'h')
     203             && buf[1] == '\n')
     204      {
     205        while (glib_on_error_halt)
     206          ;
     207        glib_on_error_halt = TRUE;
     208        return;
     209      }
     210    else
     211      goto retry;
     212  #else
     213    if (!prg_name)
     214      prg_name = g_get_prgname ();
     215  
     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)
     219    {
     220      WCHAR *caption = NULL;
     221  
     222      if (prg_name && *prg_name)
     223        {
     224          caption = g_utf8_to_utf16 (prg_name, -1, NULL, NULL, NULL);
     225        }
     226  
     227      MessageBoxW (NULL, L"g_on_error_query called, program terminating",
     228                   caption,
     229                   MB_OK|MB_ICONERROR);
     230  
     231      g_free (caption);
     232    }
     233  #else
     234    printf ("g_on_error_query called, program '%s' terminating\n",
     235        (prg_name && *prg_name) ? prg_name : "(null)");
     236  #endif
     237    _exit(0);
     238  #endif
     239  }
     240  
     241  /**
     242   * g_on_error_stack_trace:
     243   * @prg_name: the program name, needed by gdb for the "[S]tack trace"
     244   *     option
     245   *
     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
     250   * gdk_init().
     251   *
     252   * This function may cause different actions on non-UNIX platforms.
     253   *
     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)).
     259   */
     260  void
     261  g_on_error_stack_trace (const gchar *prg_name)
     262  {
     263  #if defined(G_OS_UNIX)
     264    pid_t pid;
     265    gchar buf[16];
     266    const gchar *args[5] = { DEBUGGER, NULL, NULL, NULL, NULL };
     267    int status;
     268  
     269    if (!prg_name)
     270      return;
     271  
     272    _g_sprintf (buf, "%u", (guint) getpid ());
     273  
     274  #ifdef USE_LLDB
     275    args[1] = prg_name;
     276    args[2] = "-p";
     277    args[3] = buf;
     278  #else
     279    args[1] = prg_name;
     280    args[2] = buf;
     281  #endif
     282  
     283    pid = fork ();
     284    if (pid == 0)
     285      {
     286        stack_trace (args);
     287        _exit (0);
     288      }
     289    else if (pid == (pid_t) -1)
     290      {
     291        perror ("unable to fork " DEBUGGER);
     292        return;
     293      }
     294  
     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.
     297     */
     298    while (1)
     299      {
     300        pid_t retval = waitpid (pid, &status, 0);
     301        if (WIFEXITED (retval) || WIFSIGNALED (retval))
     302          break;
     303      }
     304  #else
     305    if (IsDebuggerPresent ())
     306      G_BREAKPOINT ();
     307    else
     308      g_abort ();
     309  #endif
     310  }
     311  
     312  #ifndef G_OS_WIN32
     313  
     314  static gboolean stack_trace_done = FALSE;
     315  
     316  static void
     317  stack_trace_sigchld (int signum)
     318  {
     319    stack_trace_done = TRUE;
     320  }
     321  
     322  #define BUFSIZE 1024
     323  
     324  static inline const char *
     325  get_strerror (char *buffer, gsize n)
     326  {
     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)
     332      return buffer;
     333    return NULL;
     334  #else
     335    const char *error_str = strerror (errno);
     336    if (!error_str)
     337      return NULL;
     338  
     339    strncpy (buffer, error_str, n);
     340    return buffer;
     341  #endif
     342  }
     343  
     344  static gssize
     345  checked_write (int fd, gconstpointer buf, gsize n)
     346  {
     347    gssize written = write (fd, buf, n);
     348  
     349    if (written == -1)
     350      {
     351        char msg[BUFSIZE] = {0};
     352        char error_str[BUFSIZE / 2] = {0};
     353  
     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);
     356        perror (msg);
     357        _exit (0);
     358      }
     359  
     360    return written;
     361  }
     362  
     363  static int
     364  checked_dup (int fd)
     365  {
     366    int new_fd = dup (fd);
     367  
     368    if (new_fd == -1)
     369      {
     370        char msg[BUFSIZE] = {0};
     371        char error_str[BUFSIZE / 2] = {0};
     372  
     373        get_strerror (error_str, sizeof (error_str) - 1);
     374        snprintf (msg, sizeof (msg) - 1, "Unable to duplicate fd %d: %s", fd, error_str);
     375        perror (msg);
     376        _exit (0);
     377      }
     378  
     379    return new_fd;
     380  }
     381  
     382  static void
     383  stack_trace (const char * const *args)
     384  {
     385    pid_t pid;
     386    int in_fd[2];
     387    int out_fd[2];
     388    fd_set fdset;
     389    fd_set readset;
     390    struct timeval tv;
     391    int sel, idx, state;
     392  #ifdef USE_LLDB
     393    int line_idx;
     394  #endif
     395    char buffer[BUFSIZE];
     396    char c;
     397  
     398    stack_trace_done = FALSE;
     399    signal (SIGCHLD, stack_trace_sigchld);
     400  
     401    if (!g_unix_open_pipe_internal (in_fd, TRUE, FALSE) ||
     402        !g_unix_open_pipe_internal (out_fd, TRUE, FALSE))
     403      {
     404        perror ("unable to open pipe");
     405        _exit (0);
     406      }
     407  
     408    pid = fork ();
     409    if (pid == 0)
     410      {
     411        /* Save stderr for printing failure below */
     412        int old_err = dup (2);
     413        if (old_err != -1)
     414  	{
     415  	  int getfd = fcntl (old_err, F_GETFD);
     416  	  if (getfd != -1)
     417  	    (void) fcntl (old_err, F_SETFD, getfd | FD_CLOEXEC);
     418  	}
     419  
     420        close (0);
     421        checked_dup (in_fd[0]);   /* set the stdin to the in pipe */
     422        close (1);
     423        checked_dup (out_fd[1]);  /* set the stdout to the out pipe */
     424        close (2);
     425        checked_dup (out_fd[1]);  /* set the stderr to the out pipe */
     426  
     427        execvp (args[0], (char **) args);      /* exec gdb */
     428  
     429        /* Print failure to original stderr */
     430        if (old_err != -1)
     431          {
     432            close (2);
     433            /* We can ignore the return value here as we're failing anyways */
     434            (void) !dup (old_err);
     435          }
     436        perror ("exec " DEBUGGER " failed");
     437        _exit (0);
     438      }
     439    else if (pid == (pid_t) -1)
     440      {
     441        perror ("unable to fork");
     442        _exit (0);
     443      }
     444  
     445    FD_ZERO (&fdset);
     446    FD_SET (out_fd[0], &fdset);
     447  
     448  #ifdef USE_LLDB
     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);
     453  #else
     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);
     459  #endif
     460  
     461    idx = 0;
     462  #ifdef USE_LLDB
     463    line_idx = 0;
     464  #endif
     465    state = 0;
     466  
     467    while (1)
     468      {
     469        readset = fdset;
     470        tv.tv_sec = 1;
     471        tv.tv_usec = 0;
     472  
     473        sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv);
     474        if (sel == -1)
     475          break;
     476  
     477        if ((sel > 0) && (FD_ISSET (out_fd[0], &readset)))
     478          {
     479            if (read (out_fd[0], &c, 1))
     480              {
     481  #ifdef USE_LLDB
     482                line_idx += 1;
     483  #endif
     484  
     485                switch (state)
     486                  {
     487                  case 0:
     488  #ifdef USE_LLDB
     489                    if (c == '*' || (c == ' ' && line_idx == 1))
     490  #else
     491                    if (c == '#')
     492  #endif
     493                      {
     494                        state = 1;
     495                        idx = 0;
     496                        buffer[idx++] = c;
     497                      }
     498                    break;
     499                  case 1:
     500                    if (idx < BUFSIZE)
     501                      buffer[idx++] = c;
     502                    if ((c == '\n') || (c == '\r'))
     503                      {
     504                        buffer[idx] = 0;
     505                        _g_fprintf (stdout, "%s", buffer);
     506                        state = 0;
     507                        idx = 0;
     508  #ifdef USE_LLDB
     509                        line_idx = 0;
     510  #endif
     511                      }
     512                    break;
     513                  default:
     514                    break;
     515                  }
     516              }
     517          }
     518        else if (stack_trace_done)
     519          break;
     520      }
     521  
     522    close (in_fd[0]);
     523    close (in_fd[1]);
     524    close (out_fd[0]);
     525    close (out_fd[1]);
     526    _exit (0);
     527  }
     528  
     529  #endif /* !G_OS_WIN32 */