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 <time.h>
00017 #include <sys/types.h>
00018
00019 #ifndef _WIN32
00020 # include <sys/wait.h>
00021 # include <sys/syscall.h>
00022 #endif
00023
00024 #ifndef WVCRASH_USE_SIGALTSTACK
00025 # define WVCRASH_USE_SIGALTSTACK 1
00026 #endif
00027
00028
00029 #ifdef __linux
00030
00031 # include <execinfo.h>
00032 #include <unistd.h>
00033
00034 #ifdef __USE_GNU
00035 static const char *argv0 = program_invocation_short_name;
00036 #else
00037 static const char *argv0 = "UNKNOWN";
00038 #endif // __USE_GNU
00039
00040 #if WVCRASH_USE_SIGALTSTACK
00041 static const size_t altstack_size = 1048576;
00042 static char altstack[altstack_size];
00043 #endif
00044
00045
00046 static const int buffer_size = 2048 + wvcrash_ring_buffer_size;
00047
00048 static char desc[buffer_size];
00049
00050
00051 static void wr(int fd, const char *str)
00052 {
00053 write(fd, str, strlen(str));
00054 }
00055
00056
00057
00058 static void wrn(int fd, int num)
00059 {
00060 int tmp;
00061 char c;
00062
00063 if (num < 0)
00064 {
00065 wr(fd, "-");
00066 num = -num;
00067 }
00068 else if (num == 0)
00069 {
00070 wr(fd, "0");
00071 return;
00072 }
00073
00074 tmp = 0;
00075 while (num > 0)
00076 {
00077 tmp *= 10;
00078 tmp += num%10;
00079 num /= 10;
00080 }
00081
00082 while (tmp > 0)
00083 {
00084 c = '0' + (tmp%10);
00085 write(fd, &c, 1);
00086 tmp /= 10;
00087 }
00088 }
00089
00090
00091
00092 static void wra(int fd, const void *addr)
00093 {
00094 const unsigned int ptrbitsshift = (sizeof(ptrdiff_t) << 3) - 4;
00095 char digits[] = "0123456789ABCDEF";
00096
00097 write(fd, "0x", 2);
00098 for (int shift=ptrbitsshift; shift>=0; shift-=4)
00099 write(fd, &digits[(((ptrdiff_t)addr)>>shift)&0xF], 1);
00100 }
00101
00102
00103 static void wvcrash_real(int sig, int fd, pid_t pid)
00104 {
00105 static void *trace[64];
00106 static char *signame = strsignal(sig);
00107
00108 wr(fd, argv0);
00109 if (desc[0])
00110 {
00111 wr(fd, " (");
00112 wr(fd, desc);
00113 wr(fd, ")");
00114 }
00115 wr(fd, " dying on signal ");
00116 wrn(fd, sig);
00117 if (signame)
00118 {
00119 wr(fd, " (");
00120 wr(fd, signame);
00121 wr(fd, ")\n");
00122 }
00123
00124
00125 static char pid_str[32];
00126 wr(fd, "\nProcess ID: ");
00127 snprintf(pid_str, sizeof(pid_str), "%d", getpid());
00128 pid_str[31] = '\0';
00129 wr(fd, pid_str);
00130 wr(fd, "\nParent's process ID: ");
00131 snprintf(pid_str, sizeof(pid_str), "%d", getppid());
00132 pid_str[31] = '\0';
00133 wr(fd, pid_str);
00134 wr(fd, "\n");
00135
00136 #if WVCRASH_USE_SIGALTSTACK
00137
00138 const void *last_real_stack_frame;
00139 for (;;)
00140 {
00141 last_real_stack_frame = __builtin_frame_address(0);
00142 if (last_real_stack_frame == NULL
00143 || last_real_stack_frame < &altstack[0]
00144 || last_real_stack_frame >= &altstack[altstack_size])
00145 break;
00146 last_real_stack_frame = __builtin_frame_address(1);
00147 if (last_real_stack_frame == NULL
00148 || last_real_stack_frame < &altstack[0]
00149 || last_real_stack_frame >= &altstack[altstack_size])
00150 break;
00151 last_real_stack_frame = __builtin_frame_address(2);
00152 if (last_real_stack_frame == NULL
00153 || last_real_stack_frame < &altstack[0]
00154 || last_real_stack_frame >= &altstack[altstack_size])
00155 break;
00156 last_real_stack_frame = __builtin_frame_address(3);
00157 if (last_real_stack_frame == NULL
00158 || last_real_stack_frame < &altstack[0]
00159 || last_real_stack_frame >= &altstack[altstack_size])
00160 break;
00161 last_real_stack_frame = __builtin_frame_address(4);
00162 if (last_real_stack_frame == NULL
00163 || last_real_stack_frame < &altstack[0]
00164 || last_real_stack_frame >= &altstack[altstack_size])
00165 break;
00166 last_real_stack_frame = __builtin_frame_address(5);
00167 if (last_real_stack_frame == NULL
00168 || last_real_stack_frame < &altstack[0]
00169 || last_real_stack_frame >= &altstack[altstack_size])
00170 break;
00171 last_real_stack_frame = NULL;
00172 break;
00173 }
00174 if (last_real_stack_frame != NULL)
00175 {
00176 wr(fd, "\nLast real stack frame: ");
00177 wra(fd, last_real_stack_frame);
00178 const void *top_of_stack = WvTaskMan::current_top_of_stack();
00179 wr(fd, "\nTop of stack: ");
00180 wra(fd, top_of_stack);
00181 size_t stack_size = size_t(top_of_stack) - size_t(last_real_stack_frame);
00182 wr(fd, "\nStack size: ");
00183 wrn(fd, int(stack_size));
00184 size_t stack_size_limit = WvTaskMan::current_stacksize_limit();
00185 if (stack_size_limit > 0)
00186 {
00187 wr(fd, "\nStack size rlimit: ");
00188 wrn(fd, int(stack_size_limit));
00189 if (stack_size > stack_size_limit)
00190 wr(fd, " DEFINITE STACK OVERFLOW");
00191 else if (stack_size + 16384 > stack_size_limit)
00192 wr(fd, " PROBABLE STACK OVERFLOW");
00193 }
00194 wr(fd, "\n");
00195 }
00196 #endif
00197
00198
00199
00200 {
00201 const char *ring;
00202 bool first = true;
00203 while ((ring = wvcrash_ring_buffer_get()) != NULL)
00204 {
00205 if (first)
00206 {
00207 first = false;
00208 wr(fd, "\nRing buffer:\n");
00209 }
00210 wr(fd, ring);
00211 }
00212 }
00213
00214
00215 {
00216 const char *assert_msg = wvcrash_read_assert();
00217 if (assert_msg && assert_msg[0])
00218 {
00219 wr(fd, "\nAssert:\n");
00220 wr(fd, assert_msg);
00221 }
00222 }
00223
00224
00225 {
00226 const char *will_msg = wvcrash_read_will();
00227 if (will_msg && will_msg[0])
00228 {
00229 wr(fd, "\nLast Will and Testament:\n");
00230 wr(fd, will_msg);
00231 wr(fd, "\n");
00232 }
00233 }
00234
00235 if (WvCrashInfo::in_stream_state != WvCrashInfo::UNUSED
00236 && WvCrashInfo::in_stream)
00237 {
00238 const char *state = NULL;
00239 switch (WvCrashInfo::in_stream_state)
00240 {
00241 case WvCrashInfo::UNUSED:
00242
00243 break;
00244 case WvCrashInfo::PRE_SELECT:
00245 state = "\nStream in pre_select: ";
00246 break;
00247 case WvCrashInfo::POST_SELECT:
00248 state = "\nStream in post_select: ";
00249 break;
00250 case WvCrashInfo::EXECUTE:
00251 state = "\nStream in execute: ";
00252 break;
00253 }
00254
00255 if (state)
00256 {
00257 static char ptr_str[32];
00258 snprintf(ptr_str, sizeof(ptr_str), "%p", WvCrashInfo::in_stream);
00259 ptr_str[sizeof(ptr_str) - 1] = '\0';
00260
00261 wr(fd, state);
00262 wr(fd, WvCrashInfo::in_stream_id && WvCrashInfo::in_stream_id[0]
00263 ? WvCrashInfo::in_stream_id : "unknown stream");
00264 wr(fd, " (");
00265 wr(fd, ptr_str);
00266 wr(fd, ")\n");
00267 }
00268 }
00269
00270 wr(fd, "\nBacktrace:\n");
00271 backtrace_symbols_fd(trace,
00272 backtrace(trace, sizeof(trace)/sizeof(trace[0])), fd);
00273
00274 if (pid > 0)
00275 {
00276
00277
00278
00279 int i;
00280 struct timespec ts = { 0, 100*1000*1000 };
00281 close(fd);
00282 for (i=0; i < 100; ++i)
00283 {
00284 if (waitpid(pid, NULL, WNOHANG) == pid)
00285 break;
00286 nanosleep(&ts, NULL);
00287 }
00288 }
00289
00290
00291
00292
00293 if (sig == SIGABRT)
00294 sig = SIGBUS;
00295 else if (sig != 0)
00296 sig = SIGABRT;
00297
00298 signal(sig, SIG_DFL);
00299 raise(sig);
00300 }
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310 void wvcrash(int sig)
00311 {
00312 int fds[2];
00313 pid_t pid;
00314
00315 signal(sig, SIG_DFL);
00316 wr(2, "\n\nwvcrash: crashing!\n");
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330 for (int count = 5; count < 15; count++)
00331 close(count);
00332
00333 if (pipe(fds))
00334 wvcrash_real(sig, 2, 0);
00335 else
00336 {
00337 pid = fork();
00338 if (pid < 0)
00339 wvcrash_real(sig, 2, 0);
00340 else if (pid == 0)
00341 {
00342 close(fds[1]);
00343 dup2(fds[0], 0);
00344 fcntl(0, F_SETFD, 0);
00345
00346 execlp("wvcrash", "wvcrash", NULL);
00347
00348
00349 wr(2, "wvcrash: can't exec wvcrash binary "
00350 "- writing to wvcrash.txt!\n");
00351 execlp("dd", "dd", "of=wvcrash.txt", NULL);
00352
00353 wr(2, "wvcrash: can't exec dd to write to wvcrash.txt!\n");
00354 _exit(127);
00355 }
00356 else if (pid > 0)
00357 {
00358 close(fds[0]);
00359 wvcrash_real(sig, fds[1], pid);
00360 }
00361 }
00362
00363
00364 _exit(126);
00365 }
00366
00367
00368 static void wvcrash_setup_alt_stack()
00369 {
00370 #if WVCRASH_USE_SIGALTSTACK
00371 stack_t ss;
00372
00373 ss.ss_sp = altstack;
00374 ss.ss_flags = 0;
00375 ss.ss_size = altstack_size;
00376
00377 if (ss.ss_sp == NULL || sigaltstack(&ss, NULL))
00378 fprintf(stderr, "Failed to setup sigaltstack for wvcrash: %s\n",
00379 strerror(errno));
00380 #endif //WVCRASH_USE_SIGALTSTACK
00381 }
00382
00383 void wvcrash_add_signal(int sig)
00384 {
00385 #if WVCRASH_USE_SIGALTSTACK
00386 struct sigaction act;
00387
00388 memset(&act,0,sizeof(act));
00389 act.sa_handler = wvcrash;
00390 sigfillset(&act.sa_mask);
00391 act.sa_flags = SA_ONSTACK | SA_RESTART;
00392
00393 if (sigaction(sig, &act, NULL))
00394 fprintf(stderr, "Failed to setup wvcrash handler for signal %d: %s\n",
00395 sig, strerror(errno));
00396 #else
00397 signal(sig, wvcrash);
00398 #endif //WVCRASH_USE_SIGALTSTACK
00399 }
00400
00401
00402 extern void __wvcrash_init_buffers(const char *program_name);
00403
00404 void wvcrash_setup(const char *_argv0, const char *_desc)
00405 {
00406 if (_argv0)
00407 argv0 = basename(_argv0);
00408 __wvcrash_init_buffers(argv0);
00409 if (_desc)
00410 {
00411 strncpy(desc, _desc, buffer_size);
00412 desc[buffer_size - 1] = '\0';
00413 }
00414 else
00415 desc[0] = '\0';
00416
00417 wvcrash_setup_alt_stack();
00418
00419 wvcrash_add_signal(SIGSEGV);
00420 wvcrash_add_signal(SIGBUS);
00421 wvcrash_add_signal(SIGABRT);
00422 wvcrash_add_signal(SIGFPE);
00423 wvcrash_add_signal(SIGILL);
00424 }
00425
00426 #elif defined(_WIN32)
00427
00428 #include <windows.h>
00429 #include <stdio.h>
00430 #include <imagehlp.h>
00431
00432 inline char* last_part(char* in)
00433 {
00434 int len = strlen(in);
00435 char* tmp = in+len;
00436 while (tmp > in)
00437 {
00438 if (*tmp == '/' || *tmp == '\\')
00439 return tmp+1;
00440 tmp--;
00441 }
00442 return in;
00443 }
00444
00445
00454 int backtrace(CONTEXT &ctx)
00455 {
00456 HANDLE hProcess = (HANDLE)GetCurrentProcess();
00457 HANDLE hThread = (HANDLE)GetCurrentThread();
00458
00459 SymInitialize(hProcess, NULL, TRUE);
00460
00461 STACKFRAME sf;
00462 memset(&sf, 0, sizeof(STACKFRAME));
00463
00464 sf.AddrPC.Offset = ctx.Eip;
00465 sf.AddrPC.Mode = AddrModeFlat;
00466 sf.AddrFrame.Offset = ctx.Ebp;
00467 sf.AddrFrame.Mode = AddrModeFlat;
00468 sf.AddrStack.Offset = ctx.Esp;
00469 sf.AddrStack.Mode = AddrModeFlat;
00470
00471 fprintf(stderr, "Generating stack trace......\n");
00472 fprintf(stderr, "%3s %16s:%-10s %32s:%3s %s\n", "Num", "Module", "Addr", "Filename", "Line", "Function Name");
00473 int i = 0;
00474 while (StackWalk(IMAGE_FILE_MACHINE_I386,
00475 hProcess,
00476 hThread,
00477 &sf,
00478 &ctx,
00479 NULL,
00480 SymFunctionTableAccess,
00481 SymGetModuleBase,
00482 NULL))
00483 {
00484 if (sf.AddrPC.Offset == 0)
00485 break;
00486
00487
00488 IMAGEHLP_MODULE modinfo;
00489 memset(&modinfo, 0, sizeof(IMAGEHLP_MODULE));
00490 modinfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
00491 SymGetModuleInfo(hProcess, sf.AddrPC.Offset, &modinfo);
00492
00493
00494 BYTE buffer[1024];
00495 DWORD disp = 0;
00496 memset(buffer, 0, sizeof(buffer));
00497 PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL)buffer;
00498 sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
00499 sym->MaxNameLength = sizeof(buffer) - sizeof(IMAGEHLP_SYMBOL) + 1;
00500 SymGetSymFromAddr(hProcess, sf.AddrPC.Offset, &disp, sym);
00501
00502
00503 IMAGEHLP_LINE line;
00504 SymSetOptions(SYMOPT_LOAD_LINES);
00505 DWORD disp2 = 0;
00506 memset(&line, 0, sizeof(IMAGEHLP_LINE));
00507 line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
00508 SymGetLineFromAddr(hProcess, sf.AddrPC.Offset, &disp2, &line);
00509
00510
00511 fprintf(stderr, "%3d. %16s:0x%08X %32s:%-3d %s\n",
00512 ++i,
00513 modinfo.LoadedImageName[0]?modinfo.LoadedImageName:"unknown",
00514 (DWORD)sf.AddrPC.Offset,
00515 (line.FileName && line.FileName[0])?last_part(line.FileName):"unknown",
00516 (line.FileName && line.FileName[0])?line.LineNumber:0,
00517 sym->Name[0]?sym->Name:"unknown");
00518 }
00519
00520 SymCleanup(hProcess);
00521
00522 return 1;
00523 }
00524
00525
00526 static void exception_desc(FILE *file, unsigned exception,
00527 unsigned data1, unsigned data2)
00528 {
00529
00530 switch (exception)
00531 {
00532 case 0xC0000005:
00533 {
00534 switch (data1)
00535 {
00536 case 0:
00537 fprintf(file,
00538 "invalid memory read from address 0x%08X",
00539 data2);
00540 break;
00541 case 1:
00542 fprintf(file,
00543 "invalid memory write to address 0x%08X",
00544 data2);
00545 break;
00546 default:
00547 fprintf(file,
00548 "invalid memory access (unknown type %d) at address 0x%08X",
00549 data1, data2);
00550 break;
00551 }
00552 }
00553 break;
00554
00555 case 0xC0000094:
00556 fprintf(file, "integer division by zero");
00557 break;
00558
00559 default:
00560 fprintf(file, "unknown exception (data1=0x%08X, data2=0x%08X)");
00561 break;
00562 }
00563 }
00564
00565 static LONG WINAPI ExceptionFilter( struct _EXCEPTION_POINTERS * pExceptionPointers )
00566 {
00567 struct ExceptionInfo
00568 {
00569 unsigned exception;
00570 unsigned unknown[2];
00571 void *ip;
00572 unsigned more_unknown;
00573 unsigned data1;
00574 unsigned data2;
00575 };
00576 ExceptionInfo *info = *(ExceptionInfo **)pExceptionPointers;
00577
00578
00579
00580
00581 if (info->exception==0x80000003)
00582 {
00583 fprintf(stderr, "Preparing to debug!\n");
00584 return EXCEPTION_CONTINUE_SEARCH;
00585 }
00586
00587 fprintf(stderr, "--------------------------------------------------------\n");
00588 fprintf(stderr, "Exception 0x%08X:\n ", info->exception);
00589 exception_desc(stderr, info->exception, info->data1, info->data2);
00590 fprintf(stderr, "\n at instruction 0x%08X in thread 0x%08X\n", info->ip, GetCurrentThreadId());
00591 backtrace(*pExceptionPointers->ContextRecord);
00592 fprintf(stderr, "--------------------------------------------------------\n");
00593
00594
00595 return EXCEPTION_EXECUTE_HANDLER;
00596 }
00597
00598 static bool have_global_exception_handler = false;
00599 void setup_console_crash()
00600 {
00601 if (!have_global_exception_handler)
00602 {
00603 SetUnhandledExceptionFilter(ExceptionFilter);
00604 have_global_exception_handler = true;
00605 }
00606 }
00607
00608 void wvcrash(int sig) {}
00609 void wvcrash_setup(const char *_argv0, const char *_desc) {}
00610
00611 #else // Not Linux
00612
00613 void wvcrash(int sig) {}
00614 void wvcrash_add_signal(int sig) {}
00615 void wvcrash_setup(const char *_argv0, const char *_desc) {}
00616
00617 #endif // Not Linux