3 * @author David Frascone
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
23 #include <sys/types.h>
26 #include "sysdep_decls.h"
29 #define NIY() printf("%s: Not Implemented Yet!\n", __FUNCTION__)
32 static eCrashParameters gbl_params;
34 static int gbl_backtraceEntries;
35 static void **gbl_backtraceBuffer;
36 static char **gbl_backtraceSymbols;
37 static int gbl_backtraceDoneFlag = 0;
39 static void *stack_frames[50];
40 static size_t size, NThread;
41 static char **strings;
42 static char StrBuf[SIZ];
45 * Private structures for our thread list
47 typedef struct thread_list_node{
51 sighandler_t oldHandler;
52 struct thread_list_node *Next;
55 static pthread_mutex_t ThreadListMutex = PTHREAD_MUTEX_INITIALIZER;
56 static ThreadListNode *ThreadList = NULL;
58 /*********************************************************************
59 *********************************************************************
60 ** P R I V A T E F U N C T I O N S
61 *********************************************************************
62 ********************************************************************/
66 * Insert a node into our threadList
68 * @param name Text string indicating our thread
69 * @param thread Our Thread Id
70 * @param signo Signal to create backtrace with
71 * @param old_handler Our old handler for signo
73 * @returns zero on success
75 static int addThreadToList(char *name, pthread_t thread,int signo,
76 sighandler_t old_handler)
80 node = malloc(sizeof(ThreadListNode));
83 DPRINTF(ECRASH_DEBUG_VERBOSE,
84 "Adding thread 0x%08x (%s)\n", (unsigned int)thread, name);
85 node->threadName = strdup(name);
86 node->thread = thread;
87 node->backtraceSignal = signo;
88 node->oldHandler = old_handler;
90 /* And, add it to the list */
91 pthread_mutex_lock(&ThreadListMutex);
92 node->Next = ThreadList;
94 pthread_mutex_unlock(&ThreadListMutex);
101 * Remove a node from our threadList
103 * @param thread Our Thread Id
105 * @returns zero on success
107 static int removeThreadFromList(pthread_t thread)
109 ThreadListNode *Probe, *Prev=NULL;
110 ThreadListNode *Removed = NULL;
112 DPRINTF(ECRASH_DEBUG_VERBOSE,
113 "Removing thread 0x%08x from list . . .\n", (unsigned int)thread);
114 pthread_mutex_lock(&ThreadListMutex);
115 for (Probe=ThreadList;Probe != NULL; Probe = Probe->Next) {
116 if (Probe->thread == thread) {
117 // We found it! Unlink it and move on!
119 if (Prev == NULL) { // head of list
120 ThreadList = Probe->Next;
122 // Prev != null, so we need to link around ourselves.
123 Prev->Next = Probe->Next;
125 Removed->Next = NULL;
131 pthread_mutex_unlock(&ThreadListMutex);
133 // Now, if something is in Removed, free it, and return success
135 DPRINTF(ECRASH_DEBUG_VERBOSE,
136 " Found %s -- removing\n", Removed->threadName);
137 // Reset the signal handler
138 signal(Removed->backtraceSignal, Removed->oldHandler);
140 // And free the allocated memory
141 free (Removed->threadName);
146 DPRINTF(ECRASH_DEBUG_VERBOSE,
148 return -1; // Not Found
150 } // removeThreadFromList
153 * Print out a line of output to all our destinations
155 * One by one, output a line of text to all of our output destinations.
157 * Return failure if we fail to output to any of them.
159 * @param format Normal printf style vararg format
161 * @returns nothing// bytes written, or error on failure.
163 static void outputPrintf(char *format, ...)
167 va_start(ap, format);
171 snprintf (StrBuf, SIZ, format, ap);
172 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StrBuf);
175 lprintf(CTDL_EMERG, format, ap);
182 * Dump our backtrace into a global location
184 * This function will dump out our backtrace into our
185 * global holding area.
188 static void createGlobalBacktrace( void )
191 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
193 for (NThread = 0; NThread < size; NThread++)
195 snprintf (StrBuf, SIZ, "RAW: %p ", stack_frames[NThread]);
196 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StrBuf);
199 for (NThread = 0; NThread < size; NThread++)
200 lprintf(1, "RAW: %p\n", stack_frames[NThread]);
201 strings = backtrace_symbols(stack_frames, size);
202 for (NThread = 0; NThread < size; NThread++) {
203 if (strings != NULL) {
205 {// vsyslogs printf compliance sucks.
206 snprintf (StrBuf, SIZ, "RAW: %p ", strings[NThread]);
207 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StrBuf);
210 lprintf(1, "%s\n", strings[NThread]);
213 } /* createGlobalBacktrace */
214 static void outputRawtrace( void )
217 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
219 for (NThread = 0; NThread < size; NThread++)
221 snprintf (StrBuf, SIZ, "RAW: %p ", stack_frames[NThread]);
222 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StrBuf);
225 for (NThread = 0; NThread < size; NThread++)
226 lprintf(1, "RAW: %p\n", stack_frames[NThread]);
227 } /* createGlobalBacktrace */
230 * Print out (to all the fds, etc), or global backtrace
232 static void outputGlobalBacktrace ( void )
236 for (i=0; i < gbl_backtraceEntries; i++) {
237 if (gbl_backtraceSymbols != FALSE) {
238 outputPrintf("* Frame %02x: %s\n",
239 i, gbl_backtraceSymbols[i]);
241 outputPrintf("* Frame %02x: %p\n", i,
242 gbl_backtraceBuffer[i]);
245 } // outputGlobalBacktrace
248 * Output our current stack's backtrace
250 static void outputBacktrace( void )
252 createGlobalBacktrace();
253 outputGlobalBacktrace();
254 } /* outputBacktrace */
256 static void outputBacktraceThreads( void )
258 ThreadListNode *probe;
261 // When we're backtracing, don't worry about the mutex . . hopefully
262 // we're in a safe place.
264 for (probe=ThreadList; probe; probe=probe->Next) {
265 gbl_backtraceDoneFlag = 0;
266 pthread_kill(probe->thread, probe->backtraceSignal);
267 for (i=0; i < gbl_params.threadWaitTime; i++) {
268 if (gbl_backtraceDoneFlag)
272 if (gbl_backtraceDoneFlag) {
273 outputPrintf("* Backtrace of \"%s\" (0x%08x)\n",
274 probe->threadName, (unsigned int)probe->thread);
275 outputGlobalBacktrace();
277 outputPrintf("* Error: unable to get backtrace of \"%s\" (0x%08x)\n",
278 probe->threadName, (unsigned int)probe->thread);
282 } // outputBacktraceThreads
286 * Handle signals (crash signals)
288 * This function will catch all crash signals, and will output the
291 * It will physically write (and sync) the current thread's information
292 * before it attempts to send signals to other threads.
294 * @param signum Signal received.
296 static void crash_handler(int signo)
299 outputPrintf("*********************************************************\n");
300 outputPrintf("* eCrash Crash Handler\n");
301 outputPrintf("*********************************************************\n");
303 outputPrintf("* Got a crash! signo=%d\n", signo);
305 outputPrintf("* Offending Thread's Backtrace:\n");
310 if (gbl_params.dumpAllThreads != FALSE) {
311 outputBacktraceThreads();
315 outputPrintf("*********************************************************\n");
316 outputPrintf("* eCrash Crash Handler\n");
317 outputPrintf("*********************************************************\n");
323 * Handle signals (bt signals)
325 * This function shoudl be called to generate a crashdump into our
326 * global area. Once the dump has been completed, this function will
327 * return after tickling a global. Since mutexes are not async
328 * signal safe, the main thread, after signaling us to generate our
329 * own backtrace, will sleep for a few seconds waiting for us to complete.
331 * @param signum Signal received.
333 static void bt_handler(int signo)
335 createGlobalBacktrace();
336 gbl_backtraceDoneFlag=1;
340 * Validate a passed-in symbol table
342 * For now, just print it out (if verbose), and make sure it's
343 * sorted and none of the pointers are zero.
345 static int ValidateSymbolTable( void )
349 unsigned long lastAddress =0;
351 // Get out of here if the table is empty
352 if (!gbl_params.symbolTable) return 0;
354 // Dump it in verbose mode
355 DPRINTF(ECRASH_DEBUG_VERBOSE,
356 "Symbol Table Provided with %d symbols\n",
357 gbl_params.symbolTable->numSymbols);
358 for (i=0; i < gbl_params.symbolTable->numSymbols; i++){
359 // Dump it in verbose mode
360 DPRINTF(ECRASH_DEBUG_VERBOSE,
362 gbl_params.symbolTable->symbols[i].function,
363 gbl_params.symbolTable->symbols[i].address);
365 (unsigned long)gbl_params.symbolTable->symbols[i].address) {
366 DPRINTF(ECRASH_DEBUG_ERROR,
367 "Error: symbol table is not sorted (last=%p, current=%p)\n",
369 gbl_params.symbolTable->symbols[i].address);
377 } // ValidateSymbolTable
379 /*********************************************************************
380 *********************************************************************
381 ** P U B L I C F U N C T I O N S
382 *********************************************************************
383 ********************************************************************/
388 * This function must be called before calling any other eCrash
389 * functions. It sets up the global behavior of the system, and
390 * registers the calling thread for crash dumps.
392 * @param params Our input parameters. The passed in structure will be copied.
394 * @return Zero on success.
396 int eCrash_Init(eCrashParameters *params)
400 #ifdef DO_SIGNALS_RIGHT
402 struct sigaction act;
405 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,"Init Starting params = %p\n", params);
407 // Allocate our backtrace area
408 gbl_backtraceBuffer = malloc(sizeof(void *) * (params->maxStackDepth+5));
410 #ifdef DO_SIGNALS_RIGHT
411 sigemptyset(&blocked);
412 act.sa_sigaction = crash_handler;
413 act.sa_mask = blocked;
414 act.sa_flags = SA_SIGINFO;
417 if (params != NULL) {
418 // Make ourselves a global copy of params.
419 gbl_params = *params;
420 gbl_params.filename = strdup(params->filename);
422 // Set our defaults, if they weren't specified
423 if (gbl_params.maxStackDepth == 0 )
424 gbl_params.maxStackDepth = ECRASH_DEFAULT_STACK_DEPTH;
426 if (gbl_params.defaultBacktraceSignal == 0 )
427 gbl_params.defaultBacktraceSignal = ECRASH_DEFAULT_BACKTRACE_SIGNAL;
429 if (gbl_params.threadWaitTime == 0 )
430 gbl_params.threadWaitTime = ECRASH_DEFAULT_THREAD_WAIT_TIME;
432 if (gbl_params.debugLevel == 0 )
433 gbl_params.debugLevel = ECRASH_DEBUG_DEFAULT;
435 // Copy our symbol table
436 if (gbl_params.symbolTable) {
437 DPRINTF(ECRASH_DEBUG_VERBOSE,
438 "symbolTable @ %p -- %d symbols\n", gbl_params.symbolTable,
439 gbl_params.symbolTable->numSymbols);
440 // Make a copy of our symbol table
441 gbl_params.symbolTable = malloc(sizeof(eCrashSymbolTable));
442 memcpy(gbl_params.symbolTable, params->symbolTable,
443 sizeof(eCrashSymbolTable));
445 // Now allocate / copy the actual table.
446 gbl_params.symbolTable->symbols = malloc(sizeof(eCrashSymbol) *
447 gbl_params.symbolTable->numSymbols);
448 memcpy(gbl_params.symbolTable->symbols,
449 params->symbolTable->symbols,
450 sizeof(eCrashSymbol) * gbl_params.symbolTable->numSymbols);
452 ValidateSymbolTable();
455 // And, finally, register for our signals
456 for (sigIndex=0; gbl_params.signals[sigIndex] != 0; sigIndex++) {
457 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,
458 " Catching signal[%d] %d\n", sigIndex,
459 gbl_params.signals[sigIndex]);
461 // I know there's a better way to catch signals with pthreads.
462 // I'll do it later TODO
463 signal(gbl_params.signals[sigIndex], crash_handler);
466 DPRINTF(ECRASH_DEBUG_ERROR, " Error: Null Params!\n");
469 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE, "Init Complete ret=%d\n", ret);
474 * UnInitialize eCrash.
476 * This function may be called to de-activate eCrash, release the
477 * signal handlers, and free any memory allocated by eCrash.
479 * @return Zero on success.
481 int eCrash_Uninit( void )
486 } /* eCrash_Uninit */
489 * Register a thread for backtracing on crash.
491 * This function must be called by any thread wanting it's stack
492 * dumped in the event of a crash. The thread my specify what
493 * signal should be used, or the default, SIGUSR1 will be used.
495 * @param signo Signal to use to generate dump (default: SIGUSR1)
497 * @return Zero on success.
499 int eCrash_RegisterThread(char *name, int signo)
501 sighandler_t old_handler;
503 // Register for our signal
505 signo = gbl_params.defaultBacktraceSignal;
508 old_handler = signal(signo, bt_handler);
509 return addThreadToList(name, pthread_self(), signo, old_handler);
511 } /* eCrash_RegisterThread */
514 * Un-register a thread for stack dumps.
516 * This function may be called to un-register any previously
519 * @return Zero on success.
521 int eCrash_UnregisterThread( void )
523 return removeThreadFromList(pthread_self());
524 } /* eCrash_UnregisterThread */