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