00001
00002
00003
00004
00005
00006
00007
00008 #include "wvcrash.h"
00009 #include "wvtask.h"
00010
00011 #include <errno.h>
00012 #include <fcntl.h>
00013 #include <signal.h>
00014 #include <stdio.h>
00015 #include <string.h>
00016 #include <sys/syscall.h>
00017 #include <sys/types.h>
00018 #include <sys/wait.h>
00019 #include <time.h>
00020
00021 #ifndef WVCRASH_USE_SIGALTSTACK
00022 #define WVCRASH_USE_SIGALTSTACK 1
00023 #endif
00024
00025
00026 #ifdef __linux
00027
00028 # include <execinfo.h>
00029 #include <unistd.h>
00030
00031 #ifdef __USE_GNU
00032 static const char *argv0 = program_invocation_short_name;
00033 #else
00034 static const char *argv0 = "UNKNOWN";
00035 #endif // __USE_GNU
00036
00037 #if WVCRASH_USE_SIGALTSTACK
00038 static const size_t altstack_size = 1048576;
00039 static char altstack[altstack_size];
00040 #endif
00041
00042
00043 static const int buffer_size = 2048 + wvcrash_ring_buffer_size;
00044
00045 static char desc[buffer_size];
00046
00047
00048 static void wr(int fd, const char *str)
00049 {
00050 write(fd, str, strlen(str));
00051 }
00052
00053
00054
00055 static void wrn(int fd, int num)
00056 {
00057 int tmp;
00058 char c;
00059
00060 if (num < 0)
00061 {
00062 wr(fd, "-");
00063 num = -num;
00064 }
00065 else if (num == 0)
00066 {
00067 wr(fd, "0");
00068 return;
00069 }
00070
00071 tmp = 0;
00072 while (num > 0)
00073 {
00074 tmp *= 10;
00075 tmp += num%10;
00076 num /= 10;
00077 }
00078
00079 while (tmp > 0)
00080 {
00081 c = '0' + (tmp%10);
00082 write(fd, &c, 1);
00083 tmp /= 10;
00084 }
00085 }
00086
00087
00088
00089 static void wra(int fd, const void *addr)
00090 {
00091 const unsigned int ptrbitsshift = (sizeof(ptrdiff_t) << 3) - 4;
00092 char digits[] = "0123456789ABCDEF";
00093
00094 write(fd, "0x", 2);
00095 for (int shift=ptrbitsshift; shift>=0; shift-=4)
00096 write(fd, &digits[(((ptrdiff_t)addr)>>shift)&0xF], 1);
00097 }
00098
00099 static void wvcrash_real(int sig, int fd, pid_t pid)
00100 {
00101 static void *trace[64];
00102 static char *signame = strsignal(sig);
00103
00104 wr(fd, argv0);
00105 if (desc[0])
00106 {
00107 wr(fd, " (");
00108 wr(fd, desc);
00109 wr(fd, ")");
00110 }
00111 wr(fd, " dying on signal ");
00112 wrn(fd, sig);
00113 if (signame)
00114 {
00115 wr(fd, " (");
00116 wr(fd, signame);
00117 wr(fd, ")\n");
00118 }
00119
00120
00121 static char pid_str[32];
00122 wr(fd, "\nProcess ID: ");
00123 snprintf(pid_str, sizeof(pid_str), "%d", getpid());
00124 pid_str[31] = '\0';
00125 wr(fd, pid_str);
00126 wr(fd, "\nParent's process ID: ");
00127 snprintf(pid_str, sizeof(pid_str), "%d", getppid());
00128 pid_str[31] = '\0';
00129 wr(fd, pid_str);
00130 wr(fd, "\n");
00131
00132 #if WVCRASH_USE_SIGALTSTACK
00133
00134 const void *last_real_stack_frame;
00135 for (;;)
00136 {
00137 last_real_stack_frame = __builtin_frame_address(0);
00138 if (last_real_stack_frame == NULL
00139 || last_real_stack_frame < &altstack[0]
00140 || last_real_stack_frame >= &altstack[altstack_size])
00141 break;
00142 last_real_stack_frame = __builtin_frame_address(1);
00143 if (last_real_stack_frame == NULL
00144 || last_real_stack_frame < &altstack[0]
00145 || last_real_stack_frame >= &altstack[altstack_size])
00146 break;
00147 last_real_stack_frame = __builtin_frame_address(2);
00148 if (last_real_stack_frame == NULL
00149 || last_real_stack_frame < &altstack[0]
00150 || last_real_stack_frame >= &altstack[altstack_size])
00151 break;
00152 last_real_stack_frame = __builtin_frame_address(3);
00153 if (last_real_stack_frame == NULL
00154 || last_real_stack_frame < &altstack[0]
00155 || last_real_stack_frame >= &altstack[altstack_size])
00156 break;
00157 last_real_stack_frame = __builtin_frame_address(4);
00158 if (last_real_stack_frame == NULL
00159 || last_real_stack_frame < &altstack[0]
00160 || last_real_stack_frame >= &altstack[altstack_size])
00161 break;
00162 last_real_stack_frame = __builtin_frame_address(5);
00163 if (last_real_stack_frame == NULL
00164 || last_real_stack_frame < &altstack[0]
00165 || last_real_stack_frame >= &altstack[altstack_size])
00166 break;
00167 last_real_stack_frame = NULL;
00168 break;
00169 }
00170 if (last_real_stack_frame != NULL)
00171 {
00172 wr(fd, "\nLast real stack frame: ");
00173 wra(fd, last_real_stack_frame);
00174 const void *top_of_stack = WvTaskMan::current_top_of_stack();
00175 wr(fd, "\nTop of stack: ");
00176 wra(fd, top_of_stack);
00177 size_t stack_size = size_t(top_of_stack) - size_t(last_real_stack_frame);
00178 wr(fd, "\nStack size: ");
00179 wrn(fd, int(stack_size));
00180 size_t stack_size_limit = WvTaskMan::current_stacksize_limit();
00181 if (stack_size_limit > 0)
00182 {
00183 wr(fd, "\nStack size rlimit: ");
00184 wrn(fd, int(stack_size_limit));
00185 if (stack_size > stack_size_limit)
00186 wr(fd, " DEFINITE STACK OVERFLOW");
00187 else if (stack_size + 16384 > stack_size_limit)
00188 wr(fd, " PROBABLE STACK OVERFLOW");
00189 }
00190 wr(fd, "\n");
00191 }
00192 #endif
00193
00194
00195
00196 {
00197 const char *ring;
00198 bool first = true;
00199 while ((ring = wvcrash_ring_buffer_get()) != NULL)
00200 {
00201 if (first)
00202 {
00203 first = false;
00204 wr(fd, "\nRing buffer:\n");
00205 }
00206 wr(fd, ring);
00207 }
00208 }
00209
00210
00211 {
00212 const char *assert_msg = wvcrash_read_assert();
00213 if (assert_msg && assert_msg[0])
00214 {
00215 wr(fd, "\nAssert:\n");
00216 wr(fd, assert_msg);
00217 }
00218 }
00219
00220
00221 {
00222 const char *will_msg = wvcrash_read_will();
00223 if (will_msg && will_msg[0])
00224 {
00225 wr(fd, "\nLast Will and Testament:\n");
00226 wr(fd, will_msg);
00227 wr(fd, "\n");
00228 }
00229 }
00230
00231 wr(fd, "\nBacktrace:\n");
00232 backtrace_symbols_fd(trace,
00233 backtrace(trace, sizeof(trace)/sizeof(trace[0])), fd);
00234
00235 if (pid > 0)
00236 {
00237
00238
00239
00240 int i;
00241 struct timespec ts = { 0, 100*1000*1000 };
00242 close(fd);
00243 for (i=0; i < 100; ++i)
00244 {
00245 if (waitpid(pid, NULL, WNOHANG) == pid)
00246 break;
00247 nanosleep(&ts, NULL);
00248 }
00249 }
00250
00251
00252
00253
00254 if (sig == SIGABRT)
00255 sig = SIGBUS;
00256 else if (sig != 0)
00257 sig = SIGABRT;
00258
00259 signal(sig, SIG_DFL);
00260 raise(sig);
00261 }
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271 void wvcrash(int sig)
00272 {
00273 int fds[2];
00274 pid_t pid;
00275
00276 signal(sig, SIG_DFL);
00277 wr(2, "\n\nwvcrash: crashing!\n");
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291 for (int count = 5; count < 15; count++)
00292 close(count);
00293
00294 if (pipe(fds))
00295 wvcrash_real(sig, 2, 0);
00296 else
00297 {
00298 pid = fork();
00299 if (pid < 0)
00300 wvcrash_real(sig, 2, 0);
00301 else if (pid == 0)
00302 {
00303 close(fds[1]);
00304 dup2(fds[0], 0);
00305 fcntl(0, F_SETFD, 0);
00306
00307 execlp("wvcrash", "wvcrash", NULL);
00308
00309
00310 wr(2, "wvcrash: can't exec wvcrash binary "
00311 "- writing to wvcrash.txt!\n");
00312 execlp("dd", "dd", "of=wvcrash.txt", NULL);
00313
00314 wr(2, "wvcrash: can't exec dd to write to wvcrash.txt!\n");
00315 _exit(127);
00316 }
00317 else if (pid > 0)
00318 {
00319 close(fds[0]);
00320 wvcrash_real(sig, fds[1], pid);
00321 }
00322 }
00323
00324
00325 _exit(126);
00326 }
00327
00328
00329 static void wvcrash_setup_alt_stack()
00330 {
00331 #if WVCRASH_USE_SIGALTSTACK
00332 stack_t ss;
00333
00334 ss.ss_sp = altstack;
00335 ss.ss_flags = 0;
00336 ss.ss_size = altstack_size;
00337
00338 if (ss.ss_sp == NULL || sigaltstack(&ss, NULL))
00339 fprintf(stderr, "Failed to setup sigaltstack for wvcrash: %s\n",
00340 strerror(errno));
00341 #endif //WVCRASH_USE_SIGALTSTACK
00342 }
00343
00344 void wvcrash_add_signal(int sig)
00345 {
00346 #if WVCRASH_USE_SIGALTSTACK
00347 struct sigaction act;
00348
00349 memset(&act,0,sizeof(act));
00350 act.sa_handler = wvcrash;
00351 sigfillset(&act.sa_mask);
00352 act.sa_flags = SA_ONSTACK | SA_RESTART;
00353
00354 if (sigaction(sig, &act, NULL))
00355 fprintf(stderr, "Failed to setup wvcrash handler for signal %d: %s\n",
00356 sig, strerror(errno));
00357 #else
00358 signal(sig, wvcrash);
00359 #endif //WVCRASH_USE_SIGALTSTACK
00360 }
00361
00362
00363 extern void __wvcrash_init_buffers(const char *program_name);
00364
00365 void wvcrash_setup(const char *_argv0, const char *_desc)
00366 {
00367 if (_argv0)
00368 argv0 = basename(_argv0);
00369 __wvcrash_init_buffers(argv0);
00370 if (_desc)
00371 {
00372 strncpy(desc, _desc, buffer_size);
00373 desc[buffer_size - 1] = '\0';
00374 }
00375 else
00376 desc[0] = '\0';
00377
00378 wvcrash_setup_alt_stack();
00379
00380 wvcrash_add_signal(SIGSEGV);
00381 wvcrash_add_signal(SIGBUS);
00382 wvcrash_add_signal(SIGABRT);
00383 wvcrash_add_signal(SIGFPE);
00384 wvcrash_add_signal(SIGILL);
00385 }
00386
00387 #else // Not Linux
00388
00389 void wvcrash(int sig) {}
00390 void wvcrash_add_signal(int sig) {}
00391 void wvcrash_setup(const char *_argv0, const char *_desc) {}
00392
00393 #endif // Not Linux