[gimp] app, libgimpbase: move the debug functions to libgimpbase.
- From: Jehan Pagès <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app, libgimpbase: move the debug functions to libgimpbase.
- Date: Fri, 9 Feb 2018 01:31:51 +0000 (UTC)
commit 276f07521caf46342e435dfcb9c4acfbc26624fd
Author: Jehan <jehan girinstud io>
Date: Fri Feb 9 01:57:03 2018 +0100
app, libgimpbase: move the debug functions to libgimpbase.
This will allow to use them on plug-ins later on.
app/errors.c | 209 ++------------------------------------------
libgimpbase/gimputils.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++
libgimpbase/gimputils.h | 5 +
3 files changed, 236 insertions(+), 202 deletions(-)
---
diff --git a/app/errors.c b/app/errors.c
index 22059de..62b326a 100644
--- a/app/errors.c
+++ b/app/errors.c
@@ -25,15 +25,7 @@
#include <unistd.h>
#endif
-#include <sys/wait.h>
-
-#ifdef HAVE_EXECINFO_H
-/* Allowing backtrace() API. */
-#include <execinfo.h>
-#endif
-
#include <gio/gio.h>
-#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include "libgimpbase/gimpbase.h"
@@ -78,10 +70,6 @@ static G_GNUC_NORETURN void gimp_eek (const gchar *reason,
const gchar *message,
gboolean use_handler);
-static gboolean gimp_print_stack_trace (FILE *file,
- gchar **trace);
-static void gimp_on_error_query (void);
-
/* public functions */
@@ -241,7 +229,8 @@ gimp_message_log_func (const gchar *log_domain,
* Hence when this happens, critical errors are simply processed as
* lower level errors.
*/
- gimp_print_stack_trace (NULL, &trace);
+ gimp_print_stack_trace ((const gchar *) full_prog_name,
+ NULL, &trace);
n_traces++;
}
}
@@ -358,7 +347,8 @@ gimp_eek (const gchar *reason,
* and is waiting for us in a text file.
*/
fd = g_fopen (backtrace_file, "w");
- has_backtrace = gimp_print_stack_trace (fd, NULL);
+ has_backtrace = gimp_print_stack_trace ((const gchar *) full_prog_name,
+ fd, NULL);
fclose (fd);
#endif
@@ -392,7 +382,7 @@ gimp_eek (const gchar *reason,
sigemptyset (&sigset);
sigprocmask (SIG_SETMASK, &sigset, NULL);
- gimp_on_error_query ();
+ gimp_on_error_query ((const gchar *) full_prog_name);
}
break;
@@ -403,7 +393,8 @@ gimp_eek (const gchar *reason,
sigemptyset (&sigset);
sigprocmask (SIG_SETMASK, &sigset, NULL);
- gimp_print_stack_trace (stdout, NULL);
+ gimp_print_stack_trace ((const gchar *) full_prog_name,
+ stdout, NULL);
}
break;
@@ -424,189 +415,3 @@ gimp_eek (const gchar *reason,
exit (EXIT_FAILURE);
}
-
-/* In some error cases (e.g. segmentation fault), trying to allocate
- * more memory will trigger more segmentation faults and therefore loop
- * our error handling (which is just wrong). Therefore printing to a
- * file description is an implementation without any memory allocation.
- * On the other hand, if trace is not #NULL, a newly-allocated string
- * will be returned.
- */
-static gboolean
-gimp_print_stack_trace (FILE *file,
- gchar **trace)
-{
- gboolean stack_printed = FALSE;
-
- /* This works only on UNIX systems. */
-#ifndef G_OS_WIN32
- GString *gtrace = NULL;
- gchar gimp_pid[16];
- pid_t pid;
- gchar buffer[256];
- ssize_t read_n;
- int out_fd[2];
-
- g_snprintf (gimp_pid, 16, "%u", (guint) getpid ());
-
- if (pipe (out_fd) == -1)
- {
- return FALSE;
- }
-
- pid = fork ();
- if (pid == 0)
- {
- /* Child process. */
- gchar *args[7] = { "gdb", "-batch", "-ex", "backtrace full",
- full_prog_name, NULL, NULL };
-
- args[5] = gimp_pid;
-
- /* Redirect the debugger output. */
- dup2 (out_fd[1], STDOUT_FILENO);
- close (out_fd[0]);
- close (out_fd[1]);
-
- /* Run GDB. */
- if (execvp (args[0], args) == -1)
- {
- /* LLDB as alternative. */
- gchar *args_lldb[11] = { "lldb", "--attach-pid", NULL, "--batch",
- "--one-line", "bt",
- "--one-line-on-crash", "bt",
- "--one-line-on-crash", "quit", NULL };
-
- args_lldb[2] = gimp_pid;
-
- execvp (args_lldb[0], args_lldb);
- }
-
- _exit (0);
- }
- else if (pid > 0)
- {
- /* Main process */
- int status;
-
- waitpid (pid, &status, 0);
-
- /* It is important to close the writing side of the pipe, otherwise
- * the read() will wait forever without getting the information that
- * writing is finished.
- */
- close (out_fd[1]);
-
- while ((read_n = read (out_fd[0], buffer, 256)) > 0)
- {
- /* It's hard to know if the debugger was found since it
- * happened in the child. Let's just assume that any output
- * means it succeeded.
- */
- stack_printed = TRUE;
-
- buffer[read_n] = '\0';
- if (file)
- g_fprintf (file, "%s", buffer);
- if (trace)
- {
- if (! gtrace)
- gtrace = g_string_new (NULL);
- g_string_append (gtrace, (const gchar *) buffer);
- }
- }
- close (out_fd[0]);
- }
- else if (pid == (pid_t) -1)
- {
- /* Fork failed. */
- return FALSE;
- }
-
-#ifdef HAVE_EXECINFO_H
- if (! stack_printed)
- {
- /* As a last resort, try using the backtrace() Linux API. It is a bit
- * less fancy than gdb or lldb, which is why it is not given priority.
- */
- void *bt_buf[100];
- char **symbols;
- int n_symbols;
- int i;
-
- n_symbols = backtrace (bt_buf, 100);
- symbols = backtrace_symbols (bt_buf, n_symbols);
- if (symbols)
- {
- stack_printed = TRUE;
-
- for (i = 0; i < n_symbols; i++)
- {
- if (file)
- g_fprintf (file, "%s\n", (const gchar *) symbols[i]);
- if (trace)
- {
- if (! gtrace)
- gtrace = g_string_new (NULL);
- g_string_append (gtrace,
- (const gchar *) symbols[i]);
- g_string_append_c (gtrace, '\n');
- }
- }
- free (symbols);
- }
- }
-#endif /* HAVE_EXECINFO_H */
-
- if (trace)
- {
- if (gtrace)
- *trace = g_string_free (gtrace, FALSE);
- else
- *trace = NULL;
- }
-#endif /* G_OS_WIN32 */
-
- return stack_printed;
-}
-
-/* This is mostly the same as g_on_error_query() except that we use our
- * own backtrace function, much more complete.
- */
-static void
-gimp_on_error_query (void)
-{
-#ifndef G_OS_WIN32
- gchar buf[16];
-
- retry:
-
- g_fprintf (stdout,
- "%s (pid:%u): %s: ",
- full_prog_name,
- (guint) getpid (),
- "[E]xit, show [S]tack trace or [P]roceed");
- fflush (stdout);
-
- if (isatty(0) && isatty(1))
- fgets (buf, 8, stdin);
- else
- strcpy (buf, "E\n");
-
- if ((buf[0] == 'E' || buf[0] == 'e')
- && buf[1] == '\n')
- _exit (0);
- else if ((buf[0] == 'P' || buf[0] == 'p')
- && buf[1] == '\n')
- return;
- else if ((buf[0] == 'S' || buf[0] == 's')
- && buf[1] == '\n')
- {
- if (! gimp_print_stack_trace (stdout, NULL))
- g_fprintf (stderr, "%s\n", "Stack trace not available on your system.");
- goto retry;
- }
- else
- goto retry;
-#endif
-}
diff --git a/libgimpbase/gimputils.c b/libgimpbase/gimputils.c
index 07990c8..67f8df1 100644
--- a/libgimpbase/gimputils.c
+++ b/libgimpbase/gimputils.c
@@ -21,14 +21,23 @@
#include "config.h"
+#include <stdlib.h>
#include <string.h>
#include <stdio.h>
+#include <sys/wait.h>
+
#ifdef PLATFORM_OSX
#include <AppKit/AppKit.h>
#endif
+#ifdef HAVE_EXECINFO_H
+/* Allowing backtrace() API. */
+#include <execinfo.h>
+#endif
+
#include <gio/gio.h>
+#include <glib/gprintf.h>
#if defined(G_OS_WIN32)
/* This is a hack for Windows known directory support.
@@ -1067,3 +1076,218 @@ gimp_flags_value_get_abbrev (GFlagsClass *flags_class,
return NULL;
}
+
+/**
+ * gimp_print_stack_trace:
+ * @prog_name: the program to attach to.
+ * @fd: a #FILE * file descriptor.
+ * @trace: location to store a newly allocated string of the trace.
+ *
+ * Attempts to generate a stack trace at current code position in
+ * @prog_name. @prog_name is mostly a helper, but it has to be the
+ * program name of the current program. This function is not meant to
+ * generate stack trace for third-party programs, and will attach the
+ * current process id only.
+ * Internally, this function uses `gdb` or `lldb` if they are available,
+ * or the stacktrace() API on platforms where it is available. It always
+ * fails on Win32.
+ *
+ * The stack trace, once generated, will either be printed to @fd or
+ * returned as a newly allocated string in @trace, if not #NULL.
+ *
+ * In some error cases (e.g. segmentation fault), trying to allocate
+ * more memory will trigger more segmentation faults and therefore loop
+ * our error handling (which is just wrong). Therefore printing to a
+ * file description is an implementation without any memory allocation.
+
+ * Return value: #TRUE if a stack trace could be generated, #FALSE
+ * otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_print_stack_trace (const gchar *prog_name,
+ gpointer fd,
+ gchar **trace)
+{
+ gboolean stack_printed = FALSE;
+
+ /* This works only on UNIX systems. */
+#ifndef G_OS_WIN32
+ GString *gtrace = NULL;
+ gchar gimp_pid[16];
+ pid_t pid;
+ gchar buffer[256];
+ ssize_t read_n;
+ int out_fd[2];
+
+ g_snprintf (gimp_pid, 16, "%u", (guint) getpid ());
+
+ if (pipe (out_fd) == -1)
+ {
+ return FALSE;
+ }
+
+ pid = fork ();
+ if (pid == 0)
+ {
+ /* Child process. */
+ gchar *args[7] = { "gdb", "-batch", "-ex", "backtrace full",
+ (gchar *) prog_name, NULL, NULL };
+
+ args[5] = gimp_pid;
+
+ /* Redirect the debugger output. */
+ dup2 (out_fd[1], STDOUT_FILENO);
+ close (out_fd[0]);
+ close (out_fd[1]);
+
+ /* Run GDB. */
+ if (execvp (args[0], args) == -1)
+ {
+ /* LLDB as alternative. */
+ gchar *args_lldb[11] = { "lldb", "--attach-pid", NULL, "--batch",
+ "--one-line", "bt",
+ "--one-line-on-crash", "bt",
+ "--one-line-on-crash", "quit", NULL };
+
+ args_lldb[2] = gimp_pid;
+
+ execvp (args_lldb[0], args_lldb);
+ }
+
+ _exit (0);
+ }
+ else if (pid > 0)
+ {
+ /* Main process */
+ int status;
+
+ waitpid (pid, &status, 0);
+
+ /* It is important to close the writing side of the pipe, otherwise
+ * the read() will wait forever without getting the information that
+ * writing is finished.
+ */
+ close (out_fd[1]);
+
+ while ((read_n = read (out_fd[0], buffer, 256)) > 0)
+ {
+ /* It's hard to know if the debugger was found since it
+ * happened in the child. Let's just assume that any output
+ * means it succeeded.
+ */
+ stack_printed = TRUE;
+
+ buffer[read_n] = '\0';
+ if (fd)
+ g_fprintf (fd, "%s", buffer);
+ if (trace)
+ {
+ if (! gtrace)
+ gtrace = g_string_new (NULL);
+ g_string_append (gtrace, (const gchar *) buffer);
+ }
+ }
+ close (out_fd[0]);
+ }
+ else if (pid == (pid_t) -1)
+ {
+ /* Fork failed. */
+ return FALSE;
+ }
+
+#ifdef HAVE_EXECINFO_H
+ if (! stack_printed)
+ {
+ /* As a last resort, try using the backtrace() Linux API. It is a bit
+ * less fancy than gdb or lldb, which is why it is not given priority.
+ */
+ void *bt_buf[100];
+ char **symbols;
+ int n_symbols;
+ int i;
+
+ n_symbols = backtrace (bt_buf, 100);
+ symbols = backtrace_symbols (bt_buf, n_symbols);
+ if (symbols)
+ {
+ stack_printed = TRUE;
+
+ for (i = 0; i < n_symbols; i++)
+ {
+ if (fd)
+ g_fprintf (fd, "%s\n", (const gchar *) symbols[i]);
+ if (trace)
+ {
+ if (! gtrace)
+ gtrace = g_string_new (NULL);
+ g_string_append (gtrace,
+ (const gchar *) symbols[i]);
+ g_string_append_c (gtrace, '\n');
+ }
+ }
+ free (symbols);
+ }
+ }
+#endif /* HAVE_EXECINFO_H */
+
+ if (trace)
+ {
+ if (gtrace)
+ *trace = g_string_free (gtrace, FALSE);
+ else
+ *trace = NULL;
+ }
+#endif /* G_OS_WIN32 */
+
+ return stack_printed;
+}
+
+/**
+ * gimp_print_stack_trace:
+ * @prog_name: the program to attach to.
+ *
+ * This is mostly the same as g_on_error_query() except that we use our
+ * own backtrace function, much more complete.
+ * It does nothing on Win32.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_on_error_query (const gchar *prog_name)
+{
+#ifndef G_OS_WIN32
+ gchar buf[16];
+
+ retry:
+
+ g_fprintf (stdout,
+ "%s (pid:%u): %s: ",
+ prog_name,
+ (guint) getpid (),
+ "[E]xit, show [S]tack trace or [P]roceed");
+ fflush (stdout);
+
+ if (isatty(0) && isatty(1))
+ fgets (buf, 8, stdin);
+ else
+ strcpy (buf, "E\n");
+
+ if ((buf[0] == 'E' || buf[0] == 'e')
+ && buf[1] == '\n')
+ _exit (0);
+ else if ((buf[0] == 'P' || buf[0] == 'p')
+ && buf[1] == '\n')
+ return;
+ else if ((buf[0] == 'S' || buf[0] == 's')
+ && buf[1] == '\n')
+ {
+ if (! gimp_print_stack_trace (prog_name, stdout, NULL))
+ g_fprintf (stderr, "%s\n", "Stack trace not available on your system.");
+ goto retry;
+ }
+ else
+ goto retry;
+#endif
+}
diff --git a/libgimpbase/gimputils.h b/libgimpbase/gimputils.h
index c159766..a4aef9e 100644
--- a/libgimpbase/gimputils.h
+++ b/libgimpbase/gimputils.h
@@ -75,6 +75,11 @@ const gchar * gimp_flags_value_get_help (GFlagsClass *flags_class,
const gchar * gimp_flags_value_get_abbrev (GFlagsClass *flags_class,
GFlagsValue *flags_value);
+gboolean gimp_print_stack_trace (const gchar *prog_name,
+ gpointer fd,
+ gchar **trace);
+void gimp_on_error_query (const gchar *prog_name);
+
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]