[gimp] Bug 793514 - Adding version check for gdb.
- From: Jehan Pagès <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] Bug 793514 - Adding version check for gdb.
- Date: Thu, 22 Feb 2018 04:54:29 +0000 (UTC)
commit 6975188d6f8fcb1e84e4e7a2632bc3ebf620ea55
Author: Jehan <jehan girinstud io>
Date: Thu Feb 22 05:05:24 2018 +0100
Bug 793514 - Adding version check for gdb.
It seems that older GDB (under version 7) are not handling very well
some common debug information format, in particular DWARF > 3. Such
version of GDB is usually not a problem since it is quite old (more than
10 years old, it would seem) so you don't see it anymore on any modern
GNU/Linux distribution. On FreeBSD on the other hand, it is still
available (probably for license reasons) and even installed by default!
As a consequence, it makes debugging fail, even though LLDB is also
installed by default.
That is even more of a problem because it would seem that GIMP is killed
(most likely by FreeBSD kernel according to the reporter tests) as a
side-effect of GDB failing, which is seriously bad, in particular since
we also use the debug dialog for non-fatal errors (which could therefore
end up killing GIMP as side effect of a bad GDB!).
So I add some GDB version check. I implement this without any dynamic
memory management, as usual, since this needs to happen also during
crash handling where the state is unstable and prone to memory
allocation failure.
I also add gimp_utils_backtrace_available() public API which can be used
by the Preferences.
libgimpbase/gimputils.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++-
libgimpbase/gimputils.h | 1 +
2 files changed, 182 insertions(+), 3 deletions(-)
---
diff --git a/libgimpbase/gimputils.c b/libgimpbase/gimputils.c
index c4b7da1..d3dbbcd 100644
--- a/libgimpbase/gimputils.c
+++ b/libgimpbase/gimputils.c
@@ -64,6 +64,13 @@
* Utilities of general interest
**/
+static gboolean gimp_utils_generic_available (const gchar *program,
+ gint major,
+ gint minor);
+static gboolean gimp_utils_gdb_available (gint major,
+ gint minor);
+static gboolean gimp_utils_lldb_available (gint major,
+ gint minor);
/**
* gimp_utf8_strtrim:
@@ -1146,10 +1153,17 @@ gimp_print_stack_trace (const gchar *prog_name,
close (out_fd[0]);
close (out_fd[1]);
- /* Run GDB. */
- if (execvp (args[0], args) == -1)
+ /* Run GDB if version 7.0 or over. Why I do such a check is that
+ * it turns out older versions may not only fail, but also have
+ * very undesirable side effects like terminating the debugged
+ * program, at least on FreeBSD where GDB 6.1 is apparently
+ * installed by default on the stable release at day of writing.
+ * See bug 793514. */
+ if (! gimp_utils_gdb_available (7, 0) ||
+ execvp (args[0], args) == -1)
{
- /* LLDB as alternative. */
+ /* LLDB as alternative if the GDB call failed or if it was in
+ * a too-old version. */
gchar *args_lldb[11] = { "lldb", "--attach-pid", NULL, "--batch",
"--one-line", "bt",
"--one-line-on-crash", "bt",
@@ -1306,3 +1320,167 @@ gimp_on_error_query (const gchar *prog_name)
goto retry;
#endif
}
+
+/**
+ * gimp_utils_backtrace_available:
+ * @optimal: whether we get optimal traces.
+ *
+ * Returns #TRUE if we have dependencies to generate backtraces. If
+ * @optimal is #TRUE, the function will return #TRUE only when we
+ * are able to generate optimal traces (i.e. with GDB or LLDB);
+ * otherwise we return #TRUE even if only backtrace() API is available.
+ *
+ * On Win32, we return TRUE if Dr. Mingw is built-in, FALSE otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_utils_backtrace_available (gboolean optimal)
+{
+#ifndef G_OS_WIN32
+ if (gimp_utils_gdb_available (7, 0) ||
+ gimp_utils_lldb_available (0, 0))
+ return TRUE;
+#ifdef HAVE_EXECINFO_H
+ if (! optimal)
+ return TRUE;
+#endif
+#else /* G_OS_WIN32 */
+#ifdef HAVE_EXCHNDL
+ return TRUE;
+#endif
+#endif /* G_OS_WIN32 */
+ return FALSE;
+}
+
+/* Private functions. */
+
+static gboolean
+gimp_utils_generic_available (const gchar *program,
+ gint major,
+ gint minor)
+{
+#ifndef G_OS_WIN32
+ pid_t pid;
+ int out_fd[2];
+
+ if (pipe (out_fd) == -1)
+ {
+ return FALSE;
+ }
+
+ /* XXX: I don't use g_spawn_sync() or similar glib functions because
+ * to read the contents of the stdout, these functions would allocate
+ * memory dynamically. As we know, when debugging crashes, this is a
+ * definite blocker. So instead I simply use a buffer on the stack
+ * with a lower level fork() call.
+ */
+ pid = fork ();
+ if (pid == 0)
+ {
+ /* Child process. */
+ gchar *args[3] = { (gchar *) program, "--version", NULL };
+
+ /* Redirect the debugger output. */
+ dup2 (out_fd[1], STDOUT_FILENO);
+ close (out_fd[0]);
+ close (out_fd[1]);
+
+ /* Run version check. */
+ execvp (args[0], args);
+ _exit (-1);
+ }
+ else if (pid > 0)
+ {
+ /* Main process */
+ gchar buffer[256];
+ ssize_t read_n;
+ int status;
+ gint installed_major = 0;
+ gint installed_minor = 0;
+ gboolean major_reading = FALSE;
+ gboolean minor_reading = FALSE;
+ gint i;
+ gchar c;
+
+ waitpid (pid, &status, 0);
+
+ if (! WIFEXITED (status) || WEXITSTATUS (status) != 0)
+ return FALSE;
+
+ /* 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]);
+
+ /* I could loop forever until EOL, but I am pretty sure the
+ * version information is stored on the first line and one call to
+ * read() with 256 characters should be more than enough.
+ */
+ read_n = read (out_fd[0], buffer, 256);
+
+ /* This is quite a very stupid parser. I only look for the first
+ * numbers and consider them as version information. This works
+ * fine for both GDB and LLDB as far as I can see for the output
+ * of `${program} --version` but this should obviously not be
+ * considered as a *really* generic version test.
+ */
+ for (i = 0; i < read_n; i++)
+ {
+ c = buffer[i];
+ if (c >= '0' && c <= '9')
+ {
+ if (minor_reading)
+ {
+ installed_minor = 10 * installed_minor + (c - '0');
+ }
+ else
+ {
+ major_reading = TRUE;
+ installed_major = 10 * installed_major + (c - '0');
+ }
+ }
+ else if (c == '.')
+ {
+ if (major_reading)
+ {
+ minor_reading = TRUE;
+ major_reading = FALSE;
+ }
+ else if (minor_reading)
+ {
+ break;
+ }
+ }
+ else if (c == '\n')
+ {
+ /* Version information should be in the first line. */
+ break;
+ }
+ }
+ close (out_fd[0]);
+
+ return (installed_major > 0 &&
+ (installed_major > major ||
+ (installed_major == major && installed_minor >= minor)));
+ }
+#endif
+
+ /* Fork failed, or Win32. */
+ return FALSE;
+}
+
+static gboolean
+gimp_utils_gdb_available (gint major,
+ gint minor)
+{
+ return gimp_utils_generic_available ("gdb", major, minor);
+}
+
+static gboolean
+gimp_utils_lldb_available (gint major,
+ gint minor)
+{
+ return gimp_utils_generic_available ("lldb", major, minor);
+}
diff --git a/libgimpbase/gimputils.h b/libgimpbase/gimputils.h
index 2eefa26..a759767 100644
--- a/libgimpbase/gimputils.h
+++ b/libgimpbase/gimputils.h
@@ -79,6 +79,7 @@ gboolean gimp_print_stack_trace (const gchar *prog_name,
gpointer stream,
gchar **trace);
void gimp_on_error_query (const gchar *prog_name);
+gboolean gimp_utils_backtrace_available (gboolean optimal);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]