#include <pthread.h>
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <cstring>
#ifdef __FreeBSD__
#include <sys/param.h>
#endif
#if !defined(__FreeBSD__) || __FreeBSD_version > 700024
#include <libelf.h>
#include <gelf.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <config.h>
#ifdef ENABLE_GOOGLE_BREAKPAD
#include "client/linux/handler/exception_handler.h"
using namespace google_breakpad;
#else
#include <string.h>
#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;
}
#if !defined(__FreeBSD__) || __FreeBSD_version > 700024
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;
}
#endif
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);
}
#if !defined(__FreeBSD__) || __FreeBSD_version > 700024
memset(mypath, 0, sizeof(mypath));
#ifndef __FreeBSD__
readlink ("/proc/self/exe", mypath, sizeof(mypath));
#else
readlink ("/proc/curproc/file", mypath, sizeof(mypath));
#endif
has_debug_symbols = elf_has_debug_symbols (mypath);
#else
has_debug_symbols = TRUE;
#endif
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;
}
syntax highlighted by Code2HTML, v. 0.9.1