* When saving a Task, if the status is COMPLETED then also set PERCENT-COMPLETE:100...
[citadel.git] / citadel / ecrash.c
blob6a8ef3a0c1ce905151df7664d5f85ed6ec514474
1 /*
2 * File: eCrash.c
3 * @author David Frascone
4 *
5 * eCrash Implementation
7 * eCrash will allow you to capture stack traces in the
8 * event of a crash, and write those traces to disk, stdout,
9 * or any other file handle.
11 * modified to integrate closer into citadel by Wilfried Goesgens
13 * vim: ts=4
16 #include "sysdep.h"
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include <string.h>
22 #include <fcntl.h>
23 #include <syslog.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <pthread.h>
27 #include <libcitadel.h>
28 #include "server.h"
29 #include "sysdep_decls.h"
30 #include "support.h"
31 #include "config.h"
32 #include "citserver.h"
33 #include "ecrash.h"
35 #define NIY() printf("%s: Not Implemented Yet!\n", __FUNCTION__)
36 #ifdef HAVE_BACKTRACE
37 #include <execinfo.h>
38 static eCrashParameters gbl_params;
40 static int gbl_backtraceEntries;
41 static void **gbl_backtraceBuffer;
42 static char **gbl_backtraceSymbols;
43 static int gbl_backtraceDoneFlag = 0;
45 static void *stack_frames[50];
46 static size_t size, NThread;
47 static char **strings;
48 static char StaticBuf[SIZ];
50 /*
51 * Private structures for our thread list
53 typedef struct thread_list_node{
54 char *threadName;
55 pthread_t thread;
56 int backtraceSignal;
57 sighandler_t oldHandler;
58 struct thread_list_node *Next;
59 } ThreadListNode;
61 static pthread_mutex_t ThreadListMutex = PTHREAD_MUTEX_INITIALIZER;
62 static ThreadListNode *ThreadList = NULL;
64 /*********************************************************************
65 *********************************************************************
66 ** P R I V A T E F U N C T I O N S
67 *********************************************************************
68 ********************************************************************/
71 /*!
72 * Insert a node into our threadList
74 * @param name Text string indicating our thread
75 * @param thread Our Thread Id
76 * @param signo Signal to create backtrace with
77 * @param old_handler Our old handler for signo
79 * @returns zero on success
81 static int addThreadToList(char *name, pthread_t thread,int signo,
82 sighandler_t old_handler)
84 ThreadListNode *node;
86 node = malloc(sizeof(ThreadListNode));
87 if (!node) return -1;
89 DPRINTF(ECRASH_DEBUG_VERBOSE,
90 "Adding thread 0x%08x (%s)\n", (unsigned int)thread, name);
91 node->threadName = strdup(name);
92 node->thread = thread;
93 node->backtraceSignal = signo;
94 node->oldHandler = old_handler;
96 /* And, add it to the list */
97 pthread_mutex_lock(&ThreadListMutex);
98 node->Next = ThreadList;
99 ThreadList = node;
100 pthread_mutex_unlock(&ThreadListMutex);
102 return 0;
104 } // addThreadToList
107 * Remove a node from our threadList
109 * @param thread Our Thread Id
111 * @returns zero on success
113 static int removeThreadFromList(pthread_t thread)
115 ThreadListNode *Probe, *Prev=NULL;
116 ThreadListNode *Removed = NULL;
118 DPRINTF(ECRASH_DEBUG_VERBOSE,
119 "Removing thread 0x%08x from list . . .\n", (unsigned int)thread);
120 pthread_mutex_lock(&ThreadListMutex);
121 for (Probe=ThreadList;Probe != NULL; Probe = Probe->Next) {
122 if (Probe->thread == thread) {
123 // We found it! Unlink it and move on!
124 Removed = Probe;
125 if (Prev == NULL) { // head of list
126 ThreadList = Probe->Next;
127 } else {
128 // Prev != null, so we need to link around ourselves.
129 Prev->Next = Probe->Next;
131 Removed->Next = NULL;
132 break;
135 Prev = Probe;
137 pthread_mutex_unlock(&ThreadListMutex);
139 // Now, if something is in Removed, free it, and return success
140 if (Removed) {
141 DPRINTF(ECRASH_DEBUG_VERBOSE,
142 " Found %s -- removing\n", Removed->threadName);
143 // Reset the signal handler
144 signal(Removed->backtraceSignal, Removed->oldHandler);
146 // And free the allocated memory
147 free (Removed->threadName);
148 free (Removed);
150 return 0;
151 } else {
152 DPRINTF(ECRASH_DEBUG_VERBOSE,
153 " Not Found\n");
154 return -1; // Not Found
156 } // removeThreadFromList
159 * Print out a line of output to all our destinations
161 * One by one, output a line of text to all of our output destinations.
163 * Return failure if we fail to output to any of them.
165 * @param format Normal printf style vararg format
167 * @returns nothing// bytes written, or error on failure.
169 static void outputPrintf(char *format, ...)
171 va_list ap;
173 va_start(ap, format);
175 if (enable_syslog)
177 snprintf (StaticBuf, SIZ, format, ap);
178 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
180 else
181 CtdlLogPrintf(CTDL_EMERG, format, ap);
183 } // outputPrintf
188 * Dump our backtrace into a global location
190 * This function will dump out our backtrace into our
191 * global holding area.
194 static void createGlobalBacktrace( void )
197 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
198 if (enable_syslog)
199 for (NThread = 0; NThread < size; NThread++)
201 snprintf (StaticBuf, SIZ, "RAW: %p ", stack_frames[NThread]);
202 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
204 else
205 for (NThread = 0; NThread < size; NThread++)
206 CtdlLogPrintf(1, "RAW: %p\n", stack_frames[NThread]);
207 strings = backtrace_symbols(stack_frames, size);
208 for (NThread = 0; NThread < size; NThread++) {
209 if (strings != NULL) {
210 if (enable_syslog)
211 {// vsyslogs printf compliance sucks.
212 snprintf (StaticBuf, SIZ, "RAW: %p ", strings[NThread]);
213 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
215 else
216 CtdlLogPrintf(1, "%s\n", strings[NThread]);
219 } /* createGlobalBacktrace */
220 static void outputRawtrace( void )
223 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
224 if (enable_syslog)
225 for (NThread = 0; NThread < size; NThread++)
227 snprintf (StaticBuf, SIZ, "RAW: %p ", stack_frames[NThread]);
228 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
230 else
231 for (NThread = 0; NThread < size; NThread++)
232 CtdlLogPrintf(1, "RAW: %p\n", stack_frames[NThread]);
233 } /* createGlobalBacktrace */
236 * Print out (to all the fds, etc), or global backtrace
238 static void outputGlobalBacktrace ( void )
240 int i;
242 for (i=0; i < gbl_backtraceEntries; i++) {
243 if (gbl_backtraceSymbols != FALSE) {
244 outputPrintf("* Frame %02x: %s\n",
245 i, gbl_backtraceSymbols[i]);
246 } else {
247 outputPrintf("* Frame %02x: %p\n", i,
248 gbl_backtraceBuffer[i]);
251 } // outputGlobalBacktrace
254 * Output our current stack's backtrace
256 static void outputBacktrace( void )
258 createGlobalBacktrace();
259 outputGlobalBacktrace();
260 } /* outputBacktrace */
262 static void outputBacktraceThreads( void )
264 ThreadListNode *probe;
265 int i;
267 // When we're backtracing, don't worry about the mutex . . hopefully
268 // we're in a safe place.
270 for (probe=ThreadList; probe; probe=probe->Next) {
271 gbl_backtraceDoneFlag = 0;
272 pthread_kill(probe->thread, probe->backtraceSignal);
273 for (i=0; i < gbl_params.threadWaitTime; i++) {
274 if (gbl_backtraceDoneFlag)
275 break;
276 sleep(1);
278 if (gbl_backtraceDoneFlag) {
279 outputPrintf("* Backtrace of \"%s\" (0x%08x)\n",
280 probe->threadName, (unsigned int)probe->thread);
281 outputGlobalBacktrace();
282 } else {
283 outputPrintf("* Error: unable to get backtrace of \"%s\" (0x%08x)\n",
284 probe->threadName, (unsigned int)probe->thread);
286 outputPrintf("*\n");
288 } // outputBacktraceThreads
292 * Handle signals (crash signals)
294 * This function will catch all crash signals, and will output the
295 * crash dump.
297 * It will physically write (and sync) the current thread's information
298 * before it attempts to send signals to other threads.
300 * @param signum Signal received.
302 static void crash_handler(int signo)
304 outputRawtrace();
305 outputPrintf("*********************************************************\n");
306 outputPrintf("* eCrash Crash Handler\n");
307 outputPrintf("*********************************************************\n");
308 outputPrintf("*\n");
309 outputPrintf("* Got a crash! signo=%d\n", signo);
310 outputPrintf("*\n");
311 outputPrintf("* Offending Thread's Backtrace:\n");
312 outputPrintf("*\n");
313 outputBacktrace();
314 outputPrintf("*\n");
316 if (gbl_params.dumpAllThreads != FALSE) {
317 outputBacktraceThreads();
320 outputPrintf("*\n");
321 outputPrintf("*********************************************************\n");
322 outputPrintf("* eCrash Crash Handler\n");
323 outputPrintf("*********************************************************\n");
325 exit(signo);
326 } // crash_handler
329 * Handle signals (bt signals)
331 * This function shoudl be called to generate a crashdump into our
332 * global area. Once the dump has been completed, this function will
333 * return after tickling a global. Since mutexes are not async
334 * signal safe, the main thread, after signaling us to generate our
335 * own backtrace, will sleep for a few seconds waiting for us to complete.
337 * @param signum Signal received.
339 static void bt_handler(int signo)
341 createGlobalBacktrace();
342 gbl_backtraceDoneFlag=1;
343 } // bt_handler
346 * Validate a passed-in symbol table
348 * For now, just print it out (if verbose), and make sure it's
349 * sorted and none of the pointers are zero.
351 static int ValidateSymbolTable( void )
353 int i;
354 int rc=0;
355 unsigned long lastAddress =0;
357 // Get out of here if the table is empty
358 if (!gbl_params.symbolTable) return 0;
360 // Dump it in verbose mode
361 DPRINTF(ECRASH_DEBUG_VERBOSE,
362 "Symbol Table Provided with %d symbols\n",
363 gbl_params.symbolTable->numSymbols);
364 for (i=0; i < gbl_params.symbolTable->numSymbols; i++){
365 // Dump it in verbose mode
366 DPRINTF(ECRASH_DEBUG_VERBOSE,
367 "%-30s %p\n",
368 gbl_params.symbolTable->symbols[i].function,
369 gbl_params.symbolTable->symbols[i].address);
370 if (lastAddress >
371 (unsigned long)gbl_params.symbolTable->symbols[i].address) {
372 DPRINTF(ECRASH_DEBUG_ERROR,
373 "Error: symbol table is not sorted (last=%p, current=%p)\n",
374 (void *)lastAddress,
375 gbl_params.symbolTable->symbols[i].address);
376 rc = -1;
379 } // for
381 return rc;
383 } // ValidateSymbolTable
385 /*********************************************************************
386 *********************************************************************
387 ** P U B L I C F U N C T I O N S
388 *********************************************************************
389 ********************************************************************/
392 * Initialize eCrash.
394 * This function must be called before calling any other eCrash
395 * functions. It sets up the global behavior of the system, and
396 * registers the calling thread for crash dumps.
398 * @param params Our input parameters. The passed in structure will be copied.
400 * @return Zero on success.
402 int eCrash_Init(eCrashParameters *params)
404 int sigIndex;
405 int ret = 0;
406 #ifdef DO_SIGNALS_RIGHT
407 sigset_t blocked;
408 struct sigaction act;
409 #endif
411 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,"Init Starting params = %p\n", params);
413 // Allocate our backtrace area
414 gbl_backtraceBuffer = malloc(sizeof(void *) * (params->maxStackDepth+5));
416 #ifdef DO_SIGNALS_RIGHT
417 sigemptyset(&blocked);
418 act.sa_sigaction = crash_handler;
419 act.sa_mask = blocked;
420 act.sa_flags = SA_SIGINFO;
421 #endif
423 if (params != NULL) {
424 // Make ourselves a global copy of params.
425 gbl_params = *params;
426 gbl_params.filename = strdup(params->filename);
428 // Set our defaults, if they weren't specified
429 if (gbl_params.maxStackDepth == 0 )
430 gbl_params.maxStackDepth = ECRASH_DEFAULT_STACK_DEPTH;
432 if (gbl_params.defaultBacktraceSignal == 0 )
433 gbl_params.defaultBacktraceSignal = ECRASH_DEFAULT_BACKTRACE_SIGNAL;
435 if (gbl_params.threadWaitTime == 0 )
436 gbl_params.threadWaitTime = ECRASH_DEFAULT_THREAD_WAIT_TIME;
438 if (gbl_params.debugLevel == 0 )
439 gbl_params.debugLevel = ECRASH_DEBUG_DEFAULT;
441 // Copy our symbol table
442 if (gbl_params.symbolTable) {
443 DPRINTF(ECRASH_DEBUG_VERBOSE,
444 "symbolTable @ %p -- %d symbols\n", gbl_params.symbolTable,
445 gbl_params.symbolTable->numSymbols);
446 // Make a copy of our symbol table
447 gbl_params.symbolTable = malloc(sizeof(eCrashSymbolTable));
448 memcpy(gbl_params.symbolTable, params->symbolTable,
449 sizeof(eCrashSymbolTable));
451 // Now allocate / copy the actual table.
452 gbl_params.symbolTable->symbols = malloc(sizeof(eCrashSymbol) *
453 gbl_params.symbolTable->numSymbols);
454 memcpy(gbl_params.symbolTable->symbols,
455 params->symbolTable->symbols,
456 sizeof(eCrashSymbol) * gbl_params.symbolTable->numSymbols);
458 ValidateSymbolTable();
461 // And, finally, register for our signals
462 for (sigIndex=0; gbl_params.signals[sigIndex] != 0; sigIndex++) {
463 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,
464 " Catching signal[%d] %d\n", sigIndex,
465 gbl_params.signals[sigIndex]);
467 // I know there's a better way to catch signals with pthreads.
468 // I'll do it later TODO
469 signal(gbl_params.signals[sigIndex], crash_handler);
471 } else {
472 DPRINTF(ECRASH_DEBUG_ERROR, " Error: Null Params!\n");
473 ret = -1;
475 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE, "Init Complete ret=%d\n", ret);
476 return ret;
477 } /* eCrash_Init */
480 * UnInitialize eCrash.
482 * This function may be called to de-activate eCrash, release the
483 * signal handlers, and free any memory allocated by eCrash.
485 * @return Zero on success.
487 int eCrash_Uninit( void )
489 NIY();
491 return 0;
492 } /* eCrash_Uninit */
495 * Register a thread for backtracing on crash.
497 * This function must be called by any thread wanting it's stack
498 * dumped in the event of a crash. The thread my specify what
499 * signal should be used, or the default, SIGUSR1 will be used.
501 * @param signo Signal to use to generate dump (default: SIGUSR1)
503 * @return Zero on success.
505 int eCrash_RegisterThread(char *name, int signo)
507 sighandler_t old_handler;
509 // Register for our signal
510 if (signo == 0) {
511 signo = gbl_params.defaultBacktraceSignal;
514 old_handler = signal(signo, bt_handler);
515 return addThreadToList(name, pthread_self(), signo, old_handler);
517 } /* eCrash_RegisterThread */
520 * Un-register a thread for stack dumps.
522 * This function may be called to un-register any previously
523 * registered thread.
525 * @return Zero on success.
527 int eCrash_UnregisterThread( void )
529 return removeThreadFromList(pthread_self());
530 } /* eCrash_UnregisterThread */
532 #endif