Dump backtrace in case of SIGABRT, SIGSEGV YAZ-787
[yaz-moved-to-github.git] / src / backtrace.c
diff --git a/src/backtrace.c b/src/backtrace.c
new file mode 100644 (file)
index 0000000..567e55f
--- /dev/null
@@ -0,0 +1,180 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) Index Data
+ * See the file LICENSE for details.
+ */
+
+/**
+ * \file
+ * \brief get information for abnormal terminated, crashes, etc
+ */
+
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#include <yaz/log.h>
+#include <yaz/snprintf.h>
+#include <yaz/backtrace.h>
+#include <yaz/nmem.h>
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+#define BACKTRACE_SZ 100
+
+static char static_progname[256];
+
+static void yaz_invoke_backtrace(char *buf, int buf_sz)
+{
+    FILE *file = yaz_log_file();
+    int fd = fileno(file);
+#if HAVE_EXECINFO_H
+    pid_t pid;
+    int fds[2];
+    void *backtrace_info[BACKTRACE_SZ];
+    int sz = BACKTRACE_SZ;
+
+    write(fd, buf, strlen(buf));
+    sz = backtrace(backtrace_info, sz);
+    backtrace_symbols_fd(backtrace_info, sz, fd);
+
+    pipe(fds);
+    pid = fork();
+    if (pid == (pid_t) (-1))
+    {   /* error */
+        const char *cp = "backtrace: fork failure";
+        write(fd, cp, strlen(cp));
+    }
+    else if (pid == 0)
+    {   /* child */
+        char *arg[20];
+        int arg_no = 0;
+        char pidstr[40];
+        const char *cp = "backtrace: could not exec gdb";
+
+        close(fds[1]);
+        close(0);
+        dup(fds[0]);
+        if (fd != 1)
+        {
+            close(1);
+            dup(fd);
+        }
+        if (fd != 2)
+        {
+            close(2);
+            dup(fd);
+        }
+        arg[arg_no++] = "/usr/bin/gdb";
+        arg[arg_no++] = "-n";
+        arg[arg_no++] = "-batch";
+        arg[arg_no++] = "-ex";
+        arg[arg_no++] = "info threads";
+        arg[arg_no++] = "-ex";
+        arg[arg_no++] = "thread apply all bt";
+        arg[arg_no++] = static_progname;
+        sprintf(pidstr, NMEM_INT_PRINTF, (nmem_int_t) getppid());
+        arg[arg_no++] = pidstr;
+        arg[arg_no] = 0;
+        execv(arg[0], arg);
+        write(2, cp, strlen(cp)); /* exec failure if we make it this far */
+        _exit(1);
+    }
+    else
+    {  /* parent */
+        int sec = 0;
+
+        close(fds[0]);
+        write(fds[1], "quit\n", 5);
+        while (1)
+        {
+            int status;
+            pid_t s = waitpid(pid, &status, WNOHANG);
+            if (s != 0)
+                break;
+            if (sec == 9)
+                kill(pid, SIGTERM);
+            if (sec == 10)
+                kill(pid, SIGKILL);
+            if (sec == 11)
+                break;
+            if (sec > 3)
+                write(fds[1], "quit\n", 5);
+            sleep(1);
+            sec++;
+        }
+        close(fds[1]);
+    }
+#else
+    strcat(buf, "no backtrace support (execinfo.h not found)\n");
+    write(fd, buf, strlen(buf));
+#endif
+}
+
+static void yaz_panic_sig_handler(int sig)
+{
+    char buf[512];
+
+    signal(SIGABRT, SIG_DFL);
+    strcpy(buf, "\nYAZ panic received ");
+    switch (sig)
+    {
+    case SIGSEGV:
+        strcat(buf, "SIGSEGV");
+        break;
+    case SIGABRT:
+        strcat(buf, "SIGABRT");
+        break;
+    case SIGFPE:
+        strcat(buf, "SIGFPE");
+        break;
+    case SIGBUS:
+        strcat(buf, "SIGBUS");
+        break;
+    default:
+        yaz_snprintf(buf + strlen(buf), sizeof buf, "signo=%d", sig);
+        break;
+    }
+    yaz_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf) - 1,
+                 " PID=" NMEM_INT_PRINTF "\n", (nmem_int_t) getpid());
+    yaz_invoke_backtrace(buf, sizeof buf);
+    abort();
+}
+
+void yaz_enable_panic_backtrace(const char *progname)
+{
+    strncpy(static_progname, progname, sizeof(static_progname) - 1);
+    static_progname[sizeof(static_progname) - 1] = '\0';
+#if HAVE_EXECINFO_H
+    signal(SIGABRT, yaz_panic_sig_handler);
+    signal(SIGSEGV, yaz_panic_sig_handler);
+    signal(SIGFPE, yaz_panic_sig_handler);
+    signal(SIGBUS, yaz_panic_sig_handler);
+#endif
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+