2 * author: David Frascone
4 * eCrash Implementation
6 * eCrash will allow you to capture stack traces in the
7 * event of a crash, and write those traces to disk, stdout,
8 * or any other file handle.
10 * modified to integrate closer into citadel by Wilfried Goesgens
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37 #include <sys/types.h>
40 #include <libcitadel.h>
42 #include "sysdep_decls.h"
45 #include "citserver.h"
48 #define NIY() printf("%s: Not Implemented Yet!\n", __FUNCTION__)
51 static eCrashParameters gbl_params;
53 static int gbl_backtraceEntries;
54 static void **gbl_backtraceBuffer;
55 static char **gbl_backtraceSymbols;
56 static int gbl_backtraceDoneFlag = 0;
58 static void *stack_frames[50];
59 static size_t size, NThread;
60 static char **strings;
61 static char StaticBuf[SIZ];
64 * Private structures for our thread list
66 typedef struct thread_list_node{
70 sighandler_t oldHandler;
71 struct thread_list_node *Next;
74 static pthread_mutex_t ThreadListMutex = PTHREAD_MUTEX_INITIALIZER;
75 static ThreadListNode *ThreadList = NULL;
77 /*********************************************************************
78 *********************************************************************
79 ** P R I V A T E F U N C T I O N S
80 *********************************************************************
81 ********************************************************************/
85 * Insert a node into our threadList
87 * @param name Text string indicating our thread
88 * @param thread Our Thread Id
89 * @param signo Signal to create backtrace with
90 * @param old_handler Our old handler for signo
92 * @returns zero on success
94 static int addThreadToList(char *name, pthread_t thread,int signo,
95 sighandler_t old_handler)
99 node = malloc(sizeof(ThreadListNode));
100 if (!node) return -1;
102 DPRINTF(ECRASH_DEBUG_VERBOSE,
103 "Adding thread 0x%08x (%s)\n", (unsigned int)thread, name);
104 node->threadName = strdup(name);
105 node->thread = thread;
106 node->backtraceSignal = signo;
107 node->oldHandler = old_handler;
109 /* And, add it to the list */
110 pthread_mutex_lock(&ThreadListMutex);
111 node->Next = ThreadList;
113 pthread_mutex_unlock(&ThreadListMutex);
120 * Remove a node from our threadList
122 * @param thread Our Thread Id
124 * @returns zero on success
126 static int removeThreadFromList(pthread_t thread)
128 ThreadListNode *Probe, *Prev=NULL;
129 ThreadListNode *Removed = NULL;
131 DPRINTF(ECRASH_DEBUG_VERBOSE,
132 "Removing thread 0x%08x from list . . .\n", (unsigned int)thread);
133 pthread_mutex_lock(&ThreadListMutex);
134 for (Probe=ThreadList;Probe != NULL; Probe = Probe->Next) {
135 if (Probe->thread == thread) {
136 // We found it! Unlink it and move on!
138 if (Prev == NULL) { // head of list
139 ThreadList = Probe->Next;
141 // Prev != null, so we need to link around ourselves.
142 Prev->Next = Probe->Next;
144 Removed->Next = NULL;
150 pthread_mutex_unlock(&ThreadListMutex);
152 // Now, if something is in Removed, free it, and return success
154 DPRINTF(ECRASH_DEBUG_VERBOSE,
155 " Found %s -- removing\n", Removed->threadName);
156 // Reset the signal handler
157 signal(Removed->backtraceSignal, Removed->oldHandler);
159 // And free the allocated memory
160 free (Removed->threadName);
165 DPRINTF(ECRASH_DEBUG_VERBOSE,
167 return -1; // Not Found
169 } // removeThreadFromList
172 * Print out a line of output to all our destinations
174 * One by one, output a line of text to all of our output destinations.
176 * Return failure if we fail to output to any of them.
178 * @param format Normal printf style vararg format
180 * @returns nothing// bytes written, or error on failure.
182 static void outputPrintf(char *format, ...)
186 va_start(ap, format);
190 snprintf (StaticBuf, SIZ, format, ap);
191 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
194 CtdlLogPrintf(CTDL_EMERG, format, ap);
201 * Dump our backtrace into a global location
203 * This function will dump out our backtrace into our
204 * global holding area.
207 static void createGlobalBacktrace( void )
210 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
212 for (NThread = 0; NThread < size; NThread++)
214 snprintf (StaticBuf, SIZ, "RAW: %p ", stack_frames[NThread]);
215 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
218 for (NThread = 0; NThread < size; NThread++)
219 CtdlLogPrintf(CTDL_ALERT, "RAW: %p\n", stack_frames[NThread]);
220 strings = backtrace_symbols(stack_frames, size);
221 for (NThread = 0; NThread < size; NThread++) {
222 if (strings != NULL) {
224 {// vsyslogs printf compliance sucks.
225 snprintf (StaticBuf, SIZ, "RAW: %p ", strings[NThread]);
226 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
229 CtdlLogPrintf(CTDL_ALERT, "%s\n", strings[NThread]);
232 } /* createGlobalBacktrace */
233 static void outputRawtrace( void )
236 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
238 for (NThread = 0; NThread < size; NThread++)
240 snprintf (StaticBuf, SIZ, "RAW: %p ", stack_frames[NThread]);
241 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
244 for (NThread = 0; NThread < size; NThread++)
245 CtdlLogPrintf(CTDL_ALERT, "RAW: %p\n", stack_frames[NThread]);
246 } /* createGlobalBacktrace */
249 * Print out (to all the fds, etc), or global backtrace
251 static void outputGlobalBacktrace ( void )
255 for (i=0; i < gbl_backtraceEntries; i++) {
256 if (gbl_backtraceSymbols != FALSE) {
257 outputPrintf("* Frame %02x: %s\n",
258 i, gbl_backtraceSymbols[i]);
260 outputPrintf("* Frame %02x: %p\n", i,
261 gbl_backtraceBuffer[i]);
264 } // outputGlobalBacktrace
267 * Output our current stack's backtrace
269 static void outputBacktrace( void )
271 createGlobalBacktrace();
272 outputGlobalBacktrace();
273 } /* outputBacktrace */
275 static void outputBacktraceThreads( void )
277 ThreadListNode *probe;
280 // When we're backtracing, don't worry about the mutex . . hopefully
281 // we're in a safe place.
283 for (probe=ThreadList; probe; probe=probe->Next) {
284 gbl_backtraceDoneFlag = 0;
285 pthread_kill(probe->thread, probe->backtraceSignal);
286 for (i=0; i < gbl_params.threadWaitTime; i++) {
287 if (gbl_backtraceDoneFlag)
291 if (gbl_backtraceDoneFlag) {
292 outputPrintf("* Backtrace of \"%s\" (0x%08x)\n",
293 probe->threadName, (unsigned int)probe->thread);
294 outputGlobalBacktrace();
296 outputPrintf("* Error: unable to get backtrace of \"%s\" (0x%08x)\n",
297 probe->threadName, (unsigned int)probe->thread);
301 } // outputBacktraceThreads
305 * Handle signals (crash signals)
307 * This function will catch all crash signals, and will output the
310 * It will physically write (and sync) the current thread's information
311 * before it attempts to send signals to other threads.
313 * @param signum Signal received.
315 static void crash_handler(int signo)
318 outputPrintf("*********************************************************\n");
319 outputPrintf("* eCrash Crash Handler\n");
320 outputPrintf("*********************************************************\n");
322 outputPrintf("* Got a crash! signo=%d\n", signo);
324 outputPrintf("* Offending Thread's Backtrace:\n");
329 if (gbl_params.dumpAllThreads != FALSE) {
330 outputBacktraceThreads();
334 outputPrintf("*********************************************************\n");
335 outputPrintf("* eCrash Crash Handler\n");
336 outputPrintf("*********************************************************\n");
342 * Handle signals (bt signals)
344 * This function shoudl be called to generate a crashdump into our
345 * global area. Once the dump has been completed, this function will
346 * return after tickling a global. Since mutexes are not async
347 * signal safe, the main thread, after signaling us to generate our
348 * own backtrace, will sleep for a few seconds waiting for us to complete.
350 * @param signum Signal received.
352 static void bt_handler(int signo)
354 createGlobalBacktrace();
355 gbl_backtraceDoneFlag=1;
359 * Validate a passed-in symbol table
361 * For now, just print it out (if verbose), and make sure it's
362 * sorted and none of the pointers are zero.
364 static int ValidateSymbolTable( void )
368 unsigned long lastAddress =0;
370 // Get out of here if the table is empty
371 if (!gbl_params.symbolTable) return 0;
373 // Dump it in verbose mode
374 DPRINTF(ECRASH_DEBUG_VERBOSE,
375 "Symbol Table Provided with %d symbols\n",
376 gbl_params.symbolTable->numSymbols);
377 for (i=0; i < gbl_params.symbolTable->numSymbols; i++){
378 // Dump it in verbose mode
379 DPRINTF(ECRASH_DEBUG_VERBOSE,
381 gbl_params.symbolTable->symbols[i].function,
382 gbl_params.symbolTable->symbols[i].address);
384 (unsigned long)gbl_params.symbolTable->symbols[i].address) {
385 DPRINTF(ECRASH_DEBUG_ERROR,
386 "Error: symbol table is not sorted (last=%p, current=%p)\n",
388 gbl_params.symbolTable->symbols[i].address);
396 } // ValidateSymbolTable
398 /*********************************************************************
399 *********************************************************************
400 ** P U B L I C F U N C T I O N S
401 *********************************************************************
402 ********************************************************************/
407 * This function must be called before calling any other eCrash
408 * functions. It sets up the global behavior of the system, and
409 * registers the calling thread for crash dumps.
411 * @param params Our input parameters. The passed in structure will be copied.
413 * @return Zero on success.
415 int eCrash_Init(eCrashParameters *params)
419 #ifdef DO_SIGNALS_RIGHT
421 struct sigaction act;
424 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,"Init Starting params = %p\n", params);
426 // Allocate our backtrace area
427 gbl_backtraceBuffer = malloc(sizeof(void *) * (params->maxStackDepth+5));
429 #ifdef DO_SIGNALS_RIGHT
430 sigemptyset(&blocked);
431 act.sa_sigaction = crash_handler;
432 act.sa_mask = blocked;
433 act.sa_flags = SA_SIGINFO;
436 if (params != NULL) {
437 // Make ourselves a global copy of params.
438 gbl_params = *params;
439 gbl_params.filename = strdup(params->filename);
441 // Set our defaults, if they weren't specified
442 if (gbl_params.maxStackDepth == 0 )
443 gbl_params.maxStackDepth = ECRASH_DEFAULT_STACK_DEPTH;
445 if (gbl_params.defaultBacktraceSignal == 0 )
446 gbl_params.defaultBacktraceSignal = ECRASH_DEFAULT_BACKTRACE_SIGNAL;
448 if (gbl_params.threadWaitTime == 0 )
449 gbl_params.threadWaitTime = ECRASH_DEFAULT_THREAD_WAIT_TIME;
451 if (gbl_params.debugLevel == 0 )
452 gbl_params.debugLevel = ECRASH_DEBUG_DEFAULT;
454 // Copy our symbol table
455 if (gbl_params.symbolTable) {
456 DPRINTF(ECRASH_DEBUG_VERBOSE,
457 "symbolTable @ %p -- %d symbols\n", gbl_params.symbolTable,
458 gbl_params.symbolTable->numSymbols);
459 // Make a copy of our symbol table
460 gbl_params.symbolTable = malloc(sizeof(eCrashSymbolTable));
461 memcpy(gbl_params.symbolTable, params->symbolTable,
462 sizeof(eCrashSymbolTable));
464 // Now allocate / copy the actual table.
465 gbl_params.symbolTable->symbols = malloc(sizeof(eCrashSymbol) *
466 gbl_params.symbolTable->numSymbols);
467 memcpy(gbl_params.symbolTable->symbols,
468 params->symbolTable->symbols,
469 sizeof(eCrashSymbol) * gbl_params.symbolTable->numSymbols);
471 ValidateSymbolTable();
474 // And, finally, register for our signals
475 for (sigIndex=0; gbl_params.signals[sigIndex] != 0; sigIndex++) {
476 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,
477 " Catching signal[%d] %d\n", sigIndex,
478 gbl_params.signals[sigIndex]);
480 // I know there's a better way to catch signals with pthreads.
481 // I'll do it later TODO
482 signal(gbl_params.signals[sigIndex], crash_handler);
485 DPRINTF(ECRASH_DEBUG_ERROR, " Error: Null Params!\n");
488 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE, "Init Complete ret=%d\n", ret);
493 * UnInitialize eCrash.
495 * This function may be called to de-activate eCrash, release the
496 * signal handlers, and free any memory allocated by eCrash.
498 * @return Zero on success.
500 int eCrash_Uninit( void )
505 } /* eCrash_Uninit */
508 * Register a thread for backtracing on crash.
510 * This function must be called by any thread wanting it's stack
511 * dumped in the event of a crash. The thread my specify what
512 * signal should be used, or the default, SIGUSR1 will be used.
514 * @param signo Signal to use to generate dump (default: SIGUSR1)
516 * @return Zero on success.
518 int eCrash_RegisterThread(char *name, int signo)
520 sighandler_t old_handler;
522 // Register for our signal
524 signo = gbl_params.defaultBacktraceSignal;
527 old_handler = signal(signo, bt_handler);
528 return addThreadToList(name, pthread_self(), signo, old_handler);
530 } /* eCrash_RegisterThread */
533 * Un-register a thread for stack dumps.
535 * This function may be called to un-register any previously
538 * @return Zero on success.
540 int eCrash_UnregisterThread( void )
542 return removeThreadFromList(pthread_self());
543 } /* eCrash_UnregisterThread */