YAZ 5.35.1
backtrace.c
Go to the documentation of this file.
1/* This file is part of the YAZ toolkit.
2 * Copyright (C) Index Data
3 * See the file LICENSE for details.
4 */
5
12#if HAVE_CONFIG_H
13#include "config.h"
14#endif
15
16#include <yaz/yconfig.h>
17
18#include <signal.h>
19#include <string.h>
20#include <errno.h>
21#if HAVE_UNISTD_H
22#include <unistd.h>
23#endif
24#include <stdlib.h>
25#if HAVE_SYS_WAIT_H
26#include <sys/wait.h>
27#endif
28#include <yaz/log.h>
29#include <yaz/snprintf.h>
30#include <yaz/backtrace.h>
31#include <yaz/nmem.h>
32
33#if HAVE_SYS_TYPES_H
34#include <sys/types.h>
35#endif
36
37#if HAVE_SYS_PRCTL_H
38#include <sys/prctl.h>
39#endif
40
41#if HAVE_EXECINFO_H
42#include <execinfo.h>
43#endif
44
45#define BACKTRACE_SZ 100
46
47static char static_progname[256];
48#if HAVE_EXECINFO_H
49static int yaz_panic_fd = -1;
50
51static void iwrite(int fd, const char *buf, size_t len)
52{
53 while (len)
54 {
55 ssize_t r = write(fd, buf, len);
56 if (r == -1)
57 {
58 if (errno == EINTR)
59 continue;
60 break;
61 }
62 buf += r;
63 len -= r;
64 }
65}
66
67static void yaz_invoke_gdb(void)
68{
69 int fd = yaz_panic_fd;
70 pid_t pid;
71 int fds[2];
72 if (pipe(fds) == -1)
73 {
74 const char *cp = "backtrace: pipe failed\n";
75 iwrite(fd, cp, strlen(cp));
76 return;
77 }
78 pid = fork();
79 if (pid == (pid_t) (-1))
80 { /* error */
81 const char *cp = "backtrace: fork failure\n";
82 iwrite(fd, cp, strlen(cp));
83 }
84 else if (pid == 0)
85 { /* child */
86 char *arg[20];
87 int arg_no = 0;
88 char pidstr[40];
89 const char *cp = "backtrace: could not exec gdb\n";
90
91 close(fds[1]);
92 close(0);
93 if (dup(fds[0]) != 0)
94 _exit(1);
95 if (fd != 1)
96 {
97 close(1);
98 if (dup(fd) != 1)
99 _exit(1);
100 }
101 if (fd != 2)
102 {
103 close(2);
104 if (dup(fd) != 2)
105 _exit(1);
106 }
107 arg[arg_no++] = "/usr/bin/gdb";
108 arg[arg_no++] = "-n";
109 arg[arg_no++] = "-batch";
110 arg[arg_no++] = "-ex";
111 arg[arg_no++] = "info threads";
112 arg[arg_no++] = "-ex";
113 arg[arg_no++] = "thread apply all bt";
114 arg[arg_no++] = static_progname;
115 sprintf(pidstr, NMEM_INT_PRINTF, (nmem_int_t) getppid());
116 arg[arg_no++] = pidstr;
117 arg[arg_no] = 0;
118 execv(arg[0], arg);
119 iwrite(2, cp, strlen(cp)); /* exec failure if we make it this far */
120 _exit(1);
121 }
122 else
123 { /* parent */
124 int sec = 0;
125
126#ifdef PR_SET_PTRACER
127 prctl(PR_SET_PTRACER, pid, 0, 0, 0);
128#endif
129 close(fds[0]);
130 iwrite(fds[1], "quit\n", 5);
131 while (1)
132 {
133 int status;
134 pid_t s = waitpid(pid, &status, WNOHANG);
135 if (s != 0)
136 break;
137 if (sec == 9)
138 kill(pid, SIGTERM);
139 if (sec == 10)
140 kill(pid, SIGKILL);
141 if (sec == 11)
142 break;
143 if (sec > 3)
144 iwrite(fds[1], "quit\n", 5);
145 sleep(1);
146 sec++;
147 }
148 close(fds[1]);
149 }
150}
151
152static int yaz_panic_signal = 0;
153static void yaz_panic_alarm(int sig)
154{
155 const char *cp = "backtrace: backtrace hangs\n";
156
157 iwrite(yaz_panic_fd, cp, strlen(cp));
158 _exit(1);
159}
160
161static void yaz_invoke_backtrace(int sig)
162{
163 int fd = yaz_panic_fd;
164 void *backtrace_info[BACKTRACE_SZ];
165 int sz = BACKTRACE_SZ;
166
167 yaz_panic_signal = sig;
168 signal(SIGALRM, yaz_panic_alarm);
169 alarm(1);
170 sz = backtrace(backtrace_info, sz);
171 backtrace_symbols_fd(backtrace_info, sz, fd);
172 alarm(0);
173}
174
175static void yaz_panic_sig_handler(int sig)
176{
177 char buf[512];
178 FILE *file;
179
180 signal(SIGABRT, SIG_DFL);
181 signal(SIGSEGV, SIG_DFL);
182 signal(SIGFPE, SIG_DFL);
183 signal(SIGBUS, SIG_DFL);
184 strcpy(buf, "\nYAZ panic received ");
185 switch (sig)
186 {
187 case SIGSEGV:
188 strcat(buf, "SIGSEGV");
189 break;
190 case SIGABRT:
191 strcat(buf, "SIGABRT");
192 break;
193 case SIGFPE:
194 strcat(buf, "SIGFPE");
195 break;
196 case SIGBUS:
197 strcat(buf, "SIGBUS");
198 break;
199 default:
200 yaz_snprintf(buf + strlen(buf), sizeof buf, "signo=%d", sig);
201 break;
202 }
203 yaz_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf) - 1,
204 " PID=" NMEM_INT_PRINTF "\n", (nmem_int_t) getpid());
205
206 file = yaz_log_file();
207 /* static variable to be used in the following + handlers */
208 yaz_panic_fd = fileno(file);
209
210 iwrite(yaz_panic_fd, buf, strlen(buf));
211 yaz_invoke_backtrace(sig);
212 yaz_invoke_gdb();
213 kill(getpid(), sig);
214}
215#endif
216
218{
219 strncpy(static_progname, progname, sizeof(static_progname) - 1);
220 static_progname[sizeof(static_progname) - 1] = '\0';
221#if HAVE_EXECINFO_H
222 signal(SIGABRT, yaz_panic_sig_handler);
223 signal(SIGSEGV, yaz_panic_sig_handler);
224 signal(SIGFPE, yaz_panic_sig_handler);
225 signal(SIGBUS, yaz_panic_sig_handler);
226#endif
227}
228
229/*
230 * Local variables:
231 * c-basic-offset: 4
232 * c-file-style: "Stroustrup"
233 * indent-tabs-mode: nil
234 * End:
235 * vim: shiftwidth=4 tabstop=8 expandtab
236 */
237
static char static_progname[256]
Definition backtrace.c:47
void yaz_enable_panic_backtrace(const char *progname)
enables backtrace when SIGSEGV/SIGABRT/.. signal is received
Definition backtrace.c:217
#define BACKTRACE_SZ
Definition backtrace.c:45
backtrace handling
Header for errno utilities.
FILE * yaz_log_file(void)
returns FILE handle for log or NULL if no file is in use
Definition log.c:138
Logging utility.
Header for Nibble Memory functions.
int nmem_int_t
BER/utility integer (32-bit on most platforms)
Definition nmem.h:65
#define NMEM_INT_PRINTF
printf format for nmem_int_t type
Definition nmem.h:67
static int arg_no
Definition options.c:17
void yaz_snprintf(char *buf, size_t size, const char *fmt,...)
Definition snprintf.c:31
Header for config file reading utilities.
const char * progname
Definition stemwords.c:12
Header with fundamental macros.