[gnome-builder] bug-buddy: fork() and execv() gdb on SIGSEGV



commit d45e4cb97f048013063ec8b7ac9f5e2b49c45e84
Author: Christian Hergert <chergert redhat com>
Date:   Sun Jun 25 17:30:36 2017 -0700

    bug-buddy: fork() and execv() gdb on SIGSEGV
    
    This can be extremely helpful in situations where it is hard to
    get a debugger in the normal process. We connect a SIGSEGV
    handler that can safely fork()/execv() a new process (meaning
    we can't use malloc, locks, etc).
    
    That gdb process will inspect our process while we wait for it
    to complete. After the gdb process exits, our crashing process
    will allow itself to exit.

 meson.build     |   12 ++++-
 src/bug-buddy.c |  130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/bug-buddy.h |   30 +++++++++++++
 src/main.c      |    8 +++
 4 files changed, 177 insertions(+), 3 deletions(-)
---
diff --git a/meson.build b/meson.build
index c39692f..814b6ff 100644
--- a/meson.build
+++ b/meson.build
@@ -274,7 +274,13 @@ subdir('doc')
 exe_link_args = [ '-pie' ]
 exe_c_args = [ '-fPIE' ]
 
-executable('gnome-builder', 'src/main.c',
+gnome_builder_sources = [
+  'src/main.c',
+  'src/bug-buddy.c',
+  'src/bug-buddy.h',
+]
+
+executable('gnome-builder', gnome_builder_sources,
         gui_app: true,
         install: true,
          c_args: exe_c_args,
@@ -283,7 +289,7 @@ executable('gnome-builder', 'src/main.c',
    dependencies: libide_deps + [libide_dep],
 )
 
-executable('gnome-builder-cli', 'src/main.c',
+executable('gnome-builder-cli', gnome_builder_sources,
         gui_app: false,
         install: true,
          c_args: exe_c_args,
@@ -292,7 +298,7 @@ executable('gnome-builder-cli', 'src/main.c',
    dependencies: libide_deps + [libide_dep],
 )
 
-executable('gnome-builder-worker', 'src/main.c',
+executable('gnome-builder-worker', gnome_builder_sources,
         gui_app: false,
         install: true,
     install_dir: get_option('libexecdir'),
diff --git a/src/bug-buddy.c b/src/bug-buddy.c
new file mode 100644
index 0000000..b3289f1
--- /dev/null
+++ b/src/bug-buddy.c
@@ -0,0 +1,130 @@
+/* bug-buddy.c
+ *
+ * Copyright (C) 2017 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "bug-buddy.h"
+
+/*
+ * This is not the bug buddy you're looking for. It's just named after GNOME's
+ * ancient Bug Buddy.
+ *
+ * This file sets up the necessary state at startup and then executes gdb from
+ * a SIGSEGV handler so that we get a useful stack trace when the process
+ * unexpectedly exits.
+ */
+
+static struct
+{
+  /* Our stashed path to the GDB binary */
+  gchar gdb_path[1024];
+  gchar commands[1024];
+} bug_buddy_state;
+
+static void
+bug_buddy_sigsegv_handler (int signum)
+{
+  gchar gdb_filename[] = "/tmp/gnome-builder-gdb-commands.XXXXXX";
+  gchar *argv[8] = { NULL };
+  GPid pid;
+  int fd = -1;
+  int status;
+
+  /* Only proceed if we have a gdb path to execute */
+  if (bug_buddy_state.gdb_path[0] == '\0')
+    goto failure;
+
+  if (-1 == (fd = g_mkstemp (gdb_filename)))
+    goto failure;
+
+  /* Call once, hope for the best. */
+  write (fd, bug_buddy_state.commands, strlen (bug_buddy_state.commands));
+  fsync (fd);
+
+  argv[0] = bug_buddy_state.gdb_path;
+  argv[1] = "-batch";
+  argv[2] = "-x";
+  argv[3] = gdb_filename;
+  argv[4] = "-nx";
+
+  close (fd);
+  fd = -1;
+
+  pid = fork ();
+
+  if (pid == 0)
+    {
+      execv (argv[0], (gchar **)argv);
+    }
+  else
+    {
+      waitpid (pid, &status, 0);
+      unlink (gdb_filename);
+    }
+
+failure:
+
+  _exit (-1);
+}
+
+void
+bug_buddy_init (void)
+{
+  GString *str = NULL;
+  gchar *gdb_path = NULL;
+
+  /*
+   * Everything needs to be prepared at startup so that we can avoid using
+   * any malloc, locks, etc in our SIGSEGV handler. So we'll find gdb right
+   * now and stash the location for later. If it disappears during runtime,
+   * that's fine, we just wont be able to invoke gdb.
+   */
+
+  gdb_path = g_find_program_in_path ("gdb");
+  if (strlen (gdb_path) < ((sizeof bug_buddy_state.gdb_path) - 1))
+    g_strlcpy (bug_buddy_state.gdb_path, gdb_path, sizeof bug_buddy_state.gdb_path);
+  else
+    goto cleanup;
+
+  /*
+   * Build our commands list. Since we know our process up front, we can just
+   * use getpid() to prepare the commands now.
+   */
+  str = g_string_new (NULL);
+  g_string_append_printf (str, "attach %"G_PID_FORMAT"\n", getpid ());
+  g_string_append (str, "info threads\n");
+  g_string_append (str, "thread apply all bt\n");
+  g_assert (str->len < sizeof bug_buddy_state.commands);
+  g_strlcpy (bug_buddy_state.commands, str->str, sizeof bug_buddy_state.commands);
+
+  /*
+   * Now register our signal handler so that we get called on SIGSEGV.
+   * We'll use that signal callback to extract the backtrace with gdb.
+   */
+  signal (SIGSEGV, bug_buddy_sigsegv_handler);
+
+cleanup:
+  g_free (gdb_path);
+  if (str != NULL)
+    g_string_free (str, TRUE);
+
+}
diff --git a/src/bug-buddy.h b/src/bug-buddy.h
new file mode 100644
index 0000000..c8f589c
--- /dev/null
+++ b/src/bug-buddy.h
@@ -0,0 +1,30 @@
+/* bug-buddy.h
+ *
+ * Copyright (C) 2017 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BUG_BUDDY_H
+#define BUG_BUDDY_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+void bug_buddy_init (void);
+
+G_END_DECLS
+
+#endif /* BUG_BUDDY_H */
diff --git a/src/main.c b/src/main.c
index 6b8f71a..cd2cdf7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -18,8 +18,14 @@
 
 #define G_LOG_DOMAIN "builder"
 
+#include <execinfo.h>
 #include <ide.h>
 #include <gtksourceview/gtksource.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bug-buddy.h"
 
 static gboolean
 verbose_cb (const gchar  *option_name,
@@ -90,6 +96,8 @@ main (int   argc,
   IdeApplication *app;
   int ret;
 
+  bug_buddy_init ();
+
   ide_log_init (TRUE, NULL);
   early_verbose_check (&argc, &argv);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]