#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_GOOGLE_BREAKPAD #include "client/linux/handler/exception_handler.h" using namespace google_breakpad; #else #include #endif extern "C" int gtk_module_init (int *argc, char** argv[]); static bool run_bug_buddy (const gchar *appname, pid_t pid, const gchar *minidump_path); static bool check_if_gdb (void *callback_context); static gchar *bugbuddy; #ifdef ENABLE_GOOGLE_BREAKPAD // Callback when minidump written. static bool MinidumpCallback(const char *dump_path, const char *minidump_id, void *context, bool succeeded) { gchar *appname; gchar *minidump_file; printf("%s is dumped\n", minidump_id); appname = g_get_prgname (); minidump_file = g_strdup_printf ("/tmp/%s.dmp", minidump_id); run_bug_buddy (appname, 0, minidump_file); g_unlink (minidump_file); g_free (minidump_file); _exit(0); } #else static void bugbuddy_segv_handle(int signum) { static int in_segv = 0; struct sigaction sa; pid_t pid; sa.sa_handler = NULL; in_segv++; if (in_segv > 2) { /* The fprintf() was segfaulting, we are just totally hosed */ _exit(1); } else if (in_segv > 1) { /* dialog display isn't working out */ fprintf(stderr, "Multiple segmentation faults occurred; can't display error dialog\n"); _exit(1); } /* Make sure we release grabs */ gdk_pointer_ungrab(GDK_CURRENT_TIME); gdk_keyboard_ungrab(GDK_CURRENT_TIME); XUngrabServer (GDK_DISPLAY ()); gdk_flush(); check_if_gdb (NULL); /* If we are here is because gdb couldn't be run. * FIXME: Show some error? */ _exit(1); } #endif #define N_TRIES 3 static gboolean find_in_debug_path (const char *filename, const char *debug_filename) { char *dir; char *tries[N_TRIES]; int i; dir = g_path_get_dirname (filename); tries[0] = g_build_filename (dir, debug_filename, NULL); tries[1] = g_build_filename (dir, ".debug", debug_filename, NULL); tries[2] = g_build_filename ("/usr", "lib", "debug", dir, debug_filename, NULL); g_free (dir); for (i = 0; i < N_TRIES; ++i) { if (g_file_test (tries[i], G_FILE_TEST_EXISTS)) return true; } return false; } static gboolean elf_has_debug_symbols (const char *filename) { int fd; Elf *elf; GElf_Ehdr elf_header; Elf_Scn *section = 0; int number = 0; if (elf_version(EV_CURRENT) == EV_NONE ) { fprintf(stderr, "Elf library out of date!n"); return false; } fd = open(filename, O_RDONLY); if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL){ close (fd); return false; } gelf_getehdr (elf, &elf_header); while ((section = elf_nextscn(elf, section)) != 0) { char *name = 0; GElf_Shdr shdr; /* Check for stabs debug information */ if (gelf_getshdr (section, &shdr) != 0) { if (shdr.sh_type == SHT_SYMTAB) { elf_end (elf); return true; } } /* Check for .gnu_debuglink separete debug file */ if (shdr.sh_type == SHT_PROGBITS) { char *name = elf_strptr(elf, elf_header.e_shstrndx, shdr.sh_name); if (strcmp (name, ".gnu_debuglink") == 0) { Elf_Data *edata; edata = elf_getdata(section, NULL); if (edata != NULL && find_in_debug_path (filename, (const char*) edata->d_buf)) { elf_end (elf); return true; } } } } /* no symtab neither debug file present */ elf_end (elf); return false; } static bool release_grabs (void) { /* Make sure we release grabs */ gdk_pointer_ungrab(GDK_CURRENT_TIME); gdk_keyboard_ungrab(GDK_CURRENT_TIME); XUngrabServer (GDK_DISPLAY ()); gdk_flush(); return true; } static bool run_bug_buddy (const gchar *appname, pid_t pid, const gchar *minidump_path) { gchar *exec_str; gboolean res; GError *error = NULL; if (pid != 0) exec_str = g_strdup_printf("bug-buddy " "--appname=\"%s\" " "--pid=%d", appname,(int) pid); else if (minidump_path != NULL) exec_str = g_strdup_printf("bug-buddy " "--appname=\"%s\" " "--minidump=%s", appname, minidump_path); else return false; res = g_spawn_command_line_sync (exec_str, NULL, NULL, NULL, &error); g_free(exec_str); if (!res) { g_warning("Couldn't run bug-buddy\n"); return false; } return true; } static bool run_gdb (const gchar *appname, pid_t pid) { gchar *exec_str; gchar *title; gboolean res; GError *error = NULL; title = g_strdup_printf ("Debugging %s", appname); exec_str = g_strdup_printf("gnome-terminal " "--title=\"%s\" " "--disable-factory " "--command=\"gdb %s %d\"", title, appname, (int)pid); g_free (title); res = g_spawn_command_line_sync (exec_str, NULL, NULL, NULL, &error); g_free(exec_str); if (!res) { g_warning("Couldn't run debugger\n"); return false; } return true; } static bool check_if_gdb (void *callback_context) { char mypath[255]; gchar *gdb; bool has_debug_symbols; char *filename; gchar *appname; pid_t pid; gboolean res; release_grabs (); if (g_getenv ("GNOME_DISABLE_CRASH_DIALOG")) _exit(0); appname = g_get_prgname (); pid = getpid (); gdb = g_find_program_in_path ("gdb"); if (gdb && g_getenv("GNOME_HACKER")) { res = run_gdb (appname, pid); if (!res) _exit (1); _exit(0); } memset(mypath, 0, sizeof(mypath)); readlink ("/proc/self/exe", mypath, sizeof(mypath)); has_debug_symbols = elf_has_debug_symbols (mypath); if (bugbuddy && gdb && has_debug_symbols) { res = run_bug_buddy (appname, pid, NULL); if (!res) _exit (1); _exit(0); } return true; } int gtk_module_init (int *argc, char** argv[]) { bugbuddy = g_find_program_in_path ("bug-buddy"); if (bugbuddy && !g_getenv ("GNOME_DISABLE_CRASH_DIALOG")) { #ifdef ENABLE_GOOGLE_BREAKPAD static struct sigaction old_action; sigaction(SIGSEGV, NULL, &old_action); if (old_action.sa_handler != SIG_DFL) return 0; sigaction(SIGABRT, NULL, &old_action); if (old_action.sa_handler != SIG_DFL) return 0; sigaction(SIGTRAP, NULL, &old_action); if (old_action.sa_handler != SIG_DFL) return 0; sigaction(SIGFPE, NULL, &old_action); if (old_action.sa_handler != SIG_DFL) return 0; sigaction(SIGBUS, NULL, &old_action); if (old_action.sa_handler != SIG_DFL) return 0; static ExceptionHandler handler("/tmp", check_if_gdb, MinidumpCallback, NULL, true); #else static struct sigaction *setptr; static struct sigaction old_action; struct sigaction sa; memset(&sa, 0, sizeof(sa)); setptr = &sa; sa.sa_handler = bugbuddy_segv_handle; sigaction(SIGSEGV, NULL, &old_action); if (old_action.sa_handler == SIG_DFL) sigaction(SIGSEGV, setptr, NULL); sigaction(SIGABRT, NULL, &old_action); if (old_action.sa_handler == SIG_DFL) sigaction(SIGABRT, setptr, NULL); sigaction(SIGTRAP, NULL, &old_action); if (old_action.sa_handler == SIG_DFL) sigaction(SIGTRAP, setptr, NULL); sigaction(SIGFPE, NULL, &old_action); if (old_action.sa_handler == SIG_DFL) sigaction(SIGFPE, setptr, NULL); sigaction(SIGBUS, NULL, &old_action); if (old_action.sa_handler == SIG_DFL) sigaction(SIGBUS, setptr, NULL); #endif } return 0; }