Back to index

citadel  8.12
ecrash.c
Go to the documentation of this file.
00001 /*
00002  * author: David Frascone
00003  * 
00004  * eCrash Implementation
00005  *
00006  * eCrash will allow you to capture stack traces in the
00007  * event of a crash, and write those traces to disk, stdout,
00008  * or any other file handle.
00009  *
00010  * modified to integrate closer into citadel by Wilfried Goesgens
00011  *
00012  * vim: ts=4
00013  *
00014  * This program is open source software; you can redistribute it and/or modify
00015  * it under the terms of the GNU General Public License as published by
00016  * the Free Software Foundation; either version 3 of the License, or
00017  * (at your option) any later version.
00018  *
00019  * This program is distributed in the hope that it will be useful,
00020  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00021  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00022  * GNU General Public License for more details.
00023  *
00024  * You should have received a copy of the GNU General Public License
00025  * along with this program; if not, write to the Free Software
00026  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00027  */
00028 
00029 #include "sysdep.h"
00030 #include <stdio.h>
00031 #include <unistd.h>
00032 #include <stdlib.h>
00033 #include <stdarg.h>
00034 #include <string.h>
00035 #include <fcntl.h>
00036 #include <syslog.h>
00037 #include <sys/types.h>
00038 #include <sys/stat.h>
00039 #include <pthread.h>
00040 #include <libcitadel.h>
00041 #include "server.h"
00042 #include "sysdep_decls.h"
00043 #include "support.h"
00044 #include "config.h"
00045 #include "citserver.h"
00046 #include "ecrash.h"
00047 
00048 #define NIY() printf("function not implemented yet!\n");
00049 #ifdef HAVE_BACKTRACE
00050 #include <execinfo.h>
00051 static eCrashParameters gbl_params;
00052 
00053 static int    gbl_backtraceEntries;
00054 static void **gbl_backtraceBuffer;
00055 static char **gbl_backtraceSymbols;
00056 static int    gbl_backtraceDoneFlag = 0;
00057 
00058 static void *stack_frames[50];
00059 static size_t size, NThread;
00060 static char **strings;
00061 
00062 /* 
00063  * Private structures for our thread list
00064  */
00065 typedef struct thread_list_node{
00066        char *threadName;
00067        pthread_t thread;
00068        int backtraceSignal;
00069        sighandler_t oldHandler;
00070        struct thread_list_node *Next;
00071 } ThreadListNode;
00072 
00073 static pthread_mutex_t ThreadListMutex = PTHREAD_MUTEX_INITIALIZER;
00074 static ThreadListNode *ThreadList = NULL;
00075 
00076 /*********************************************************************
00077  *********************************************************************
00078  **     P  R  I  V  A  T  E      F  U  N  C  T  I  O  N  S
00079  *********************************************************************
00080  ********************************************************************/
00081 
00082 
00093 static int addThreadToList(char *name, pthread_t thread,int signo,
00094                                        sighandler_t old_handler)
00095 {
00096        ThreadListNode *node;
00097 
00098        node = malloc(sizeof(ThreadListNode));
00099        if (!node) return -1;
00100 
00101        DPRINTF(ECRASH_DEBUG_VERBOSE,
00102                                    "Adding thread 0x%08x (%s)\n", (unsigned int)thread, name);
00103        node->threadName = strdup(name);
00104        node->thread = thread;
00105        node->backtraceSignal = signo;
00106        node->oldHandler = old_handler;
00107 
00108        /* And, add it to the list */
00109        pthread_mutex_lock(&ThreadListMutex);
00110        node->Next = ThreadList;
00111        ThreadList = node;
00112        pthread_mutex_unlock(&ThreadListMutex);
00113        
00114        return 0;
00115 
00116 } // addThreadToList
00117 
00125 static int removeThreadFromList(pthread_t thread)
00126 {
00127        ThreadListNode *Probe, *Prev=NULL;
00128        ThreadListNode *Removed = NULL;
00129 
00130        DPRINTF(ECRASH_DEBUG_VERBOSE,
00131                                    "Removing thread 0x%08x from list . . .\n", (unsigned int)thread);
00132        pthread_mutex_lock(&ThreadListMutex);
00133        for (Probe=ThreadList;Probe != NULL; Probe = Probe->Next) {
00134               if (Probe->thread == thread) {
00135                      // We found it!  Unlink it and move on!
00136                      Removed = Probe;
00137                      if (Prev == NULL) { // head of list
00138                             ThreadList = Probe->Next;
00139                      } else {
00140                             // Prev != null, so we need to link around ourselves.
00141                             Prev->Next = Probe->Next;
00142                      }
00143                      Removed->Next = NULL;
00144                      break;
00145               }
00146 
00147               Prev = Probe;
00148        }
00149        pthread_mutex_unlock(&ThreadListMutex);
00150 
00151        // Now, if something is in Removed, free it, and return success
00152        if (Removed) {
00153            DPRINTF(ECRASH_DEBUG_VERBOSE,
00154                                           "   Found %s -- removing\n", Removed->threadName);
00155               // Reset the signal handler
00156               signal(Removed->backtraceSignal, Removed->oldHandler);
00157 
00158               // And free the allocated memory
00159               free (Removed->threadName);
00160               free (Removed);
00161 
00162               return 0;
00163        } else {
00164            DPRINTF(ECRASH_DEBUG_VERBOSE,
00165                                           "   Not Found\n");
00166               return -1; // Not Found
00167        }
00168 } // removeThreadFromList
00169 
00181 static void outputPrintf(char *format, ...)
00182 {
00183        va_list ap;
00184 
00185        va_start(ap, format);
00186 
00187        vsyslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, format, ap);
00188 } // outputPrintf
00189 
00190 
00191 
00199 static void createGlobalBacktrace( void )
00200 {
00201 
00202        size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
00203        for (NThread = 0; NThread < size; NThread++) 
00204        {
00205               syslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, "RAW: %p  ", stack_frames[NThread]);
00206        }
00207        strings = backtrace_symbols(stack_frames, size);
00208        for (NThread = 0; NThread < size; NThread++) {
00209               if (strings != NULL) {
00210                      syslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, "RAW: %p  ", strings[NThread]);
00211               }
00212        }
00213 } /* createGlobalBacktrace */
00214 static void outputRawtrace( void )
00215 {
00216 
00217        size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
00218        for (NThread = 0; NThread < size; NThread++) 
00219        {
00220               syslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, "RAW: %p  ", stack_frames[NThread]);
00221        }
00222 } /* createGlobalBacktrace */
00223 
00227 static void outputGlobalBacktrace ( void )
00228 {
00229        int i;
00230 
00231        for (i=0; i < gbl_backtraceEntries; i++) {
00232               if (gbl_backtraceSymbols != FALSE) {
00233                      outputPrintf("*      Frame %02x: %s\n",
00234                                  i, gbl_backtraceSymbols[i]);
00235               } else {
00236                      outputPrintf("*      Frame %02x: %p\n", i,
00237                                  gbl_backtraceBuffer[i]);
00238               }
00239        }
00240 } // outputGlobalBacktrace
00241 
00245 static void outputBacktrace( void )
00246 {
00247        createGlobalBacktrace();
00248        outputGlobalBacktrace();
00249 } /* outputBacktrace */
00250 
00251 static void outputBacktraceThreads( void )
00252 {
00253        ThreadListNode *probe;
00254        int i;
00255 
00256        // When we're backtracing, don't worry about the mutex . . hopefully
00257        // we're in a safe place.
00258 
00259        for (probe=ThreadList; probe; probe=probe->Next) {
00260               gbl_backtraceDoneFlag = 0;
00261               pthread_kill(probe->thread, probe->backtraceSignal);
00262               for (i=0; i < gbl_params.threadWaitTime; i++) {
00263                      if (gbl_backtraceDoneFlag)
00264                             break;
00265                      sleep(1);
00266               }
00267               if (gbl_backtraceDoneFlag) {
00268                      outputPrintf("*  Backtrace of \"%s\" (0x%08x)\n", 
00269                                            probe->threadName, (unsigned int)probe->thread);
00270                      outputGlobalBacktrace();
00271               } else {
00272                      outputPrintf("*  Error: unable to get backtrace of \"%s\" (0x%08x)\n", 
00273                                            probe->threadName, (unsigned int)probe->thread);
00274               }
00275               outputPrintf("*\n");
00276        }
00277 } // outputBacktraceThreads
00278 
00279 
00291 static void crash_handler(int signo)
00292 {
00293        outputRawtrace();
00294        outputPrintf("*********************************************************\n");
00295        outputPrintf("*               eCrash Crash Handler\n");
00296        outputPrintf("*********************************************************\n");
00297        outputPrintf("*\n");
00298        outputPrintf("*  Got a crash! signo=%d\n", signo);
00299        outputPrintf("*\n");
00300        outputPrintf("*  Offending Thread's Backtrace:\n");
00301        outputPrintf("*\n");
00302        outputBacktrace();
00303        outputPrintf("*\n");
00304 
00305        if (gbl_params.dumpAllThreads != FALSE) {
00306               outputBacktraceThreads();
00307        }
00308 
00309        outputPrintf("*\n");
00310        outputPrintf("*********************************************************\n");
00311        outputPrintf("*               eCrash Crash Handler\n");
00312        outputPrintf("*********************************************************\n");
00313 
00314        exit(signo);
00315 } // crash_handler
00316 
00328 static void bt_handler(int signo)
00329 {
00330        createGlobalBacktrace();
00331        gbl_backtraceDoneFlag=1;
00332 } // bt_handler
00333 
00340 static int ValidateSymbolTable( void )
00341 {
00342        int i;
00343        int rc=0;
00344        unsigned long lastAddress =0;
00345 
00346        // Get out of here if the table is empty
00347        if (!gbl_params.symbolTable) return 0;
00348 
00349        // Dump it in verbose mode
00350        DPRINTF(ECRASH_DEBUG_VERBOSE,
00351                                    "Symbol Table Provided with %d symbols\n",
00352                                    gbl_params.symbolTable->numSymbols);
00353        for (i=0; i < gbl_params.symbolTable->numSymbols; i++){
00354               // Dump it in verbose mode
00355               DPRINTF(ECRASH_DEBUG_VERBOSE, 
00356                             "%-30s %p\n",
00357                             gbl_params.symbolTable->symbols[i].function,
00358                             gbl_params.symbolTable->symbols[i].address);
00359               if (lastAddress >
00360                   (unsigned long)gbl_params.symbolTable->symbols[i].address) {
00361                      DPRINTF(ECRASH_DEBUG_ERROR,
00362                                    "Error: symbol table is not sorted (last=%p, current=%p)\n",
00363                                    (void *)lastAddress,
00364                                    gbl_params.symbolTable->symbols[i].address);
00365                      rc = -1;
00366               }
00367 
00368        } // for
00369 
00370        return rc;
00371        
00372 } // ValidateSymbolTable
00373 
00374 /*********************************************************************
00375  *********************************************************************
00376  **      P  U  B  L  I  C      F  U  N  C  T  I  O  N  S
00377  *********************************************************************
00378  ********************************************************************/
00379 
00391 int eCrash_Init(eCrashParameters *params)
00392 {
00393        int sigIndex;
00394        int ret = 0;
00395 #ifdef DO_SIGNALS_RIGHT
00396        sigset_t blocked;
00397        struct sigaction act;
00398 #endif
00399 
00400        DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,"Init Starting params = %p\n", params);
00401 
00402        // Allocate our backtrace area
00403        gbl_backtraceBuffer = malloc(sizeof(void *) * (params->maxStackDepth+5));
00404 
00405 #ifdef DO_SIGNALS_RIGHT
00406        sigemptyset(&blocked);
00407        act.sa_sigaction = crash_handler;
00408        act.sa_mask = blocked;
00409        act.sa_flags = SA_SIGINFO;
00410 #endif
00411 
00412        if (params != NULL) {
00413               // Make ourselves a global copy of params.
00414               gbl_params = *params;
00415               gbl_params.filename = strdup(params->filename);
00416 
00417               // Set our defaults, if they weren't specified
00418               if (gbl_params.maxStackDepth == 0 )
00419                      gbl_params.maxStackDepth = ECRASH_DEFAULT_STACK_DEPTH;
00420 
00421               if (gbl_params.defaultBacktraceSignal == 0 )
00422                      gbl_params.defaultBacktraceSignal = ECRASH_DEFAULT_BACKTRACE_SIGNAL;
00423 
00424               if (gbl_params.threadWaitTime == 0 )
00425                      gbl_params.threadWaitTime = ECRASH_DEFAULT_THREAD_WAIT_TIME;
00426 
00427               if (gbl_params.debugLevel == 0 )
00428                      gbl_params.debugLevel = ECRASH_DEBUG_DEFAULT;
00429 
00430               // Copy our symbol table
00431               if (gbl_params.symbolTable) {
00432                   DPRINTF(ECRASH_DEBUG_VERBOSE,
00433                                                  "symbolTable @ %p -- %d symbols\n", gbl_params.symbolTable,
00434                                           gbl_params.symbolTable->numSymbols);
00435                      // Make a copy of our symbol table
00436                      gbl_params.symbolTable = malloc(sizeof(eCrashSymbolTable));
00437                      memcpy(gbl_params.symbolTable, params->symbolTable,
00438                                sizeof(eCrashSymbolTable));
00439 
00440                      // Now allocate / copy the actual table.
00441                      gbl_params.symbolTable->symbols = malloc(sizeof(eCrashSymbol) *
00442                                                               gbl_params.symbolTable->numSymbols);
00443                      memcpy(gbl_params.symbolTable->symbols,
00444                                params->symbolTable->symbols,
00445                                sizeof(eCrashSymbol) * gbl_params.symbolTable->numSymbols);
00446 
00447                      ValidateSymbolTable();
00448               }
00449        
00450               // And, finally, register for our signals
00451               for (sigIndex=0; gbl_params.signals[sigIndex] != 0; sigIndex++) {
00452                      DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,
00453                                                  "   Catching signal[%d] %d\n", sigIndex,
00454                                    gbl_params.signals[sigIndex]);
00455 
00456                      // I know there's a better way to catch signals with pthreads.
00457                      // I'll do it later TODO
00458                      signal(gbl_params.signals[sigIndex], crash_handler);
00459               }
00460        } else {
00461               DPRINTF(ECRASH_DEBUG_ERROR, "   Error:  Null Params!\n");
00462               ret = -1;
00463        }
00464        DPRINTF(ECRASH_DEBUG_VERY_VERBOSE, "Init Complete ret=%d\n", ret);
00465        return ret;
00466 } /* eCrash_Init */
00467 
00476 int eCrash_Uninit( void )
00477 {
00478        NIY();
00479 
00480        return 0;
00481 } /* eCrash_Uninit */
00482 
00494 int eCrash_RegisterThread(char *name, int signo)
00495 {
00496        sighandler_t old_handler;
00497 
00498        // Register for our signal
00499        if (signo == 0) {
00500               signo = gbl_params.defaultBacktraceSignal;
00501        }
00502 
00503        old_handler = signal(signo, bt_handler);
00504        return addThreadToList(name, pthread_self(), signo, old_handler);
00505 
00506 } /* eCrash_RegisterThread */
00507 
00516 int eCrash_UnregisterThread( void )
00517 {
00518        return removeThreadFromList(pthread_self());
00519 } /* eCrash_UnregisterThread */
00520 
00521 #endif