567e55f5d9129869518fcd47f75507b42166ca26
[yaz-moved-to-github.git] / src / backtrace.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5
6 /**
7  * \file
8  * \brief get information for abnormal terminated, crashes, etc
9  */
10
11
12 #if HAVE_CONFIG_H
13 #include "config.h"
14 #endif
15
16 #include <signal.h>
17 #include <string.h>
18 #include <errno.h>
19 #if HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
22 #include <stdlib.h>
23 #if HAVE_SYS_WAIT_H
24 #include <sys/wait.h>
25 #endif
26 #include <yaz/log.h>
27 #include <yaz/snprintf.h>
28 #include <yaz/backtrace.h>
29 #include <yaz/nmem.h>
30
31 #if HAVE_SYS_TYPES_H
32 #include <sys/types.h>
33 #endif
34
35 #if HAVE_EXECINFO_H
36 #include <execinfo.h>
37 #endif
38
39 #define BACKTRACE_SZ 100
40
41 static char static_progname[256];
42
43 static void yaz_invoke_backtrace(char *buf, int buf_sz)
44 {
45     FILE *file = yaz_log_file();
46     int fd = fileno(file);
47 #if HAVE_EXECINFO_H
48     pid_t pid;
49     int fds[2];
50     void *backtrace_info[BACKTRACE_SZ];
51     int sz = BACKTRACE_SZ;
52
53     write(fd, buf, strlen(buf));
54     sz = backtrace(backtrace_info, sz);
55     backtrace_symbols_fd(backtrace_info, sz, fd);
56
57     pipe(fds);
58     pid = fork();
59     if (pid == (pid_t) (-1))
60     {   /* error */
61         const char *cp = "backtrace: fork failure";
62         write(fd, cp, strlen(cp));
63     }
64     else if (pid == 0)
65     {   /* child */
66         char *arg[20];
67         int arg_no = 0;
68         char pidstr[40];
69         const char *cp = "backtrace: could not exec gdb";
70
71         close(fds[1]);
72         close(0);
73         dup(fds[0]);
74         if (fd != 1)
75         {
76             close(1);
77             dup(fd);
78         }
79         if (fd != 2)
80         {
81             close(2);
82             dup(fd);
83         }
84         arg[arg_no++] = "/usr/bin/gdb";
85         arg[arg_no++] = "-n";
86         arg[arg_no++] = "-batch";
87         arg[arg_no++] = "-ex";
88         arg[arg_no++] = "info threads";
89         arg[arg_no++] = "-ex";
90         arg[arg_no++] = "thread apply all bt";
91         arg[arg_no++] = static_progname;
92         sprintf(pidstr, NMEM_INT_PRINTF, (nmem_int_t) getppid());
93         arg[arg_no++] = pidstr;
94         arg[arg_no] = 0;
95         execv(arg[0], arg);
96         write(2, cp, strlen(cp)); /* exec failure if we make it this far */
97         _exit(1);
98     }
99     else
100     {  /* parent */
101         int sec = 0;
102
103         close(fds[0]);
104         write(fds[1], "quit\n", 5);
105         while (1)
106         {
107             int status;
108             pid_t s = waitpid(pid, &status, WNOHANG);
109             if (s != 0)
110                 break;
111             if (sec == 9)
112                 kill(pid, SIGTERM);
113             if (sec == 10)
114                 kill(pid, SIGKILL);
115             if (sec == 11)
116                 break;
117             if (sec > 3)
118                 write(fds[1], "quit\n", 5);
119             sleep(1);
120             sec++;
121         }
122         close(fds[1]);
123     }
124 #else
125     strcat(buf, "no backtrace support (execinfo.h not found)\n");
126     write(fd, buf, strlen(buf));
127 #endif
128 }
129
130 static void yaz_panic_sig_handler(int sig)
131 {
132     char buf[512];
133
134     signal(SIGABRT, SIG_DFL);
135     strcpy(buf, "\nYAZ panic received ");
136     switch (sig)
137     {
138     case SIGSEGV:
139         strcat(buf, "SIGSEGV");
140         break;
141     case SIGABRT:
142         strcat(buf, "SIGABRT");
143         break;
144     case SIGFPE:
145         strcat(buf, "SIGFPE");
146         break;
147     case SIGBUS:
148         strcat(buf, "SIGBUS");
149         break;
150     default:
151         yaz_snprintf(buf + strlen(buf), sizeof buf, "signo=%d", sig);
152         break;
153     }
154     yaz_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf) - 1,
155                  " PID=" NMEM_INT_PRINTF "\n", (nmem_int_t) getpid());
156     yaz_invoke_backtrace(buf, sizeof buf);
157     abort();
158 }
159
160 void yaz_enable_panic_backtrace(const char *progname)
161 {
162     strncpy(static_progname, progname, sizeof(static_progname) - 1);
163     static_progname[sizeof(static_progname) - 1] = '\0';
164 #if HAVE_EXECINFO_H
165     signal(SIGABRT, yaz_panic_sig_handler);
166     signal(SIGSEGV, yaz_panic_sig_handler);
167     signal(SIGFPE, yaz_panic_sig_handler);
168     signal(SIGBUS, yaz_panic_sig_handler);
169 #endif
170 }
171
172 /*
173  * Local variables:
174  * c-basic-offset: 4
175  * c-file-style: "Stroustrup"
176  * indent-tabs-mode: nil
177  * End:
178  * vim: shiftwidth=4 tabstop=8 expandtab
179  */
180