wvcrash.cc

00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * Routines to generate a stack backtrace automatically when a program
00006  * crashes.
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 // FIXME: this file mostly only works in Linux
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; // wvstreams can be a pig
00039 static char altstack[altstack_size];
00040 #endif
00041 
00042 // Reserve enough buffer for a screenful of programme.
00043 static const int buffer_size = 2048 + wvcrash_ring_buffer_size;
00044 
00045 static char desc[buffer_size];
00046 
00047 // write a string 'str' to fd
00048 static void wr(int fd, const char *str)
00049 {
00050     write(fd, str, strlen(str));
00051 }
00052 
00053 
00054 // convert 'num' to a string and write it to fd.
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 // convert 'addr' to hex and write it to fd.
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     // Write out the PID and PPID.
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     // Determine if this has likely been a stack overflow
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     // Write out the contents of the ring buffer
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     // Write out the assertion message, as logged by __assert*_fail(), if any.
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     // Write out the note, if any.
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         // Wait up to 10 seconds for child to write wvcrash file in case there
00238         // is limited space availible on the device; wvcrash file is more
00239         // useful than core dump
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     // we want to create a coredump, and the kernel seems to not want to do
00252     // that if we send ourselves the same signal that we're already in.
00253     // Whatever... just send a different one :)
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 // Hint: we can't do anything really difficult here, because the program is
00265 // probably really confused.  So we should try to limit this to straight
00266 // kernel syscalls (ie. don't fiddle with FILE* or streams or lists, just
00267 // use straight file descriptors.)
00268 // 
00269 // We fork a subprogram to do the fancy stuff like sending email.
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     // close some fds, just in case the reason we're crashing is fd
00280     // exhaustion!  Otherwise we won't be able to create our pipe to a
00281     // subprocess.  Probably only closing two fds is possible, but the
00282     // subproc could get confused if all the fds are non-close-on-exec and
00283     // it needs to open a few files.
00284     // 
00285     // Don't close fd 0, 1, or 2, however, since those might be useful to
00286     // the child wvcrash script.  Also, let's skip 3 and 4, in case someone
00287     // uses them for something.  But don't close fd numbers that are *too*
00288     // big; if someone ulimits the number of fds we can use, and *that's*
00289     // why we're crashing, there's no guarantee that high fd numbers are in
00290     // use even if we've run out.
00291     for (int count = 5; count < 15; count++)
00292         close(count);
00293     
00294     if (pipe(fds))
00295         wvcrash_real(sig, 2, 0); // just use stderr instead
00296     else
00297     {
00298         pid = fork();
00299         if (pid < 0)
00300             wvcrash_real(sig, 2, 0); // just use stderr instead
00301         else if (pid == 0) // child
00302         {
00303             close(fds[1]);
00304             dup2(fds[0], 0); // make stdin read from pipe
00305             fcntl(0, F_SETFD, 0);
00306             
00307             execlp("wvcrash", "wvcrash", NULL);
00308             
00309             // if we get here, we couldn't exec wvcrash
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) // parent
00318         {
00319             close(fds[0]);
00320             wvcrash_real(sig, fds[1], pid);
00321         }
00322     }
00323     
00324     // child (usually)
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 // Secret symbol for initialising the will and assert buffers
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

Generated on Fri Oct 5 18:20:27 2007 for WvStreams by  doxygen 1.5.3