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 
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 // FIXME: this file mostly only works in Linux
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 // Reserve enough buffer for a screenful of programme.
00037 static const int buffer_size = 2048;
00038 static char desc[buffer_size];
00039 WvCrashCallback callback;
00040 
00041 // write a string 'str' to fd
00042 static void wr(int fd, const char *str)
00043 {
00044     write(fd, str, strlen(str));
00045 }
00046 
00047 
00048 // convert 'num' to a string and write it to fd.
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     // Write out the PID and PPID.
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     // Write out the assertion message, as logged by __assert*_fail(), if any.
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     // Write out the note, if any.
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         // Wait up to 10 seconds for child to write wvcrash file in case there
00143         // is limited space availible on the device; wvcrash file is more
00144         // useful than core dump
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     // we want to create a coredump, and the kernel seems to not want to do
00157     // that if we send ourselves the same signal that we're already in.
00158     // Whatever... just send a different one :)
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 // Hint: we can't do anything really difficult here, because the program is
00170 // probably really confused.  So we should try to limit this to straight
00171 // kernel syscalls (ie. don't fiddle with FILE* or streams or lists, just
00172 // use straight file descriptors.)
00173 // 
00174 // We fork a subprogram to do the fancy stuff like sending email.
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     // close some fds, just in case the reason we're crashing is fd
00188     // exhaustion!  Otherwise we won't be able to create our pipe to a
00189     // subprocess.  Probably only closing two fds is possible, but the
00190     // subproc could get confused if all the fds are non-close-on-exec and
00191     // it needs to open a few files.
00192     // 
00193     // Don't close fd 0, 1, or 2, however, since those might be useful to
00194     // the child wvcrash script.  Also, let's skip 3 and 4, in case someone
00195     // uses them for something.  But don't close fd numbers that are *too*
00196     // big; if someone ulimits the number of fds we can use, and *that's*
00197     // why we're crashing, there's no guarantee that high fd numbers are in
00198     // use even if we've run out.
00199     for (int count = 5; count < 15; count++)
00200         close(count);
00201     
00202     if (pipe(fds))
00203         wvcrash_real(sig, 2, 0); // just use stderr instead
00204     else
00205     {
00206         pid = fork();
00207         if (pid < 0)
00208             wvcrash_real(sig, 2, 0); // just use stderr instead
00209         else if (pid == 0) // child
00210         {
00211             close(fds[1]);
00212             dup2(fds[0], 0); // make stdin read from pipe
00213             fcntl(0, F_SETFD, 0);
00214             
00215             execlp("wvcrash", "wvcrash", NULL);
00216             
00217             // if we get here, we couldn't exec wvcrash
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) // parent
00226         {
00227             close(fds[0]);
00228             wvcrash_real(sig, fds[1], pid);
00229         }
00230     }
00231     
00232     // child (usually)
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; // wvstreams can be a pig
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 // Secret symbol for initialising the will and assert buffers
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

Generated on Sun Sep 24 20:10:50 2006 for WvStreams by  doxygen 1.4.7