00001
00002
00003
00004
00005
00006
00007
00008 #include "wvcrash.h"
00009
00010 #include <errno.h>
00011 #include <fcntl.h>
00012 #include <signal.h>
00013 #include <stdio.h>
00014 #include <string.h>
00015 #include <sys/syscall.h>
00016 #include <sys/types.h>
00017 #include <sys/wait.h>
00018 #include <time.h>
00019
00020 #ifndef WVCRASH_USE_SIGALTSTACK
00021 #define WVCRASH_USE_SIGALTSTACK 0
00022 #endif
00023
00024
00025 #ifdef __linux
00026
00027 # include <execinfo.h>
00028 #include <unistd.h>
00029
00030 #ifdef __USE_GNU
00031 static const char *argv0 = program_invocation_short_name;
00032 #else
00033 static const char *argv0 = "UNKNOWN";
00034 #endif // __USE_GNU
00035
00036
00037 static const int buffer_size = 2048;
00038 static char desc[buffer_size];
00039 WvCrashCallback callback;
00040
00041
00042 static void wr(int fd, const char *str)
00043 {
00044 write(fd, str, strlen(str));
00045 }
00046
00047
00048
00049 static void wrn(int fd, int num)
00050 {
00051 int tmp;
00052 char c;
00053
00054 if (num < 0)
00055 {
00056 wr(fd, "-");
00057 num = -num;
00058 }
00059 else if (num == 0)
00060 {
00061 wr(fd, "0");
00062 return;
00063 }
00064
00065 tmp = 0;
00066 while (num > 0)
00067 {
00068 tmp *= 10;
00069 tmp += num%10;
00070 num /= 10;
00071 }
00072
00073 while (tmp > 0)
00074 {
00075 c = '0' + (tmp%10);
00076 write(fd, &c, 1);
00077 tmp /= 10;
00078 }
00079 }
00080
00081
00082 static void wvcrash_real(int sig, int fd, pid_t pid)
00083 {
00084 static void *trace[64];
00085 static char *signame = strsignal(sig);
00086
00087 wr(fd, argv0);
00088 if (desc[0])
00089 {
00090 wr(fd, " (");
00091 wr(fd, desc);
00092 wr(fd, ")");
00093 }
00094 wr(fd, " dying on signal ");
00095 wrn(fd, sig);
00096 if (signame)
00097 {
00098 wr(fd, " (");
00099 wr(fd, signame);
00100 wr(fd, ")\n");
00101 }
00102
00103
00104 static char pid_str[32];
00105 wr(fd, "\nProcess ID: ");
00106 snprintf(pid_str, sizeof(pid_str), "%d", getpid());
00107 pid_str[31] = '\0';
00108 wr(fd, pid_str);
00109 wr(fd, "\nParent's process ID: ");
00110 snprintf(pid_str, sizeof(pid_str), "%d", getppid());
00111 pid_str[31] = '\0';
00112 wr(fd, pid_str);
00113 wr(fd, "\n");
00114
00115
00116 {
00117 const char *assert_msg = wvcrash_read_assert();
00118 if (assert_msg && assert_msg[0])
00119 {
00120 wr(fd, "\nAssert:\n");
00121 wr(fd, assert_msg);
00122 }
00123 }
00124
00125
00126 {
00127 const char *will_msg = wvcrash_read_will();
00128 if (will_msg && will_msg[0])
00129 {
00130 wr(fd, "\nLast Will and Testament:\n");
00131 wr(fd, will_msg);
00132 wr(fd, "\n");
00133 }
00134 }
00135
00136 wr(fd, "\nBacktrace:\n");
00137 backtrace_symbols_fd(trace,
00138 backtrace(trace, sizeof(trace)/sizeof(trace[0])), fd);
00139
00140 if (pid > 0)
00141 {
00142
00143
00144
00145 int i;
00146 struct timespec ts = { 0, 100*1000*1000 };
00147 close(fd);
00148 for (i=0; i < 100; ++i)
00149 {
00150 if (waitpid(pid, NULL, WNOHANG) == pid)
00151 break;
00152 nanosleep(&ts, NULL);
00153 }
00154 }
00155
00156
00157
00158
00159 if (sig == SIGABRT)
00160 sig = SIGBUS;
00161 else if (sig != 0)
00162 sig = SIGABRT;
00163
00164 signal(sig, SIG_DFL);
00165 raise(sig);
00166 }
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176 void wvcrash(int sig)
00177 {
00178 int fds[2];
00179 pid_t pid;
00180
00181 signal(sig, SIG_DFL);
00182 wr(2, "\n\nwvcrash: crashing!\n");
00183
00184 if (!!callback)
00185 callback(sig);
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199 for (int count = 5; count < 15; count++)
00200 close(count);
00201
00202 if (pipe(fds))
00203 wvcrash_real(sig, 2, 0);
00204 else
00205 {
00206 pid = fork();
00207 if (pid < 0)
00208 wvcrash_real(sig, 2, 0);
00209 else if (pid == 0)
00210 {
00211 close(fds[1]);
00212 dup2(fds[0], 0);
00213 fcntl(0, F_SETFD, 0);
00214
00215 execlp("wvcrash", "wvcrash", NULL);
00216
00217
00218 wr(2, "wvcrash: can't exec wvcrash binary "
00219 "- writing to wvcrash.txt!\n");
00220 execlp("dd", "dd", "of=wvcrash.txt", NULL);
00221
00222 wr(2, "wvcrash: can't exec dd to write to wvcrash.txt!\n");
00223 _exit(127);
00224 }
00225 else if (pid > 0)
00226 {
00227 close(fds[0]);
00228 wvcrash_real(sig, fds[1], pid);
00229 }
00230 }
00231
00232
00233 _exit(126);
00234 }
00235
00236
00237 WvCrashCallback wvcrash_set_callback(WvCrashCallback _callback)
00238 {
00239 WvCrashCallback old_callback = callback;
00240 callback = _callback;
00241 return old_callback;
00242 }
00243
00244 static void wvcrash_setup_alt_stack()
00245 {
00246 #if WVCRASH_USE_SIGALTSTACK
00247 const size_t stack_size = 1048576;
00248 static char stack[stack_size];
00249 stack_t ss;
00250
00251 ss.ss_sp = stack;
00252 ss.ss_flags = 0;
00253 ss.ss_size = stack_size;
00254
00255 if (ss.ss_sp == NULL || sigaltstack(&ss, NULL))
00256 fprintf(stderr, "Failed to setup sigaltstack for wvcrash: %s\n",
00257 strerror(errno));
00258 #endif //WVCRASH_USE_SIGALTSTACK
00259 }
00260
00261 void wvcrash_add_signal(int sig)
00262 {
00263 #if WVCRASH_USE_SIGALTSTACK
00264 struct sigaction act;
00265
00266 act.sa_handler = wvcrash;
00267 sigfillset(&act.sa_mask);
00268 act.sa_flags = SA_ONSTACK | SA_RESTART;
00269 act.sa_restorer = NULL;
00270
00271 if (sigaction(sig, &act, NULL))
00272 fprintf(stderr, "Failed to setup wvcrash handler for signal %d: %s\n",
00273 sig, strerror(errno));
00274 #else
00275 signal(sig, wvcrash);
00276 #endif //WVCRASH_USE_SIGALTSTACK
00277 }
00278
00279
00280 extern void __wvcrash_init_buffers(const char *program_name);
00281
00282 void wvcrash_setup(const char *_argv0, const char *_desc)
00283 {
00284 if (_argv0)
00285 argv0 = basename(_argv0);
00286 __wvcrash_init_buffers(argv0);
00287 if (_desc)
00288 {
00289 strncpy(desc, _desc, buffer_size);
00290 desc[buffer_size - 1] = '\0';
00291 }
00292 else
00293 desc[0] = '\0';
00294
00295 wvcrash_setup_alt_stack();
00296
00297 wvcrash_add_signal(SIGSEGV);
00298 wvcrash_add_signal(SIGBUS);
00299 wvcrash_add_signal(SIGABRT);
00300 wvcrash_add_signal(SIGFPE);
00301 wvcrash_add_signal(SIGILL);
00302 }
00303
00304 #else // Not Linux
00305
00306 void wvcrash(int sig) {}
00307 void wvcrash_add_signal(int sig) {}
00308 WvCrashCallback wvcrash_set_callback(WvCrashCallback cb) {}
00309 void wvcrash_setup(const char *_argv0, const char *_desc) {}
00310
00311 #endif // Not Linux